Python & AI Tutorials Logo
Pemrograman Python

23. Fungsi Kelas Satu dan Teknik Fungsional

Di bab-bab sebelumnya, kita belajar cara mendefinisikan dan memanggil fungsi, bekerja dengan parameter dan argumen, serta memahami cakupan variabel. Sekarang kita akan mengeksplorasi fitur kuat yang membuat Python menonjol: fungsi adalah objek kelas satu. Artinya fungsi dapat diperlakukan seperti nilai lainnya—disimpan dalam variabel, dioper sebagai argumen ke fungsi lain, dan dikembalikan dari fungsi.

Kemampuan ini membuka teknik pemrograman yang elegan yang membuat kode lebih fleksibel, dapat digunakan ulang, dan ekspresif. Kita akan mengeksplorasi cara memanfaatkan fungsi kelas satu melalui contoh praktis, memahami closure (fungsi yang "mengingat" lingkungannya), menggunakan ekspresi lambda untuk definisi fungsi yang ringkas, dan menerapkan fungsi bawaan seperti map(), filter(), any(), dan all() untuk bekerja dengan koleksi secara efisien.

23.1) Fungsi sebagai Objek Kelas Satu

23.1.1) Apa Arti "Kelas Satu"

Di Python, fungsi adalah objek kelas satu, yang berarti fungsi dapat:

  • Ditugaskan ke variabel
  • Disimpan dalam struktur data (list, dictionary, dll.)
  • Dioper sebagai argumen ke fungsi lain
  • Dikembalikan sebagai nilai dari fungsi lain

Ini berbeda dari beberapa bahasa pemrograman di mana fungsi memiliki status khusus dan tidak bisa dimanipulasi seperti nilai biasa. Di Python, fungsi hanyalah tipe objek lain, mirip dengan integer, string, atau list.

Mari kita lihat ini dalam aksi:

python
# Mendefinisikan fungsi sederhana
def greet(name):
    return f"Hello, {name}!"
 
# Menetapkan fungsi ke sebuah variabel
say_hello = greet
 
# Memanggil fungsi melalui variabel baru
message = say_hello("Alice")
print(message)  # Output: Hello, Alice!
 
# Memeriksa bahwa kedua nama merujuk ke fungsi yang sama
print(greet)      # Output: <function greet at 0x...>
print(say_hello)  # Output: <function greet at 0x...>
print(greet is say_hello)  # Output: True

Perhatikan bahwa ketika kita menulis say_hello = greet, kita tidak sedang memanggil fungsi (tanpa tanda kurung). Kita sedang membuat nama baru yang merujuk ke objek fungsi yang sama. Baik greet maupun say_hello sekarang menunjuk ke fungsi yang sama, yang bisa kita verifikasi menggunakan operator is.

23.1.2) Menyimpan Fungsi dalam Struktur Data

Karena fungsi adalah objek, kita bisa menyimpannya di list, dictionary, atau koleksi lainnya:

python
# Kalkulator dengan operasi yang disimpan dalam dictionary
def add(x, y):
    return x + y
 
def subtract(x, y):
    return x - y
 
def multiply(x, y):
    return x * y
 
def divide(x, y):
    return x / y
 
# Menyimpan fungsi dalam sebuah dictionary
operations = {
    '+': add,
    '-': subtract,
    '*': multiply,
    '/': divide
}
 
# Menggunakan dictionary untuk melakukan perhitungan
num1 = 10
num2 = 5
operator = '*'
 
result = operations[operator](num1, num2)
print(f"{num1} {operator} {num2} = {result}")  # Output: 10 * 5 = 50

Pola ini sangat berguna untuk membangun sistem yang fleksibel. Alih-alih menulis rantai panjang pernyataan if-elif untuk memilih fungsi mana yang akan dipanggil, kita bisa mencari fungsi yang sesuai di dictionary dan memanggilnya secara langsung.

23.2) Mengoper Fungsi sebagai Argumen

23.2.1) Konsep Dasar

Salah satu penggunaan paling kuat dari fungsi kelas satu adalah mengopernya sebagai argumen ke fungsi lain. Ini memungkinkan kita menulis kode yang fleksibel, dapat digunakan ulang, dan bisa bekerja dengan perilaku yang berbeda-beda.

Berikut contoh sederhana:

python
# Fungsi yang menerapkan fungsi lain ke sebuah nilai
def apply_operation(value, operation):
    """Terapkan fungsi operation yang diterima sebagai parameter pada value."""
    return operation(value)
 
# Berbagai operasi
def double(x):
    return x * 2
 
def square(x):
    return x * x
 
def negate(x):
    return -x
 
# Menggunakan fungsi apply_operation yang sama dengan operasi berbeda
number = 5
print(apply_operation(number, double))   # Output: 10
print(apply_operation(number, square))   # Output: 25
print(apply_operation(number, negate))   # Output: -5

Fungsi apply_operation tidak tahu atau tidak peduli operasi spesifik apa yang sedang dilakukan. Ia hanya memanggil fungsi apa pun yang dioperkan kepadanya. Pemisahan tanggung jawab ini membuat kode lebih modular dan lebih mudah diperluas.

23.2.2) Memproses Koleksi dengan Fungsi Kustom

Pola yang umum adalah memproses setiap item dalam sebuah koleksi menggunakan fungsi yang dioper sebagai argumen:

python
# Memproses setiap item dalam list menggunakan fungsi yang diberikan
def process_list(items, processor):
    """Terapkan fungsi processor pada setiap item dalam list."""
    results = []
    for item in items:
        results.append(processor(item))
    return results
 
# Berbagai fungsi pemrosesan
def uppercase(text):
    return text.upper()
 
def add_exclamation(text):
    return text + "!"
 
def get_length(text):
    return len(text)
 
# Memproses list yang sama dengan cara berbeda
words = ["hello", "world", "python"]
 
print(process_list(words, uppercase))        # Output: ['HELLO', 'WORLD', 'PYTHON']
print(process_list(words, add_exclamation))  # Output: ['hello!', 'world!', 'python!']
print(process_list(words, get_length))       # Output: [5, 5, 6]

Pola ini sangat berguna sehingga Python menyediakan fungsi bawaan seperti map() dan filter() yang bekerja dengan cara ini.

23.2.3) Mengurutkan dengan Memberikan Fungsi Key (Pengantar Singkat)

Fungsi sorted() di Python menerima parameter key—sebuah fungsi yang menentukan cara membandingkan item:

python
# Mengurutkan siswa berdasarkan kriteria berbeda
students = [
    {"name": "Alice", "grade": 85, "age": 20},
    {"name": "Bob", "grade": 92, "age": 19},
    {"name": "Charlie", "grade": 78, "age": 21},
    {"name": "Diana", "grade": 95, "age": 20}
]
 
# Fungsi untuk mengambil grade
def get_grade(student):
    return student["grade"]
 
# Fungsi untuk mengambil name
def get_name(student):
    return student["name"]
 
# Urutkan berdasarkan grade (menaik)
by_grade = sorted(students, key=get_grade)
print("Sorted by grade:")
for student in by_grade:
    print(f"  {student['name']}: {student['grade']}")
# Output:
#   Charlie: 78
#   Alice: 85
#   Bob: 92
#   Diana: 95
 
# Urutkan berdasarkan name (alfabetis)
by_name = sorted(students, key=get_name)
print("\nSorted by name:")
for student in by_name:
    print(f"  {student['name']}: {student['grade']}")
# Output:
#   Alice: 85
#   Bob: 92
#   Charlie: 78
#   Diana: 95

Fungsi key dipanggil sekali untuk setiap item, dan nilai baliknya digunakan untuk perbandingan. Ini jauh lebih fleksibel daripada harus menulis logika pengurutan kustom.

Pola mengoper fungsi untuk mengustomisasi perilaku seperti ini sangat umum di Python. Kita akan mengeksplorasi teknik pengurutan yang lebih lanjutan di Bab 38.

23.3) Mengembalikan Fungsi dari Fungsi

23.3.1) Fungsi yang Membuat Fungsi

Seperti halnya kita bisa mengoper fungsi sebagai argumen, kita juga bisa mengembalikan fungsi dari fungsi lain. Ini memungkinkan kita membuat fungsi khusus secara dinamis:

python
# Fungsi yang membuat dan mengembalikan fungsi baru
def create_multiplier(factor):
    """Buat fungsi yang mengalikan dengan factor yang diberikan."""
    def multiplier(x):
        return x * factor
    return multiplier
 
# Membuat fungsi pengali khusus
double = create_multiplier(2)
triple = create_multiplier(3)
times_ten = create_multiplier(10)
 
# Menggunakan fungsi yang dibuat
print(double(5))      # Output: 10
print(triple(5))      # Output: 15
print(times_ten(5))   # Output: 50

Apa yang terjadi di sini? Fungsi create_multiplier mendefinisikan fungsi dalam bernama multiplier dan mengembalikannya. Setiap kali kita memanggil create_multiplier dengan factor yang berbeda, kita mendapatkan kembali fungsi baru yang "mengingat" factor spesifik tersebut. Ini adalah sekilas pertama kita tentang closure, yang akan kita eksplorasi secara mendalam di bagian berikutnya.

23.3.2) Membuat Validator yang Dikustomisasi

Mengembalikan fungsi sangat berguna untuk membuat fungsi validasi atau pemrosesan yang dikustomisasi:

python
# Membuat validator rentang secara dinamis
def create_range_validator(min_value, max_value):
    """Buat fungsi yang memvalidasi apakah sebuah angka berada dalam rentang."""
    def validator(number):
        return min_value <= number <= max_value
    return validator
 
# Membuat validator spesifik
is_valid_age = create_range_validator(0, 120)
is_valid_percentage = create_range_validator(0, 100)
is_room_temperature = create_range_validator(15, 30)
 
# Menggunakan validator
age = 25
print(f"Is {age} a valid age? {is_valid_age(age)}")  # Output: True
 
temp = 22
print(f"Is {temp}°C room temperature? {is_room_temperature(temp)}")  # Output: True
 
score = 150
print(f"Is {score} a valid percentage? {is_valid_percentage(score)}")  # Output: False

23.4) Memahami Closure: Fungsi yang Mengingat

23.4.1) Apa Itu Closure?

Sebuah closure adalah fungsi yang "mengingat" variabel dari scope tempat fungsi itu dibuat, bahkan setelah scope tersebut selesai dieksekusi. Dalam contoh dari Bagian 23.3, kita sudah menggunakan closure tanpa menyebutnya secara eksplisit.

Mari kita lihat bagaimana closure bekerja:

python
def create_counter(start=0):
    """Buat fungsi counter yang mengingat hitungannya."""
    count = start  # Variabel ini "ditangkap" oleh closure
    
    def counter():
        nonlocal count  # Mengakses variabel yang ditangkap
        count += 1
        return count
    
    return counter
 
# Membuat dua counter yang independen
counter1 = create_counter(0)
counter2 = create_counter(100)
 
# Setiap counter mempertahankan hitungannya sendiri
print(counter1())  # Output: 1
print(counter1())  # Output: 2
print(counter1())  # Output: 3
 
print(counter2())  # Output: 101
print(counter2())  # Output: 102
 
print(counter1())  # Output: 4 (counter1 is independent of counter2)

Fungsi dalam counter membentuk closure atas variabel count. Meskipun create_counter sudah selesai dieksekusi, fungsi counter yang dikembalikan masih memiliki akses ke count. Setiap pemanggilan create_counter membuat closure baru yang independen dengan variabel count miliknya sendiri.

23.4.2) Cara Closure Menangkap Variabel

Ketika sebuah fungsi didefinisikan di dalam fungsi lain, ia dapat mengakses variabel dari scope fungsi luar. Variabel-variabel ini "ditangkap" dan tetap dapat diakses bahkan setelah fungsi luar mengembalikan nilai:

Saat Python membuat fungsi dalam, ia tidak hanya menyimpan kode fungsi—ia juga menyimpan referensi ke variabel apa pun dari fungsi luar yang digunakan oleh fungsi dalam. Proses ini disebut "menangkap" variabel.

python
def create_greeter(greeting):
    """Buat fungsi sapaan dengan greeting yang kustom."""
    def greet(name):
        return f"{greeting}, {name}!"
    return greet
 
# Membuat greeter yang berbeda
say_hello = create_greeter("Hello")
say_hi = create_greeter("Hi")
say_bonjour = create_greeter("Bonjour")
 
# Setiap greeter mengingat greeting spesifiknya
print(say_hello("Alice"))    # Output: Hello, Alice!
print(say_hi("Bob"))         # Output: Hi, Bob!
print(say_bonjour("Claire")) # Output: Bonjour, Claire!

Parameter greeting ditangkap oleh closure. Setiap fungsi greeter memiliki nilai greeting yang ditangkapnya sendiri yang digunakan setiap kali dipanggil.

23.4.3) Penggunaan Praktis: Fungsi Konfigurasi

Closure sangat bagus untuk membuat fungsi dengan perilaku yang sudah dikonfigurasi sebelumnya:

python
# Membuat kalkulator harga dengan tarif pajak berbeda
def create_price_calculator(tax_rate):
    """Buat kalkulator yang menerapkan tarif pajak tertentu."""
    def calculate_total(price):
        tax = price * tax_rate
        return price + tax
    return calculate_total
 
# Membuat kalkulator untuk wilayah yang berbeda
us_calculator = create_price_calculator(0.07)    # 7% tax
uk_calculator = create_price_calculator(0.20)    # 20% VAT
japan_calculator = create_price_calculator(0.10) # 10% consumption tax
 
# Menghitung harga di berbagai wilayah
item_price = 100
 
print(f"US total: ${us_calculator(item_price):.2f}")      # Output: US total: $107.00
print(f"UK total: £{uk_calculator(item_price):.2f}")      # Output: UK total: £120.00
print(f"Japan total: ¥{japan_calculator(item_price):.2f}") # Output: Japan total: ¥110.00

23.4.4) Kapan Menggunakan Closure

Closure sangat berguna ketika kamu perlu:

  • Membuat fungsi dengan perilaku yang sudah dikonfigurasi sebelumnya
  • Mempertahankan state antar pemanggilan fungsi tanpa menggunakan class
  • Mengimplementasikan fungsi callback yang perlu mengingat konteks
  • Membuat pabrik fungsi (function factory) yang menghasilkan fungsi-fungsi khusus

23.5) Menggunakan lambda untuk Fungsi Anonim Singkat

23.5.1) Apa Itu Ekspresi Lambda?

Sebuah ekspresi lambda membuat fungsi kecil dan anonim—fungsi tanpa nama. Ekspresi lambda berguna ketika kamu membutuhkan fungsi sederhana untuk waktu singkat dan tidak ingin mendefinisikannya secara formal dengan def.

Sintaksnya adalah:

python
lambda parameters: expression

Lambda menerima parameter (seperti fungsi biasa) dan mengembalikan hasil evaluasi dari expression. Berikut contoh sederhana:

python
# Fungsi biasa
def add(x, y):
    return x + y
 
# Ekspresi lambda yang ekuivalen
add_lambda = lambda x, y: x + y
 
# Keduanya bekerja dengan cara yang sama
print(add(3, 5))        # Output: 8
print(add_lambda(3, 5)) # Output: 8

Ekspresi lambda dibatasi hanya pada satu expression—lambda tidak bisa berisi statement seperti if, for, atau beberapa baris kode. Batasan ini membuatnya tetap sederhana dan fokus.

23.5.2) Ekspresi Lambda sebagai Argumen

Ekspresi lambda sangat berguna ketika kamu perlu mengoper fungsi sederhana sebagai argumen dan tidak ingin mendefinisikan fungsi bernama yang terpisah:

python
# Mengurutkan siswa berdasarkan grade menggunakan lambda
students = [
    {"name": "Alice", "grade": 85},
    {"name": "Bob", "grade": 92},
    {"name": "Charlie", "grade": 78},
    {"name": "Diana", "grade": 95}
]
 
# Alih-alih mendefinisikan fungsi terpisah:
# def get_grade(student):
#     return student["grade"]
# sorted_students = sorted(students, key=get_grade)
 
# Kita bisa menggunakan lambda langsung:
sorted_students = sorted(students, key=lambda student: student["grade"])
 
print("Students sorted by grade:")
for student in sorted_students:
    print(f"  {student['name']}: {student['grade']}")
# Output:
#   Charlie: 78
#   Alice: 85
#   Bob: 92
#   Diana: 95

Ini lebih ringkas ketika fungsinya sederhana dan hanya digunakan sekali. Lambda lambda student: student["grade"] ekuivalen dengan fungsi yang menerima student dan mengembalikan grade-nya.

23.5.3) Lambda dengan Beberapa Parameter

Ekspresi lambda bisa menerima beberapa parameter, sama seperti fungsi biasa:

python
# Operasi kalkulator menggunakan lambda
operations = {
    'add': lambda x, y: x + y,
    'subtract': lambda x, y: x - y,
    'multiply': lambda x, y: x * y,
    'divide': lambda x, y: x / y if y != 0 else "Error"
}
 
# Menggunakan ekspresi lambda
print(operations['add'](10, 5))       # Output: 15
print(operations['multiply'](10, 5))  # Output: 50
print(operations['divide'](10, 0))    # Output: Error

Perhatikan bagaimana kita bisa menggunakan conditional expression (x / y if y != 0 else "Error") di dalam lambda, tetapi kita tidak bisa menggunakan statement if (yang akan membutuhkan beberapa baris).

23.5.4) Kapan Menggunakan Lambda vs Fungsi Bernama

Gunakan ekspresi lambda ketika:

  • Fungsinya sangat sederhana (satu expression)
  • Fungsinya hanya digunakan sekali atau dalam konteks yang sangat lokal
  • Mendefinisikan fungsi bernama akan menambah verbosity yang tidak perlu

Gunakan fungsi bernama ketika:

  • Fungsinya kompleks atau memerlukan beberapa statement
  • Fungsinya akan digunakan ulang di beberapa tempat
  • Fungsinya membutuhkan nama deskriptif agar jelas
  • Fungsinya membutuhkan docstring

23.5.5) Keterbatasan Lambda dan Alternatifnya

Ekspresi lambda memiliki keterbatasan penting:

python
# ❌ Ini tidak akan bekerja - lambda tidak bisa berisi statement
# bad_lambda = lambda x: 
#     if x > 0:
#         return x
#     else:
#         return -x
 
# ✅ Gunakan conditional expression sebagai gantinya
absolute_value = lambda x: x if x > 0 else -x
print(absolute_value(-5))  # Output: 5
print(absolute_value(3))   # Output: 3
 
# ✅ Untuk beberapa operasi, gunakan fungsi biasa
def process_and_double(x):
    print(f"Processing: {x}")
    return x * 2
 
result = process_and_double(5)  # Output: Processing: 5
print(result)                    # Output: 10

Ekspresi lambda adalah alat untuk situasi tertentu. Ketika lambda membuat kode lebih jelas dan ringkas, gunakan lambda. Ketika lambda membuat kode lebih sulit dipahami, gunakan fungsi bernama biasa sebagai gantinya.

23.6) Menggunakan map() dan filter() dengan Fungsi Sederhana

23.6.1) Fungsi map()

Fungsi map() menerapkan function yang diberikan ke setiap item dari sebuah iterable (seperti list, tuple, atau string) dan mengembalikan iterator yang berisi hasilnya. Ini adalah cara untuk mentransformasikan setiap elemen dalam koleksi tanpa menulis loop secara eksplisit.

python
map(function, iterable, *iterables)

Parameter:

  • function (wajib): Fungsi yang menerima satu atau lebih argumen, memprosesnya, dan mengembalikan sebuah nilai. Fungsi dipanggil sekali untuk setiap elemen dalam iterable(s).
  • iterable (wajib): Sebuah urutan (list, tuple, string, dll.) yang elemennya akan dioperkan ke function.
  • *iterables (opsional): Iterable tambahan untuk function multi-argumen.

Jika beberapa iterable diberikan, function harus menerima argumen sebanyak itu map() akan berhenti ketika iterable terpendek habis

Nilai balik:

Sebuah objek map (iterator) yang berisi hasil yang dikembalikan oleh function untuk setiap elemen input.

Penting: Objek map adalah iterator, bukan urutan seperti list.

python
# Melipatgandakan setiap angka dalam list
numbers = [1, 2, 3, 4, 5]
 
def double(x):
    return x * 2
 
# Menerapkan double ke setiap angka
doubled = map(double, numbers)
result = list(doubled)  # Mengonversi objek map (iterator) menjadi list
print(result)  # Output: [2, 4, 6, 8, 10]

23.6.2) Menggunakan map() dengan Lambda

Ekspresi lambda bekerja sempurna dengan map() untuk transformasi sederhana:

python
# Mengonversi suhu dari Celsius ke Fahrenheit
celsius_temps = [0, 10, 20, 30, 40]
 
fahrenheit_temps = list(map(lambda c: (c * 9/5) + 32, celsius_temps))
print(fahrenheit_temps)  # Output: [32.0, 50.0, 68.0, 86.0, 104.0]

23.6.3) Fungsi filter()

Fungsi filter() menerapkan function yang diberikan ke setiap item dari sebuah iterable dan mengembalikan iterator yang hanya berisi item-item yang membuat fungsi mengembalikan True. Ini adalah cara untuk memilih elemen dari koleksi tanpa menulis loop secara eksplisit.

python
filter(function, iterable)

Parameter:

  • function: Fungsi yang menerima satu argumen, mengevaluasinya, dan mengembalikan True atau False. Fungsi dipanggil sekali untuk setiap elemen dalam iterable.
  • iterable: Sebuah urutan (list, tuple, string, dll.) yang elemennya akan diuji oleh function.

Nilai balik:

Sebuah objek filter (iterator) yang hanya berisi elemen-elemen yang membuat function mengembalikan True.

Penting: Objek filter adalah iterator, bukan urutan seperti list.

Contoh:

python
# Menyimpan hanya angka genap
numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
 
def is_even(x):
    return x % 2 == 0
 
# Menerapkan is_even ke setiap angka, simpan hanya yang mengembalikan True
even_numbers = filter(is_even, numbers)
result = list(even_numbers)  # Mengonversi objek filter menjadi list
print(result)  # Output: [2, 4, 6, 8, 10]

23.6.4) Menggunakan filter() dengan Lambda

Ekspresi lambda umum digunakan dengan filter() untuk filtering yang ringkas:

python
# Memfilter siswa yang lulus (grade >= 60)
students = [
    {"name": "Alice", "grade": 85},
    {"name": "Bob", "grade": 55},
    {"name": "Charlie", "grade": 92},
    {"name": "Diana", "grade": 48},
    {"name": "Eve", "grade": 73}
]
 
passed = list(filter(lambda s: s["grade"] >= 60, students))
print("Students who passed:")
for student in passed:
    print(f"  {student['name']}: {student['grade']}")
# Output:
#   Alice: 85
#   Charlie: 92
#   Eve: 73

23.6.5) Menggabungkan map() dan filter()

Kamu bisa merangkai operasi map() dan filter() untuk melakukan transformasi yang kompleks:

python
# Mendapatkan kuadrat dari angka genap
numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
 
# Pertama memfilter angka genap, lalu menguadratkannya
even_numbers = filter(lambda x: x % 2 == 0, numbers)
squared = map(lambda x: x ** 2, even_numbers)
result = list(squared)
print(result)  # Output: [4, 16, 36, 64, 100]

Perbandingan Visual: map() vs filter()

filter() - Menyimpan BEBERAPA item

Input: [1, 2, 3, 4, 5]

Uji: is_even(x)

Output: [2, 4](sama atau lebih pendek)

map() - Mentransformasi SEMUA item

Input: [1, 2, 3, 4, 5]

Terapkan: double(x) = x * 2

Output: [2, 4, 6, 8, 10](panjang sama)

Perbedaan utama:

  • map(): Menerapkan fungsi untuk mentransformasi setiap item → output memiliki panjang yang sama
  • filter(): Menguji tiap item dan menyimpan hanya yang lolos → output memiliki panjang sama atau lebih pendek

Di bab ini, kita telah mengeksplorasi fitur pemrograman fungsional Python yang kuat. Kita belajar bahwa fungsi adalah objek kelas satu yang bisa diperlakukan seperti nilai lainnya, memungkinkan pola kode yang fleksibel dan dapat digunakan ulang. Kita menemukan bagaimana fungsi dapat mengembalikan fungsi lain, membentuk closure yang mengingat lingkungannya. Kita mengeksplorasi ekspresi lambda untuk definisi fungsi yang ringkas, dan kita menggunakan map() serta filter() untuk memproses koleksi dengan elegan.

Konsep-konsep ini membentuk fondasi untuk teknik pemrograman Python tingkat lanjut. Di Bab 38, kita akan membangun dari pengetahuan ini untuk menguasai decorator, salah satu fitur Python yang paling elegan.


© 2025. Primesoft Co., Ltd.
support@primesoft.ai