PLPraktikum Pemrograman LanjutLembar Kerja Mahasiswa · Doni Winarso
Universitas Muhammadiyah Riau · Fakultas Ilmu Komputer · Teknik Informatika
Modul Praktikum Pemrograman Lanjut
Lembar Kerja Mahasiswa (LKM) untuk 16 pertemuan praktikum pemrograman fungsional dengan Python. Isi setiap lembar kerja langsung di halaman ini, lalu unduh sebagai PDF atau Word untuk dikumpulkan.
Mata Kuliah
Praktikum Pemrograman Lanjutan
Kode / SKS / Semester
40401502 · 1 SKS · Semester 5
Rumpun Mata Kuliah
Mata Kuliah Prodi (MKP)
CPL
CPL 5 — Menguasai konsep teoretis Ilmu Komputer/Informatika untuk mendesain dan menyimulasikan aplikasi multi-platform. CPL 6 — Menerapkan pemikiran logis, kritis, sistematis, dan inovatif dalam pengembangan atau implementasi IPTEK. CPL 9 — Menganalisis, merancang, membuat, dan mengevaluasi antarmuka pengguna dan aplikasi interaktif.
Python 3 (disarankan 3.10 atau lebih baru), Visual Studio Code + ekstensi Python, terminal/Command Prompt
Perangkat Keras
Komputer minimum Core i5
Identitas Mahasiswa
Identitas ini otomatis dicantumkan di setiap lembar kerja yang Anda unduh.
Petunjuk Penggunaan LKM
Baca Tujuan dan Dasar Teori sebelum praktikum dimulai.
Kerjakan Langkah Praktikum secara berurutan di VS Code. Ketik sendiri kodenya, jangan hanya menyalin, lalu jalankan dengan python nama_file.py. Bandingkan keluaran Anda dengan komentar # hasil pada contoh.
Tulis jawaban Tugas pada kotak jawaban. Jawaban tersimpan otomatis di peramban (browser) perangkat ini.
Lampirkan tangkapan layar (screenshot) hasil eksekusi bila diminta. Tempelkan di file Word hasil unduhan.
Unduh LKM dengan tombol Unduh PDF / Unduh Word di setiap pertemuan, lalu kumpulkan sesuai instruksi asisten.
Jawaban hanya tersimpan di browser ini. Jika berganti komputer di lab, unduh dulu file Word Anda sebelum keluar.
Aturan gaya fungsional selama praktikum: kecuali diminta lain, tulis fungsi murni (tanpa print di dalam fungsi, tanpa mengubah variabel global, tanpa mengubah isi list argumen). Gunakan print hanya di bagian program utama untuk menampilkan hasil.
Etika praktikum: Kerjakan tugas secara mandiri. Kode yang identik antar mahasiswa (termasuk nama variabel dan komentar) akan diperiksa ulang oleh asisten. Anda boleh bertanya ke asisten atau teman tentang konsep, bukan menyalin kode.
✓ Tersimpan
Pendahuluan
Peta Pertemuan & Penilaian
Setiap pertemuan berlangsung 100 menit dan memenuhi satu Sub-CPMK. Bobot asesmen di bawah mengikuti RPS.
Capaian Pembelajaran Mata Kuliah (CPMK)
Kode
Keterangan
CPL
Bobot
CPMK 069
Mampu menerapkan program menggunakan paradigma pemrograman fungsional
CPL 5
10%
CPMK 070
Mampu menerapkan program menggunakan notasi fungsional
CPL 5
10%
CPMK 071
Mampu menerapkan program menggunakan ekspresi fungsional
CPL 6
40%
CPMK 072
Mampu menerapkan paket struktur data (List dan Tree) pada pemrograman fungsional
CPL 9
40%
Jumlah
100%
Sub-CPMK
Kode
Kemampuan akhir
069.1
Menerapkan perbedaan paradigma pemrograman fungsional, prosedural, dan berorientasi objek
069.2
Menerapkan paradigma pemrograman berbasis fungsional
070.1
Menerapkan pemrograman menggunakan notasi fungsional
070.2
Menerapkan pemrograman berbasis notasi fungsional dasar
071.1
Menerapkan pemrograman fungsional berbasis ekspresi dasar dan ekspresi kondisional
071.2
Menerapkan pemrograman fungsional berbasis ekspresi rekursif dan ekspresi lambda
072.1
Menerapkan pemrograman fungsional berbasis list
072.2
Menerapkan pemrograman fungsional berbasis tree (pohon)
Rencana 16 Pertemuan
Ke-
Sub-CPMK
Bahan Kajian
Asesmen
Bobot*
1
069.1
Paradigma pemrograman fungsional
Non-asesmen
0%
2
069.1
Paradigma fungsional vs prosedural vs OOP
Tugas
100%
3
070.1
Notasi dasar fungsional
Non-asesmen
0%
4
070.1
Notasi fungsional
Tugas
100%
5
071.1
Ekspresi dasar fungsional
Non-asesmen
0%
6
071.1
Ekspresi kondisional
Non-asesmen
0%
7
071.1
Ekspresi rekursif fungsional
Non-asesmen
0%
8
071.2
Ekspresi lambda
UTS
100%
9
071.2
Fungsi dengan type bentukan
Non-asesmen
0%
10
072.1
List dengan elemen sederhana dan karakter
Non-asesmen
0%
11
072.1
List integer dan himpunan (set)
Non-asesmen
0%
12
072.1
Resume rekursi terhadap list
Non-asesmen
0%
13
072.1
Struktur data list
Non-asesmen
0%
14
072.2
Tree untuk pohon n-ary dan biner
Non-asesmen
0%
15
072.2
Binary search dan balanced search
Non-asesmen
0%
16
072.2
Ujian pemrograman fungsional list dan tree
UAS
100%
*Bobot asesmen dihitung terhadap CPMK induknya. Contoh: Tugas Pertemuan 2 menyumbang 100% dari nilai CPMK 069; UTS menyumbang 100% dari nilai CPMK 071. Pertemuan non-asesmen tetap wajib dikerjakan karena materinya diujikan pada Tugas, UTS, dan UAS.
Cara Menghitung Nilai
Nilai CPMK 069 = Tugas Pertemuan 2
Nilai CPMK 070 = Tugas Pertemuan 4
Nilai CPMK 071 = UTS (Pertemuan 8)
Nilai CPMK 072 = UAS (Pertemuan 16)
Nilai Akhir = 0,10×CPMK069 + 0,10×CPMK070 + 0,40×CPMK071 + 0,40×CPMK072
Contoh:
Tugas P2 = 85, Tugas P4 = 80, UTS = 75, UAS = 82
Nilai Akhir = 8,5 + 8,0 + 30,0 + 32,8 = 79,3
Setiap LKM dinilai dengan skala 0–100 menggunakan rubrik yang tercantum di bagian akhir lembar kerja.
Pendahuluan
Studi Kasus: Data Nilai Mahasiswa
Sebagian besar pertemuan memakai data yang sama, yaitu modul data_mhs.py. Dengan data yang sudah dikenal, Anda bisa fokus pada cara berpikir fungsional, bukan pada memahami data baru setiap minggu.
Struktur Folder Kerja
Buat satu folder kerja dan simpan semua file praktikum di dalamnya:
praktikum_pl/
├── data_mhs.py ← data studi kasus (dibuat sekali, dipakai terus)
├── p01.py ← file praktikum pertemuan 1
├── p02.py
├── ...
├── tipe.py ← type bentukan (Pertemuan 9)
├── daftar.py ← struktur data list (Pertemuan 13)
└── pohon.py ← struktur data tree (Pertemuan 14–15)
Isi data_mhs.py
Data disimpan dalam tuple (bukan list) agar tidak dapat diubah secara tidak sengaja. Ini adalah kebiasaan pertama dalam pemrograman fungsional: data bersifat tetap (immutable).
Siapkan folder kerja. Buat folder praktikum_pl, buka di VS Code (File → Open Folder), lalu buat file data_mhs.py sesuai bagian Studi Kasus.
Imperatif vs fungsional. Buat file p01.py. Hitung jumlah kuadrat bilangan genap dari 1 sampai 10 dengan dua gaya:
# p01.py
angka = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
# Gaya imperatif: mengubah variabel total berulang kali
total = 0
for x in angka:
if x % 2 == 0:
total += x * x
print("Imperatif :", total) # hasil: 220
# Gaya fungsional: satu ekspresi, tanpa variabel yang berubah
total_f = sum(x * x for x in angka if x % 2 == 0)
print("Fungsional:", total_f) # hasil: 220
Fungsi murni vs tidak murni. Tambahkan:
diskon = 0.1 # variabel global (state)
def harga_akhir_impure(harga):
return harga - harga * diskon # bergantung pada variabel di luar fungsi
def harga_akhir_pure(harga, diskon):
return harga - harga * diskon # hanya bergantung pada argumen
print(harga_akhir_impure(100_000)) # hasil: 90000.0
diskon = 0.5
print(harga_akhir_impure(100_000)) # hasil: 50000.0 -> masukan sama, hasil berbeda!
print(harga_akhir_pure(100_000, 0.1)) # hasil: 90000.0 -> selalu sama
Efek samping pada list. Amati perbedaan fungsi yang mengubah argumen dan fungsi yang membuat data baru:
def tambah_mutasi(daftar, x):
daftar.append(x) # efek samping: list milik pemanggil ikut berubah
return daftar
def tambah_murni(daftar, x):
return daftar + [x] # membuat list baru, list asli tetap
a = [1, 2]
b = tambah_murni(a, 3)
print(a, b) # hasil: [1, 2] [1, 2, 3]
c = tambah_mutasi(a, 3)
print(a, c) # hasil: [1, 2, 3] [1, 2, 3] -> a ikut berubah
Imutabilitas dengan tuple.
t = (1, 2, 3)
# t[0] = 10 # hapus tanda # lalu jalankan: TypeError
t2 = (10,) + t[1:] # "mengubah" = membuat tuple baru
print(t, t2) # hasil: (1, 2, 3) (10, 2, 3)
Fungsi sebagai nilai (first-class function).
def kuadrat(x):
return x * x
f = kuadrat # fungsi disimpan dalam variabel
print(f(7)) # hasil: 49
print(list(map(kuadrat, [1, 2, 3]))) # hasil: [1, 4, 9]
def terapkan_dua_kali(fungsi, x): # fungsi menerima fungsi lain
return fungsi(fungsi(x))
print(terapkan_dua_kali(kuadrat, 3)) # hasil: 81
Gunakan data studi kasus. Ambil nama mahasiswa yang lulus dengan gaya fungsional:
from data_mhs import DATA_MHS
lulus = tuple(m["nama"] for m in DATA_MHS if m["nilai"] >= 60)
print(lulus)
# hasil: ('Andi Saputra', 'Bunga Lestari', 'Dimas Pratama', 'Fajar Nugroho', 'Gilang Ramadhan')
Masalah umum: Jika muncul 'python' is not recognized, Python belum masuk PATH. Instal ulang dan centang Add python.exe to PATH, atau gunakan perintah py. Jika muncul ModuleNotFoundError: No module named 'data_mhs', pastikan terminal berada di folder praktikum_pl.
E. Tugas
20
1. Tuliskan versi Python di komputer Anda dan keluaran langkah 3 sampai 7. Lampirkan screenshot terminal.
20
2. Jelaskan dengan bahasa Anda sendiri mengapa harga_akhir_impure disebut tidak murni, sedangkan harga_akhir_pure disebut murni.
20
3. Tulis ulang kode imperatif berikut menjadi satu ekspresi bergaya fungsional: hasil = [] / for x in range(1, 21): / if x % 3 == 0: hasil.append(x * 10)
20
4. Tulis fungsi murni tambah_bonus(daftar_nilai, bonus) yang mengembalikan tuple baru berisi setiap nilai ditambah bonus, tanpa mengubah tuple asli. Tunjukkan bahwa tuple asli tidak berubah.
20
5. Sebutkan tiga contoh efek samping dalam program sehari-hari (misalnya pada aplikasi kasir) dan jelaskan mengapa efek samping membuat program lebih sulit diuji.
F. Indikator Ketercapaian
Indikator
Skor maks.
Lingkungan Python dan VS Code siap digunakan
20
Mampu mengenali fungsi murni, efek samping, dan imutabilitas
40
Mampu menulis ulang kode imperatif menjadi bergaya fungsional
40
G. Refleksi
Saya dapat menjalankan file Python dari terminal VS Code.
Saya dapat membedakan fungsi murni dan fungsi yang memiliki efek samping.
Saya memahami bahwa fungsi dapat diperlakukan seperti nilai.
Pertemuan berikutnya: Anda akan menyelesaikan satu masalah yang sama dengan gaya prosedural, OOP, dan fungsional, lalu membandingkannya. Pertemuan 2 adalah tugas yang dinilai.
LKM Praktikum PL · Pertemuan 02
Paradigma Fungsional vs Prosedural vs Berorientasi Objek
Praktik paradigma dasar, ceramah, dan diskusi · 100 menit
Asesmen
Tugas · 100% dari CPMK 069
A. Tujuan Praktikum
Mahasiswa mampu menyelesaikan satu masalah dengan tiga paradigma: prosedural, OOP, dan fungsional.
Mahasiswa mampu membandingkan kelebihan dan kekurangan setiap paradigma.
Mahasiswa mampu memilih paradigma yang tepat untuk suatu kebutuhan.
B. Alat dan Bahan
Python 3.10+, VS Code, file data_mhs.py
C. Dasar Teori
Aspek
Prosedural
OOP
Fungsional
Unit utama
Prosedur / langkah
Objek (class)
Fungsi
Data
Variabel yang diubah-ubah
Atribut objek, bisa berubah
Nilai tetap (immutable)
Perulangan
for/while
for/while di dalam method
Rekursi, map, filter, reduce, comprehension
Penggunaan ulang
Memanggil prosedur
Pewarisan, polimorfisme
Komposisi fungsi, fungsi tingkat tinggi
Kekuatan
Sederhana, mudah diikuti
Memodelkan entitas dunia nyata
Mudah diuji, aman untuk paralel, minim bug state
Masalah yang akan diselesaikan: hitung rata-rata nilai mahasiswa prodi TI dan daftar nama mahasiswa TI yang lulus (nilai ≥ 60).
D. Langkah Praktikum
Versi prosedural. Buat p02.py:
# p02.py
from data_mhs import DATA_MHS
def proses_prosedural(data):
jumlah = 0
banyak = 0
lulus = []
for m in data:
if m["prodi"] == "TI":
jumlah += m["nilai"]
banyak += 1
if m["nilai"] >= 60:
lulus.append(m["nama"])
rata = jumlah / banyak if banyak else 0
return rata, lulus
print("Prosedural :", proses_prosedural(DATA_MHS))
# hasil: (74.75, ['Andi Saputra', 'Dimas Pratama', 'Fajar Nugroho'])
Versi OOP. Tambahkan:
class Mahasiswa:
def __init__(self, nim, nama, prodi, sks, nilai):
self.nim, self.nama, self.prodi = nim, nama, prodi
self.sks, self.nilai = sks, nilai
def lulus(self):
return self.nilai >= 60
class Kelas:
def __init__(self, daftar):
self.daftar = daftar
def filter_prodi(self, prodi):
return Kelas([m for m in self.daftar if m.prodi == prodi])
def rata_rata(self):
return sum(m.nilai for m in self.daftar) / len(self.daftar) if self.daftar else 0
def nama_lulus(self):
return [m.nama for m in self.daftar if m.lulus()]
kelas = Kelas([Mahasiswa(**m) for m in DATA_MHS])
ti = kelas.filter_prodi("TI")
print("OOP :", (ti.rata_rata(), ti.nama_lulus()))
Versi fungsional. Tambahkan:
def dari_prodi(prodi):
def cek(m):
return m["prodi"] == prodi
return cek # fungsi yang menghasilkan fungsi
def ambil_nilai(m):
return m["nilai"]
def rata_rata(xs):
xs = tuple(xs)
return sum(xs) / len(xs) if xs else 0
def proses_fungsional(data):
ti = tuple(filter(dari_prodi("TI"), data))
return (rata_rata(map(ambil_nilai, ti)),
tuple(m["nama"] for m in ti if m["nilai"] >= 60))
print("Fungsional :", proses_fungsional(DATA_MHS))
Uji kemudahan pengujian. Fungsi murni mudah diuji dengan assert karena tidak bergantung pada state:
1. Selesaikan dengan paradigma prosedural (fungsi berisi perulangan dan variabel akumulator).
20
2. Selesaikan dengan paradigma OOP (class Barang dengan method subtotal() dan class Keranjang dengan method total()).
25
3. Selesaikan dengan paradigma fungsional: fungsi murni subtotal(barang) dan total(keranjang) tanpa perulangan for eksplisit dan tanpa variabel yang diubah. Sertakan minimal 2 assert untuk menguji fungsi Anda.
20
4. Buat tabel perbandingan ketiga solusi Anda: jumlah baris kode, ada/tidaknya variabel yang berubah, dan kemudahan pengujian.
15
5. Jika toko ingin menambah aturan "beli ≥ 10 barang yang sama diskon 5%", solusi mana yang paling mudah diubah? Jelaskan alasannya.
F. Indikator & Rubrik Penilaian
Indikator RPS: Mahasiswa mampu menerapkan konsep pemrograman fungsional dan membedakan paradigma pemrograman fungsional dengan prosedural dan object oriented programming.
Kriteria
Sangat baik (skor penuh)
Skor maks.
Kebenaran program
Ketiga solusi menghasilkan total 276500
40
Ketepatan paradigma
Solusi fungsional benar-benar murni, solusi OOP memakai class dengan tepat
25
Pengujian
Ada assert yang bermakna, termasuk kasus batas
10
Analisis perbandingan
Perbandingan objektif dan alasan logis
25
G. Refleksi
Saya dapat menulis solusi yang sama dengan tiga paradigma.
Saya dapat menjelaskan kelebihan paradigma fungsional dalam hal pengujian.
Saya dapat menulis fungsi yang mengembalikan fungsi lain.
Pertemuan berikutnya: Notasi dasar fungsional, yaitu cara menuliskan fungsi seperti di matematika: domain, kodomain, komposisi, dan currying.
LKM Praktikum PL · Pertemuan 03
Pemrograman Notasi Dasar Fungsional
Sub-CPMK
SUB-CPMK 070.1 — Menerapkan pemrograman menggunakan notasi fungsional
CPMK
CPMK 070
Bahan Kajian
Pemrograman notasi dasar fungsional
Metode
Praktikum notasi dasar fungsional, diskusi, dan ceramah · 100 menit
Asesmen
Non-asesmen · 0%
A. Tujuan Praktikum
Mahasiswa mampu menuliskan fungsi dalam notasi matematis (domain → kodomain) dan menerjemahkannya ke Python dengan type hint.
Mahasiswa mampu menerapkan komposisi fungsi.
Mahasiswa mampu menerapkan currying dan aplikasi parsial (partial application).
B. Alat dan Bahan
Python 3.10+, VS Code, paket mypy (opsional, untuk memeriksa type hint)
C. Dasar Teori
Dalam matematika, fungsi ditulis dengan signature (domain dan kodomain) dan definisi (aturan pemetaan). Python menuliskan signature dengan type hint. Type hint tidak dipaksakan saat program berjalan, tetapi memperjelas maksud fungsi dan dapat diperiksa dengan alat seperti mypy.
Notasi matematis
Notasi Python
Keterangan
f : ℤ → ℤ
def f(x: int) -> int:
Signature
f(x) = x² + 1
return x ** 2 + 1
Definisi
g : ℤ × ℤ → ℤ
def g(x: int, y: int) -> int:
Fungsi dua argumen
(f ∘ g)(x) = f(g(x))
compose(f, g)(x)
Komposisi
h : ℤ → (ℤ → ℤ)
def h(x: int) -> Callable[[int], int]:
Currying: fungsi yang menghasilkan fungsi
Currying mengubah fungsi n-argumen menjadi rangkaian fungsi satu argumen: tambah(3, 4) menjadi tambah(3)(4). Aplikasi parsial mengisi sebagian argumen lebih dahulu sehingga terbentuk fungsi baru yang lebih khusus.
def tambah(x: int, y: int) -> int: # tambah : Z x Z -> Z
return x + y
def tambah_c(x: int) -> Callable[[int], int]: # tambah_c : Z -> (Z -> Z)
def dalam(y: int) -> int:
return x + y
return dalam
print(tambah(3, 4)) # hasil: 7
print(tambah_c(3)(4)) # hasil: 7
tambah5 = tambah_c(5) # fungsi baru yang "mengingat" x = 5
print(tambah5(10)) # hasil: 15
Fungsi dengan domain data studi kasus. Tulis signature dengan jelas:
from data_mhs import DATA_MHS
Mhs = dict # alias tipe agar signature mudah dibaca
def nilai_dari(m: Mhs) -> int: # nilai_dari : Mhs -> Z
return m["nilai"]
def lulus(n: int) -> bool: # lulus : Z -> Bool
return n >= 60
status = compose(lulus, nilai_dari) # status : Mhs -> Bool
print([status(m) for m in DATA_MHS])
# hasil: [True, True, False, True, False, True, True]
Perhatikan bahwa compose di langkah 2 diberi type hint int, tetapi tetap berjalan untuk tipe lain karena Python tidak memaksakan type hint. Di sinilah peran mypy.
(Opsional) Periksa type hint dengan mypy.
pip install mypy
mypy p03.py
Tambahkan baris print(f("3")), jalankan mypy lagi, dan amati pesan kesalahannya.
E. Tugas
20
1. Tulis notasi matematis (signature + definisi) dan kode Python dengan type hint untuk: (a) luas lingkaran dari jari-jari, (b) konversi detik ke tuple (jam, menit, detik).
20
2. Diketahui p(x) = x + 3 dan q(x) = x * x. Hitung secara manual (p ∘ q)(4) dan (q ∘ p)(4), lalu buktikan dengan compose.
20
3. Buat versi curried dari fungsi nilai_akhir(tugas, uts, uas) = 0.3·tugas + 0.3·uts + 0.4·uas sehingga dapat dipanggil nilai_akhir_c(80)(70)(90). Tulis juga signature-nya.
20
4. Gunakan partial untuk membuat fungsi diskon10 dan diskon25 dari fungsi harga_diskon(persen, harga). Tampilkan hasil untuk harga 200.000.
20
5. Jelaskan perbedaan currying dan aplikasi parsial, serta manfaat keduanya dalam menyusun program.
F. Indikator Ketercapaian
Indikator RPS: Mahasiswa mampu menerapkan notasi dasar fungsional dalam konsep pemrograman berbasis fungsional.
Indikator
Skor maks.
Menuliskan signature dan definisi fungsi dengan benar
40
Menerapkan komposisi fungsi
20
Menerapkan currying dan aplikasi parsial
40
G. Refleksi
Saya dapat membaca dan menulis signature fungsi seperti f : A → B.
Saya memahami bahwa urutan komposisi fungsi berpengaruh pada hasil.
Saya dapat membuat fungsi baru dengan currying atau partial.
Pertemuan berikutnya: Notasi fungsional tingkat lanjut: notasi himpunan dan comprehension, map, filter, reduce, serta pipeline. Pertemuan 4 adalah tugas yang dinilai.
LKM Praktikum PL · Pertemuan 04
Pemrograman Notasi Fungsional
Sub-CPMK
SUB-CPMK 070.1 — Menerapkan pemrograman menggunakan notasi fungsional
CPMK
CPMK 070
Bahan Kajian
Pemrograman notasi fungsional
Metode
Praktikum notasi fungsional, ceramah, dan diskusi · 100 menit
Asesmen
Tugas · 100% dari CPMK 070
A. Tujuan Praktikum
Mahasiswa mampu menerjemahkan notasi pembentuk himpunan (set-builder) menjadi comprehension.
Mahasiswa mampu menerapkan fungsi tingkat tinggi map, filter, dan reduce.
Mahasiswa mampu menyusun pipeline pengolahan data dari komposisi fungsi.
B. Alat dan Bahan
Python 3.10+, VS Code, data_mhs.py, modul bawaan functools dan operator
C. Dasar Teori
Notasi fungsional
Python
Hasil
{ x² | x ∈ S, x genap }
[x**2 for x in S if x % 2 == 0]
List hasil seleksi dan transformasi
map f [x₁, …, xₙ] = [f x₁, …, f xₙ]
map(f, xs)
Menerapkan f ke setiap elemen
filter p xs = [x | x ∈ xs, p x]
filter(p, xs)
Menyaring elemen yang memenuhi p
foldl ⊕ e [x₁, …, xₙ] = (…((e ⊕ x₁) ⊕ x₂) …) ⊕ xₙ
reduce(op, xs, e)
Meringkas list menjadi satu nilai
map dan filter di Python 3 bersifat lazy: menghasilkan iterator yang baru dihitung ketika dibutuhkan. Bungkus dengan list() atau tuple() untuk melihat isinya. Iterator hanya dapat dibaca satu kali.
Pipeline adalah rangkaian fungsi yang dilalui data secara berurutan: data → saring → ubah → ringkas.
D. Langkah Praktikum
Notasi himpunan ke comprehension. Buat p04.py:
# p04.py
S = range(1, 11)
A = [x ** 2 for x in S if x % 2 == 0] # { x^2 | x in S, x genap }
print(A) # hasil: [4, 16, 36, 64, 100]
P = [(x, y) for x in (1, 2, 3) for y in "ab"] # { (x, y) | x in {1,2,3}, y in {a,b} }
print(P) # hasil: [(1, 'a'), (1, 'b'), (2, 'a'), (2, 'b'), (3, 'a'), (3, 'b')]
K = {x % 3 for x in S} # comprehension himpunan
D = {x: x ** 3 for x in range(1, 4)} # comprehension dictionary
print(K, D) # hasil: {0, 1, 2} {1: 1, 2: 8, 3: 27}
Bandingkan tiga notasi untuk hasil yang sama: comprehension, map/filter, dan pipeline. Tentukan mana yang paling mudah dibaca menurut Anda.
E. Tugas Dinilai
Gunakan DATA_MHS. Jangan memakai perulangan for sebagai pernyataan (comprehension diperbolehkan).
15
1. Tulis notasi himpunan dan comprehension untuk nama mahasiswa prodi TI yang mengambil SKS ≥ 20. (Cek: Andi, Dimas, Fajar)
15
2. Gunakan map untuk menghasilkan tuple berisi pasangan (nim, NAMA DALAM HURUF KAPITAL).
15
3. Gunakan reduce untuk menghitung total SKS seluruh mahasiswa. Tuliskan juga jejak (trace) perhitungannya. (Cek: 144)
25
4. Gunakan pipe untuk menghitung rata-rata nilai mahasiswa prodi SI. (Cek: 76.0)
20
5. Buat fungsi compose_all(*fs) yang menyusun banyak fungsi dari kanan ke kiri: compose_all(f, g, h)(x) = f(g(h(x))). Gunakan reduce dan sertakan contoh pemanggilannya.
10
6. Jelaskan apa yang dimaksud lazy evaluation pada map/filter dan tunjukkan contoh akibatnya.
F. Indikator & Rubrik Penilaian
Indikator RPS: Mahasiswa mampu menerapkan konsep pemrograman fungsional berbasis notasi fungsional.
Kriteria
Sangat baik (skor penuh)
Skor maks.
Kebenaran hasil
Semua jawaban sesuai nilai cek
40
Ketepatan notasi
Notasi himpunan, map/filter/reduce, dan pipeline dipakai sesuai fungsinya
35
Gaya fungsional
Tanpa perulangan eksplisit dan tanpa mutasi
15
Penjelasan
Penjelasan lazy evaluation tepat dan ada contoh
10
G. Refleksi
Saya dapat menerjemahkan notasi himpunan menjadi comprehension.
Saya dapat memilih antara map, filter, dan reduce sesuai kebutuhan.
Saya dapat menyusun pipeline pengolahan data.
Pertemuan berikutnya: Ekspresi dasar fungsional: perbedaan ekspresi dan pernyataan, operator, presedensi, dan model substitusi.
LKM Praktikum PL · Pertemuan 05
Pemrograman Berbasis Ekspresi Dasar Fungsional
Sub-CPMK
SUB-CPMK 071.1 — Menerapkan pemrograman fungsional berbasis ekspresi dasar dan ekspresi kondisional
CPMK
CPMK 071
Bahan Kajian
Pemrograman berbasis ekspresi dasar fungsional
Metode
Praktikum ekspresi dasar fungsional · 100 menit
Asesmen
Non-asesmen · 0% (materi diujikan pada UTS)
A. Tujuan Praktikum
Mahasiswa mampu membedakan ekspresi (expression) dan pernyataan (statement).
Mahasiswa mampu menggunakan ekspresi aritmetika, relasional, logika, dan string sesuai aturan presedensi.
Mahasiswa mampu mengevaluasi fungsi dengan model substitusi.
B. Alat dan Bahan
Python 3.10+ (REPL interaktif dan VS Code), modul math dan operator
C. Dasar Teori
Ekspresi adalah potongan kode yang dievaluasi menjadi nilai, misalnya 2 + 3, len("abc"), atau x > 0. Pernyataan adalah perintah yang melakukan aksi, misalnya x = 5, for, atau import. Program fungsional dibangun dari ekspresi: setiap fungsi idealnya cukup berupa satu ekspresi return.
Model substitusi: karena fungsi murni tidak bergantung pada state, pemanggilan fungsi dapat dievaluasi dengan mengganti nama fungsi dengan definisinya. Sifat ini disebut referential transparency.
Ekspresi penugasan (walrus) :=. Berguna untuk memberi nama hasil antara di dalam ekspresi:
data = (12, 45, 7, 30)
print([y for x in data if (y := x * 2) > 20]) # hasil: [24, 90, 60]
Jebakan bilangan pecahan:0.1 + 0.2 == 0.3 bernilai False karena representasi biner. Untuk membandingkan pecahan gunakan math.isclose(a, b), dan untuk uang gunakan bilangan bulat (rupiah) atau modul decimal.
E. Tugas
20
1. Tuliskan hasil setiap baris pada langkah 1 dan jelaskan mengapa -2 ** 2 menghasilkan -4 dan 2 ** 3 ** 2 menghasilkan 512.
20
2. Tuliskan langkah evaluasi model substitusi untuk nilai_akhir(70 + 10, 70, 45 * 2).
20
3. Tulis fungsi satu ekspresi ipk(bobot_sks) yang menerima tuple pasangan (bobot, sks) dan menghasilkan IPK. Contoh: ipk(((4, 3), (3, 2), (2, 3))) = 3.0.
20
4. Tulis fungsi satu ekspresi cicilan(pokok, bunga_tahunan, bulan) dengan rumus anuitas M = P·r / (1 − (1 + r)−n), r = bunga bulanan. Hitung untuk P = 12.000.000, bunga 12%/tahun, 12 bulan.
20
5. Jelaskan perbedaan ekspresi dan pernyataan. Mengapa paradigma fungsional lebih menyukai ekspresi?
F. Indikator Ketercapaian
Indikator RPS: Mahasiswa mampu menerapkan dasar ekspresi dasar fungsional dalam konsep paradigma pemrograman fungsional.
Indikator
Skor maks.
Memahami presedensi dan jenis ekspresi
30
Menelusuri evaluasi dengan model substitusi
30
Menulis fungsi berbasis satu ekspresi dengan benar
40
G. Refleksi
Saya dapat memprediksi hasil ekspresi berdasarkan presedensi operator.
Saya dapat menelusuri pemanggilan fungsi dengan model substitusi.
Saya dapat menulis fungsi yang isinya cukup satu ekspresi.
Pertemuan berikutnya: Ekspresi kondisional, yaitu percabangan yang menghasilkan nilai, termasuk match-case.
LKM Praktikum PL · Pertemuan 06
Pemrograman Berbasis Ekspresi Kondisional
Sub-CPMK
SUB-CPMK 071.1 — Menerapkan pemrograman fungsional berbasis ekspresi dasar dan ekspresi kondisional
CPMK
CPMK 071
Bahan Kajian
Pemrograman berbasis ekspresi kondisional
Metode
Praktikum ekspresi kondisional · 100 menit
Asesmen
Non-asesmen · 0% (materi diujikan pada UTS)
A. Tujuan Praktikum
Mahasiswa mampu menulis percabangan sebagai ekspresi (a if kondisi else b).
Mahasiswa mampu menggunakan pencocokan pola (match-case) dengan guard.
Mahasiswa mampu mengganti rantai if-elif dengan tabel fungsi (dictionary dispatch).
B. Alat dan Bahan
Python 3.10+ (wajib, karena match-case baru tersedia sejak 3.10), VS Code, data_mhs.py
C. Dasar Teori
Dalam gaya imperatif, if adalah pernyataan yang memilih aksi. Dalam gaya fungsional, percabangan adalah ekspresi yang memilih nilai. Setiap cabang wajib menghasilkan nilai, sehingga tidak ada kasus yang terlupa.
Bentuk
Sintaks
Kapan dipakai
Ekspresi kondisional
A if kondisi else B
Dua pilihan, atau rantai pendek
Pencocokan pola
match nilai: case pola: ...
Memeriksa bentuk/struktur data (tuple, dict, objek)
Guard
case pola if kondisi:
Pola dengan syarat tambahan
Dictionary dispatch
{"+": add, "-": sub}[op](a, b)
Banyak pilihan berdasarkan kunci
Pola case _: adalah wildcard yang cocok dengan apa pun. Letakkan di akhir sebagai kasus bawaan.
D. Langkah Praktikum
Ekspresi kondisional sederhana. Buat p06.py:
# p06.py
def mutlak(x: float) -> float:
return x if x >= 0 else -x
def maks3(a, b, c):
return (a if a >= c else c) if a >= b else (b if b >= c else c)
def status(nilai: float) -> str:
return "Lulus" if nilai >= 60 else "Tidak Lulus"
print(mutlak(-7), maks3(4, 9, 2), status(58)) # hasil: 7 9 Tidak Lulus
Rantai ekspresi kondisional. Huruf mutu sesuai tabel studi kasus:
def huruf_mutu(nilai: float) -> str:
return ("A" if nilai >= 85 else
"B" if nilai >= 75 else
"C" if nilai >= 60 else
"D" if nilai >= 50 else
"E")
from data_mhs import DATA_MHS
print(tuple((m["nama"].split()[0], huruf_mutu(m["nilai"])) for m in DATA_MHS))
# hasil: (('Andi', 'B'), ('Bunga', 'A'), ('Citra', 'D'), ('Dimas', 'C'),
# ('Eka', 'D'), ('Fajar', 'A'), ('Gilang', 'B'))
match-case dengan nilai literal.
def keterangan(huruf: str) -> str:
match huruf:
case "A" | "B":
return "Sangat baik / baik"
case "C":
return "Cukup"
case "D" | "E":
return "Tidak lulus"
case _:
return "Huruf tidak dikenal"
print(keterangan("B"), "|", keterangan("X")) # hasil: Sangat baik / baik | Huruf tidak dikenal
match-case dengan struktur dan guard.
def kuadran(titik: tuple) -> str:
match titik:
case (0, 0):
return "Titik asal"
case (x, 0):
return "Sumbu X"
case (0, y):
return "Sumbu Y"
case (x, y) if x > 0 and y > 0:
return "Kuadran I"
case (x, y) if x < 0 and y > 0:
return "Kuadran II"
case (x, y) if x < 0:
return "Kuadran III"
case _:
return "Kuadran IV"
print([kuadran(t) for t in [(0, 0), (5, 0), (2, 3), (-1, 4), (-2, -2), (3, -1)]])
# hasil: ['Titik asal', 'Sumbu X', 'Kuadran I', 'Kuadran II', 'Kuadran III', 'Kuadran IV']
match-case pada dictionary.
def predikat(m: dict) -> str:
match m:
case {"prodi": "TI", "nilai": n} if n >= 85:
return "Kandidat asisten lab TI"
case {"nilai": n} if n < 60:
return "Wajib remedial"
case _:
return "-"
print([predikat(m) for m in DATA_MHS])
Dictionary dispatch sebagai pengganti rantai if-elif:
import operator as op
OPERASI = {"+": op.add, "-": op.sub, "*": op.mul, "/": op.truediv}
def hitung(a: float, simbol: str, b: float):
return OPERASI[simbol](a, b) if simbol in OPERASI else None
print(hitung(8, "*", 5), hitung(8, "/", 0.5), hitung(8, "^", 2)) # hasil: 40 16.0 None
Hati-hati: pola case x: (nama tanpa titik) bukan pembanding dengan variabel x, melainkan menangkap nilai apa pun ke dalam x. Untuk membandingkan dengan konstanta, gunakan literal atau nama bertitik seperti case Warna.MERAH:.
E. Tugas
20
1. Tulis fungsi satu ekspresi kabisat(tahun): tahun kabisat jika habis dibagi 400, atau habis dibagi 4 tetapi tidak habis dibagi 100. Uji dengan 1900, 2000, 2024, 2026.
20
2. Tarif parkir motor: 2 jam pertama Rp2.000, setiap jam berikutnya Rp1.000, maksimal Rp10.000 per hari. Tulis tarif_parkir(jam) dengan ekspresi kondisional.
20
3. Tulis bobot(huruf) dengan match-case yang mengembalikan 4.0/3.0/2.0/1.0/0.0, lalu hitung bobot rata-rata seluruh mahasiswa di DATA_MHS dengan menggabungkan huruf_mutu dan bobot.
25
4. Tulis proses_perintah(perintah) dengan match-case untuk tuple: ("tambah", a, b), ("kali", a, b), ("negasi", a), dan perintah lain → "perintah tidak dikenal".
15
5. Bandingkan tiga cara percabangan (ekspresi kondisional, match-case, dictionary dispatch). Kapan masing-masing paling tepat?
F. Indikator Ketercapaian
Indikator RPS: Mahasiswa mampu menerapkan konsep ekspresi fungsional dalam pemrograman.
Indikator
Skor maks.
Menulis percabangan sebagai ekspresi yang menghasilkan nilai
40
Menerapkan match-case dengan pola dan guard
40
Memilih bentuk percabangan yang tepat
20
G. Refleksi
Saya dapat menulis rantai ekspresi kondisional yang mudah dibaca.
Saya dapat menggunakan match-case untuk tuple dan dictionary.
Saya memahami fungsi wildcard case _.
Pertemuan berikutnya: Ekspresi rekursif, yaitu fungsi yang memanggil dirinya sendiri sebagai pengganti perulangan.
LKM Praktikum PL · Pertemuan 07
Pemrograman Berbasis Ekspresi Rekursif Fungsional
Sub-CPMK
SUB-CPMK 071.1 — Menerapkan pemrograman fungsional berbasis ekspresi dasar dan ekspresi kondisional (dilanjutkan ke ekspresi rekursif)
Non-asesmen · 0% (materi diujikan pada UTS dan UAS)
A. Tujuan Praktikum
Mahasiswa mampu mengidentifikasi kasus dasar (base case) dan kasus rekursif.
Mahasiswa mampu menulis fungsi rekursif sebagai pengganti perulangan.
Mahasiswa mampu menelusuri pemanggilan rekursif dan memahami batas kedalaman rekursi serta memoisasi.
B. Alat dan Bahan
Python 3.10+, VS Code, modul sys, time, functools
C. Dasar Teori
Fungsi rekursif adalah fungsi yang memanggil dirinya sendiri. Bahasa fungsional murni tidak memiliki for/while, sehingga semua pengulangan ditulis sebagai rekursi. Fungsi rekursif yang benar selalu memiliki:
Kasus dasar: kondisi berhenti yang langsung menghasilkan nilai tanpa memanggil diri sendiri.
Kasus rekursif: memanggil diri sendiri dengan masalah yang lebih kecil sehingga akhirnya mencapai kasus dasar.
Setiap pemanggilan menempati satu frame di call stack. Python membatasi kedalaman rekursi (bawaan sekitar 1000) dan tidak melakukan optimasi tail call. Untuk rekursi yang menghitung ulang submasalah yang sama (misalnya Fibonacci), gunakan memoisasi dengan @lru_cache.
D. Langkah Praktikum
Faktorial dan jejaknya. Buat p07.py:
# p07.py
def faktorial(n: int) -> int:
return 1 if n == 0 else n * faktorial(n - 1)
def faktorial_jejak(n: int, kedalaman: int = 0) -> int:
print(" " * kedalaman + f"faktorial({n})")
hasil = 1 if n == 0 else n * faktorial_jejak(n - 1, kedalaman + 1)
print(" " * kedalaman + f"=> {hasil}")
return hasil
print(faktorial(5)) # hasil: 120
faktorial_jejak(4) # amati bentuk "turun lalu naik"
Rekursi pada bilangan.
def jumlah_digit(n: int) -> int:
return n if n < 10 else n % 10 + jumlah_digit(n // 10)
def fpb(a: int, b: int) -> int: # algoritma Euclid
return a if b == 0 else fpb(b, a % b)
def pangkat(b: int, e: int) -> int: # pangkat cepat: O(log e)
if e == 0:
return 1
setengah = pangkat(b, e // 2)
return setengah * setengah if e % 2 == 0 else b * setengah * setengah
def biner(n: int) -> str:
return str(n) if n < 2 else biner(n // 2) + str(n % 2)
print(jumlah_digit(2304), fpb(48, 18), pangkat(2, 10), biner(13))
# hasil: 9 6 1024 1101
Fibonacci: rekursi bercabang dan memoisasi.
import time
from functools import lru_cache
def fib(n: int) -> int:
return n if n < 2 else fib(n - 1) + fib(n - 2)
@lru_cache(maxsize=None)
def fib_memo(n: int) -> int:
return n if n < 2 else fib_memo(n - 1) + fib_memo(n - 2)
for f in (fib, fib_memo):
mulai = time.perf_counter()
hasil = f(30)
print(f.__name__, hasil, f"{time.perf_counter() - mulai:.4f} detik")
# fib 832040 (sekitar 0,2–0,5 detik) ; fib_memo 832040 (hampir 0 detik)
Menara Hanoi dengan hasil berupa tuple langkah (bukan print):
import sys
print(sys.getrecursionlimit()) # umumnya 1000
# faktorial(5000) # hapus tanda # : RecursionError: maximum recursion depth exceeded
Rekursi tanpa kasus dasar. Tulis def salah(n): return n + salah(n - 1), panggil salah(5), lalu baca pesan errornya. Diskusikan mengapa kasus dasar wajib ada.
E. Tugas
20
1. Tulis definisi matematis (kasus dasar dan rekursif) dan fungsi Python rekursif jumlah_deret(n) = 1 + 2 + … + n. Tuliskan jejak evaluasi untuk n = 4.
20
2. Tulis fungsi rekursif balik_angka(n) yang membalik digit bilangan, contoh 2304 → 4032, tanpa mengubah ke string.
20
3. Tulis fungsi rekursif kombinasi(n, k) dengan rumus Pascal: C(n,k) = C(n−1,k−1) + C(n−1,k), C(n,0) = C(n,n) = 1. Hitung C(20,10) dengan dan tanpa @lru_cache lalu bandingkan waktunya.
20
4. Berapa jumlah langkah hanoi(n) untuk n = 1 sampai 6? Temukan rumus umumnya dan jelaskan secara rekursif.
20
5. Jelaskan apa yang terjadi di call stack ketika faktorial(4) dijalankan, dan mengapa fib tanpa memoisasi sangat lambat.
F. Indikator Ketercapaian
Indikator RPS: Mahasiswa mampu menerapkan konsep pemrograman fungsional rekursif atau fungsi yang dapat memanggil dirinya sendiri.
Indikator
Skor maks.
Menentukan kasus dasar dan kasus rekursif dengan benar
40
Fungsi rekursif berjalan benar dan berhenti
40
Memahami call stack dan memoisasi
20
G. Refleksi
Saya selalu menulis kasus dasar sebelum kasus rekursif.
Saya dapat menelusuri jejak pemanggilan rekursif.
Saya tahu kapan memoisasi diperlukan.
Pertemuan berikutnya: Ekspresi lambda dan UTS. Pelajari kembali materi Pertemuan 5–7.
LKM Praktikum PL · Pertemuan 08
Pemrograman Berbasis Ekspresi Lambda & UTS
Sub-CPMK
SUB-CPMK 071.2 — Menerapkan pemrograman fungsional berbasis ekspresi rekursif dan ekspresi lambda
CPMK
CPMK 071
Bahan Kajian
Pemrograman berbasis ekspresi lambda
Metode
Praktikum pemrograman berbasis ekspresi lambda · 100 menit
Asesmen
UTS · 100% dari CPMK 071
A. Tujuan Praktikum
Mahasiswa mampu menulis fungsi anonim dengan ekspresi lambda.
Mahasiswa mampu memakai lambda sebagai argumen sorted, max, min, map, filter, dan reduce.
Mahasiswa mampu menggunakan closure dan menghindari jebakan late binding.
Mahasiswa mampu menggabungkan ekspresi dasar, kondisional, dan lambda dalam soal UTS.
B. Alat dan Bahan
Python 3.10+, VS Code, data_mhs.py
C. Dasar Teori
Ekspresi lambda berasal dari kalkulus lambda (Alonzo Church, 1930-an), fondasi teoretis semua bahasa fungsional. Di Python, lambda membuat fungsi tanpa nama yang isinya satu ekspresi:
Satu ekspresi, termasuk ekspresi kondisional: lambda n: "L" if n >= 60 else "TL"
Pernyataan seperti return, for, if sebagai pernyataan, =
Parameter bawaan: lambda x, k=2: x * k
Docstring dan type hint
Closure adalah fungsi yang "mengingat" variabel dari lingkup tempat ia dibuat. PEP 8 menyarankan: jika fungsi perlu diberi nama, pakai def. Lambda paling tepat sebagai argumen sekali pakai.
D. Langkah Praktikum
Dasar lambda. Buat p08.py:
# p08.py
from functools import reduce
from data_mhs import DATA_MHS
print((lambda a, b: a + b)(3, 4)) # hasil: 7
status = lambda n: "Lulus" if n >= 60 else "Tidak Lulus" # hanya untuk latihan
print(status(75), status(40)) # hasil: Lulus Tidak Lulus
Lambda sebagai kunci pengurutan.
urut = sorted(DATA_MHS, key=lambda m: m["nilai"], reverse=True)
print([m["nama"] for m in urut][:3])
# hasil: ['Bunga Lestari', 'Fajar Nugroho', 'Andi Saputra']
print(max(DATA_MHS, key=lambda m: m["sks"])["nama"]) # hasil: Dimas Pratama
print(min(DATA_MHS, key=lambda m: m["nilai"])["nama"]) # hasil: Citra Dewi
# urut prodi (A-Z), lalu nilai (besar ke kecil)
for m in sorted(DATA_MHS, key=lambda m: (m["prodi"], -m["nilai"])):
print(m["prodi"], m["nilai"], m["nama"])
def pengali(n):
return lambda x: x * n
kali3, kali10 = pengali(3), pengali(10)
print(kali3(7), kali10(7)) # hasil: 21 70
def tambah_bonus(bonus):
return lambda m: {**m, "nilai": min(100, m["nilai"] + bonus)}
print(tuple(map(tambah_bonus(5), DATA_MHS))[2]) # Citra: nilai 55 -> 60, DATA_MHS tidak berubah
Jebakan late binding.
fs = [lambda x: x * i for i in range(3)]
print([f(10) for f in fs]) # hasil: [20, 20, 20] -> semua memakai i terakhir
fs = [lambda x, i=i: x * i for i in range(3)]
print([f(10) for f in fs]) # hasil: [0, 10, 20] -> i "dibekukan" lewat parameter bawaan
Ketentuan UTS: dikerjakan individu, boleh membuka modul ini dan dokumentasi Python, tidak boleh bertanya kepada teman atau memakai AI generatif. Semua fungsi harus murni. Tempel kode lengkap dan keluaran pada setiap kotak jawaban.
1. Ekspresi dasar. Tulis fungsi satu ekspresi nilai_persediaan(p) = harga × stok, lalu hitung total nilai persediaan semua produk.
20
2. Ekspresi kondisional. (a) status_stok(stok): "Habis" jika 0, "Menipis" jika kurang dari 10, selain itu "Aman", ditulis sebagai ekspresi kondisional. (b) diskon_kategori(kategori) dengan match-case: minuman 5%, makanan 10%, lainnya 0%.
25
3. Lambda. Dengan lambda, tampilkan: (a) nama produk urut dari harga termahal, (b) produk yang stoknya tersedia, (c) produk dengan nilai persediaan terbesar menggunakan max(..., key=lambda ...), (d) jumlah stok seluruh produk dengan reduce.
25
4. Gabungan. Buat fungsi laporan(produk) yang menghasilkan tuple (nama, harga_setelah_diskon, status_stok) hanya untuk produk yang tidak habis, terurut berdasarkan nama. Gunakan fungsi dari soal 1–3.
15
5. Analisis. Jelaskan mengapa fungsi-fungsi Anda murni, dan berikan satu contoh situasi ketika lambda sebaiknya diganti dengan def.
F. Indikator & Rubrik Penilaian UTS
Indikator RPS: Mahasiswa diharapkan mampu menerapkan konsep ekspresi dasar, ekspresi kondisional, dan lambda dalam pemrograman fungsional.
Kriteria
Sangat baik (skor penuh)
Skor maks.
Ekspresi dasar
Fungsi satu ekspresi benar, total persediaan benar
15
Ekspresi kondisional
Semua cabang benar, match-case memiliki wildcard
20
Ekspresi lambda
Lambda dipakai tepat sebagai argumen fungsi tingkat tinggi
25
Integrasi
Fungsi laporan benar, murni, dan menggunakan ulang fungsi lain
25
Analisis
Penjelasan kemurnian fungsi dan penggunaan lambda tepat
15
G. Refleksi
Saya dapat menulis lambda untuk argumen key pada sorted/max/min.
Saya memahami closure dan jebakan late binding.
Saya tahu kapan memakai lambda dan kapan memakai def.
Pertemuan berikutnya: Fungsi dengan type bentukan: NamedTuple, dataclass beku, Enum, dan tipe gabungan.
LKM Praktikum PL · Pertemuan 09
Pemrograman Fungsi dengan Type Bentukan
Sub-CPMK
SUB-CPMK 071.2 — Menerapkan pemrograman fungsional berbasis ekspresi rekursif dan ekspresi lambda
CPMK
CPMK 071
Bahan Kajian
Pemrograman fungsi dengan type bentukan fungsi
Metode
Praktikum berbasis type data bentukan · 100 menit
Asesmen
Non-asesmen · 0% (dipakai pada Pertemuan 13–16)
A. Tujuan Praktikum
Mahasiswa mampu mendefinisikan type bentukan yang immutable dengan NamedTuple dan @dataclass(frozen=True).
Mahasiswa mampu memakai Enum, alias tipe, Optional, dan Callable.
Mahasiswa mampu membuat tipe gabungan (sum type) dan memprosesnya dengan match-case.
B. Alat dan Bahan
Python 3.10+, VS Code, modul typing, dataclasses, enum
C. Dasar Teori
Type bentukan (composite / user-defined type) adalah tipe yang disusun dari tipe lain. Dalam pemrograman fungsional dikenal dua bentuk utama tipe data aljabar (algebraic data type):
Bentuk
Arti
Python
Product type (tipe "dan")
Gabungan beberapa nilai sekaligus: Mahasiswa = NIM dan nama dan nilai
NamedTuple, @dataclass(frozen=True)
Sum type (tipe "atau")
Salah satu dari beberapa bentuk: Bangun = Lingkaran atau Persegi atau Segitiga
Union[A, B, C] atau A | B | C
Enumerasi
Himpunan nilai tetap yang diberi nama
Enum
Tipe fungsi
Fungsi dari A ke B
Callable[[A], B]
Tipe opsional
Nilai A atau tidak ada
Optional[A] = A | None
Dibandingkan dict, type bentukan lebih aman: nama field diperiksa, dapat dibuat immutable, dan dapat dipakai pada pola match-case.
D. Langkah Praktikum
NamedTuple untuk Mahasiswa. Buat tipe.py (akan dipakai lagi nanti):
# tipe.py
from typing import NamedTuple, Optional, Callable
from data_mhs import DATA_MHS
class Mahasiswa(NamedTuple):
nim: str
nama: str
prodi: str
sks: int
nilai: float
MHS: tuple = tuple(Mahasiswa(**d) for d in DATA_MHS) # konversi dict -> Mahasiswa
if __name__ == "__main__":
m = MHS[0]
print(m) # hasil: Mahasiswa(nim='2304001', nama='Andi Saputra', prodi='TI', sks=20, nilai=82)
print(m.nama, m[4]) # hasil: Andi Saputra 82
nim, nama, *_ = m # NamedTuple tetap tuple: bisa di-unpack
m2 = m._replace(nilai=90)
print(m.nilai, m2.nilai) # hasil: 82 90 -> m tidak berubah
# m.nilai = 90 # hapus tanda # : AttributeError
Dataclass beku. Buat p09.py:
# p09.py
import math
from dataclasses import dataclass, replace
from enum import Enum
from typing import Union, Optional, Callable
from tipe import Mahasiswa, MHS
@dataclass(frozen=True)
class MataKuliah:
kode: str
nama: str
sks: int
mk = MataKuliah("40401502", "Praktikum Pemrograman Lanjutan", 1)
mk2 = replace(mk, sks=2) # salinan baru dengan sks berbeda
print(mk.sks, mk2.sks) # hasil: 1 2
# mk.sks = 3 # hapus tanda # : FrozenInstanceError
Enum untuk huruf mutu.
class Huruf(Enum):
A = 4.0
B = 3.0
C = 2.0
D = 1.0
E = 0.0
def ke_huruf(nilai: float) -> Huruf:
return (Huruf.A if nilai >= 85 else Huruf.B if nilai >= 75 else
Huruf.C if nilai >= 60 else Huruf.D if nilai >= 50 else Huruf.E)
h = ke_huruf(82)
print(h, h.name, h.value) # hasil: Huruf.B B 3.0
Sum type + match-case.
@dataclass(frozen=True)
class Lingkaran:
r: float
@dataclass(frozen=True)
class PersegiPanjang:
p: float
l: float
@dataclass(frozen=True)
class Segitiga:
alas: float
tinggi: float
Bangun = Union[Lingkaran, PersegiPanjang, Segitiga]
def luas(b: Bangun) -> float:
match b:
case Lingkaran(r=r):
return math.pi * r ** 2
case PersegiPanjang(p=p, l=l):
return p * l
case Segitiga(alas=a, tinggi=t):
return 0.5 * a * t
case _:
raise TypeError(f"Bukan bangun datar: {b!r}")
bangun = (Lingkaran(7), PersegiPanjang(4, 5), Segitiga(6, 3))
print([round(luas(b), 2) for b in bangun]) # hasil: [153.94, 20, 9.0]
Optional: hasil yang mungkin tidak ada.
def cari_mhs(nim: str, data: tuple) -> Optional[Mahasiswa]:
return next((m for m in data if m.nim == nim), None)
print(cari_mhs("2304006", MHS).nama) # hasil: Fajar Nugroho
print(cari_mhs("9999999", MHS)) # hasil: None
Callable: fungsi sebagai tipe.
Transformasi = Callable[[Mahasiswa], Mahasiswa]
def bonus(n: float) -> Transformasi:
return lambda m: m._replace(nilai=min(100, m.nilai + n))
def terapkan(f: Transformasi, data: tuple) -> tuple:
return tuple(map(f, data))
baru = terapkan(bonus(5), MHS)
print([m.nilai for m in baru]) # hasil: [87, 96, 60, 79, 63, 93, 84]
print([m.nilai for m in MHS]) # data asli tetap: [82, 91, 55, 74, 58, 88, 79]
E. Tugas
20
1. Buat NamedTupleBuku(isbn, judul, penulis, tahun, harga) dan tuple berisi minimal 4 buku. Tampilkan judul buku terbitan setelah 2015 yang diurutkan berdasarkan harga.
20
2. Tambahkan bangun Trapesium(a, b, t) pada sum type Bangun, lalu perbarui luas. Tulis juga fungsi keliling untuk Lingkaran dan PersegiPanjang.
20
3. Buat EnumHari (SENIN … MINGGU) dan fungsi hari_kerja(h: Hari) -> bool dengan match-case.
20
4. Dengan MHS dan ke_huruf, buat fungsi yang menghasilkan dictionary {Huruf: tuple nama}, misalnya {Huruf.A: ('Bunga Lestari', 'Fajar Nugroho'), ...}.
20
5. Jelaskan perbedaan product type dan sum type, serta keuntungan NamedTuple/dataclass beku dibandingkan dict biasa.
F. Indikator Ketercapaian
Indikator RPS: Mahasiswa diharapkan mampu menerapkan konsep type fungsi bentukan.
Indikator
Skor maks.
Mendefinisikan product type yang immutable
30
Mendefinisikan sum type dan memprosesnya dengan match-case
40
Menggunakan Enum, Optional, dan Callable dengan tepat
30
G. Refleksi
Saya dapat membuat NamedTuple dan dataclass beku.
Saya dapat memproses sum type dengan match-case.
Saya menyimpan tipe.py untuk dipakai di pertemuan berikutnya.
Pertemuan berikutnya: Masuk ke CPMK 072, yaitu list dengan elemen sederhana dan karakter (string sebagai list karakter).
LKM Praktikum PL · Pertemuan 10
List dengan Elemen Sederhana & Karakter
Sub-CPMK
SUB-CPMK 072.1 — Menerapkan pemrograman fungsional berbasis list
CPMK
CPMK 072
Bahan Kajian
Pemrograman fungsional berbasis list menggunakan elemen sederhana dan karakter
Metode
Praktikum elemen sederhana dan karakter · 100 menit
Asesmen
Non-asesmen · 0% (materi diujikan pada UAS)
A. Tujuan Praktikum
Mahasiswa mampu memandang list sebagai pasangan head (elemen pertama) dan tail (sisanya).
Mahasiswa mampu mengolah string sebagai list karakter dengan fungsi murni.
Mahasiswa mampu memakai operasi list: indeks, slicing, zip, enumerate, join, ord/chr.
B. Alat dan Bahan
Python 3.10+, VS Code, data_mhs.py, modul collections
C. Dasar Teori
Dalam bahasa fungsional, list didefinisikan secara rekursif: list adalah kosong, atau sebuah elemen (head) diikuti list lain (tail). Contoh: [3, 5, 7] = 3 diikuti [5, 7] = 3 diikuti 5 diikuti [7] = … diikuti [].
String di Python adalah barisan karakter yang immutable, sehingga cocok untuk gaya fungsional: setiap operasi menghasilkan string baru.
Operasi
Contoh
Hasil
Head / tail
xs[0], xs[1:]
Elemen pertama, sisa list
Unpacking
kepala, *ekor = xs
Head dan tail sekaligus
Slicing
s[::-1]
Barisan terbalik
Kode karakter
ord("A"), chr(66)
65, 'B'
Gabung
"-".join(["a", "b"])
'a-b'
Pasangkan
zip("ab", [1, 2])
('a', 1), ('b', 2)
D. Langkah Praktikum
List elemen sederhana. Buat p10.py:
# p10.py
kata = "Informatika"
huruf = list(kata)
kepala, *ekor = huruf
print(huruf) # hasil: ['I', 'n', 'f', 'o', 'r', 'm', 'a', 't', 'i', 'k', 'a']
print(kepala, "".join(ekor)) # hasil: I nformatika
print(kata[0], kata[-1], kata[1:4], kata[::-1]) # hasil: I a nfo akitamrofnI
print(len(kata), "m" in kata, kata.count("a")) # hasil: 11 True 2
bools = [True, False, True, True]
print(all(bools), any(bools), sum(bools)) # hasil: False True 3
Fungsi murni untuk karakter.
VOKAL = frozenset("aiueoAIUEO")
def hitung_vokal(s: str) -> int:
return sum(1 for c in s if c in VOKAL)
def palindrom(s: str) -> bool:
bersih = [c.lower() for c in s if c.isalnum()]
return bersih == bersih[::-1]
def inisial(nama: str) -> str:
return "".join(k[0].upper() for k in nama.split())
print(hitung_vokal("Informatika")) # hasil: 5
print(palindrom("Kasur ini rusak")) # hasil: True
print(inisial("doni winarso")) # hasil: DW
Sandi Caesar dengan ord/chr.
def geser(c: str, k: int) -> str:
if c.isupper():
return chr((ord(c) - ord("A") + k) % 26 + ord("A"))
if c.islower():
return chr((ord(c) - ord("a") + k) % 26 + ord("a"))
return c # spasi, angka, tanda baca tidak diubah
def caesar(teks: str, k: int) -> str:
return "".join(geser(c, k) for c in teks)
sandi = caesar("Halo UMRI", 3)
print(sandi, "|", caesar(sandi, -3)) # hasil: Kdor XPUL | Halo UMRI
Frekuensi karakter.
from collections import Counter
def frekuensi(s: str) -> dict:
return {c: s.lower().count(c) for c in sorted(set(s.lower())) if c.isalpha()}
print(frekuensi("Teknik Informatika"))
print(Counter("teknik informatika").most_common(3)) # hasil: [('k', 3), ('i', 3), ('t', 2)]
zip dan enumerate.
nama = ("Andi", "Bunga", "Citra")
nilai = (82, 91, 55)
print(list(zip(nama, nilai))) # hasil: [('Andi', 82), ('Bunga', 91), ('Citra', 55)]
print(dict(zip(nama, nilai))["Bunga"]) # hasil: 91
print([f"{i}. {n}" for i, n in enumerate(nama, start=1)]) # hasil: ['1. Andi', '2. Bunga', '3. Citra']
Terapkan pada data mahasiswa.
from data_mhs import DATA_MHS
print(tuple(inisial(m["nama"]) for m in DATA_MHS))
# hasil: ('AS', 'BL', 'CD', 'DP', 'EP', 'FN', 'GR')
print(tuple(m["nama"] for m in DATA_MHS if hitung_vokal(m["nama"]) < 5))
# hasil: ('Citra Dewi', 'Eka Putri')
print(sorted(DATA_MHS, key=lambda m: len(m["nama"]))[0]["nama"]) # hasil: Eka Putri
E. Tugas
20
1. Tulis fungsi anagram(a, b) yang memeriksa apakah dua kata adalah anagram (abaikan huruf besar/kecil dan spasi). Uji dengan "Listen"/"Silent".
20
2. Tulis fungsi judul(s) yang mengubah setiap kata menjadi diawali huruf kapital tanpa memakai str.title() atau str.capitalize().
20
3. Tulis fungsi kompres(s) (run-length encoding): "aaabccdddd" → "a3b1c2d4". Petunjuk: boleh memakai itertools.groupby.
20
4. Buat username(m) untuk setiap mahasiswa di DATA_MHS: huruf kecil nama depan + 3 digit terakhir NIM, contoh andi001. Hasilkan tuple semua username.
20
5. Jelaskan mengapa sifat immutable pada string sejalan dengan prinsip pemrograman fungsional. Apa akibatnya jika string dapat diubah?
F. Indikator Ketercapaian
Indikator RPS: Mahasiswa diharapkan mampu menerapkan konsep pemrograman menggunakan elemen sederhana dan karakter berbasis list.
Indikator
Skor maks.
Menggunakan konsep head/tail dan operasi list dasar
30
Mengolah string sebagai list karakter dengan fungsi murni
50
Penjelasan konsep imutabilitas
20
G. Refleksi
Saya dapat memisahkan head dan tail sebuah list.
Saya dapat mengolah teks tanpa mengubah string asal.
Saya dapat menggunakan ord, chr, zip, dan enumerate.
Pertemuan berikutnya: List bilangan bulat dan himpunan (set).
LKM Praktikum PL · Pertemuan 11
List Integer & Himpunan (Set)
Sub-CPMK
SUB-CPMK 072.1 — Menerapkan pemrograman fungsional berbasis list
CPMK
CPMK 072
Bahan Kajian
Pemrograman list dengan integer dan set himpunan
Metode
Praktikum list berbasis integer dan set himpunan · 100 menit
Asesmen
Non-asesmen · 0% (materi diujikan pada UAS)
A. Tujuan Praktikum
Mahasiswa mampu mengolah list bilangan bulat dengan fungsi agregat dan comprehension.
Mahasiswa mampu menulis fungsi statistik sederhana secara fungsional.
Mahasiswa mampu menerapkan operasi himpunan: gabungan, irisan, selisih, selisih simetris, dan subset.
B. Alat dan Bahan
Python 3.10+, VS Code, modul itertools dan statistics
C. Dasar Teori
Himpunan adalah kumpulan elemen tanpa urutan dan tanpa duplikat. Python menyediakan set (bisa diubah) dan frozenset (immutable, cocok untuk gaya fungsional dan bisa menjadi kunci dictionary).
def rata(xs) -> float:
return sum(xs) / len(xs)
def varians(xs) -> float:
r = rata(xs)
return sum((x - r) ** 2 for x in xs) / len(xs)
def median(xs) -> float:
s, n = sorted(xs), len(xs)
return s[n // 2] if n % 2 else (s[n // 2 - 1] + s[n // 2]) / 2
print(rata(xs), round(varians(xs), 3), median(xs)) # hasil: 10.625 50.734 8.5
import statistics # bandingkan dengan pustaka bawaan
print(statistics.mean(xs), statistics.median(xs)) # hasil: 10.625 8.5
Bilangan prima dan faktor.
def prima(n: int) -> bool:
return n > 1 and all(n % d for d in range(2, int(n ** 0.5) + 1))
def faktor(n: int) -> tuple:
return tuple(d for d in range(1, n + 1) if n % d == 0)
print([n for n in range(2, 40) if prima(n)])
# hasil: [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37]
print(faktor(36)) # hasil: (1, 2, 3, 4, 6, 9, 12, 18, 36)
Operasi himpunan.
A = frozenset({1, 2, 3, 4, 5})
B = frozenset({4, 5, 6, 7})
print(sorted(A | B)) # hasil: [1, 2, 3, 4, 5, 6, 7]
print(sorted(A & B)) # hasil: [4, 5]
print(sorted(A - B)) # hasil: [1, 2, 3]
print(sorted(A ^ B)) # hasil: [1, 2, 3, 6, 7]
print({1, 2} <= A, A.isdisjoint({8, 9})) # hasil: True True
print({x * x for x in A if x % 2}) # comprehension himpunan: {1, 9, 25}
Studi kasus KRS. Mata kuliah yang diambil tiap mahasiswa:
def himpunan_kuasa(s) -> list:
xs = tuple(s)
if not xs:
return [frozenset()]
sisa = himpunan_kuasa(xs[1:])
return sisa + [h | {xs[0]} for h in sisa]
P = himpunan_kuasa({"a", "b", "c"})
print(len(P), sorted(sorted(h) for h in P))
# hasil: 8 [[], ['a'], ['a', 'b'], ['a', 'b', 'c'], ['a', 'c'], ['b'], ['b', 'c'], ['c']]
E. Tugas
20
1. Tulis fungsi murni normalisasi(xs) yang mengubah setiap nilai menjadi skala 0–1 dengan rumus (x − min) / (max − min). Uji pada nilai DATA_MHS.
20
2. Tulis fungsi modus(xs) yang mengembalikan himpunan nilai yang paling sering muncul (bisa lebih dari satu). Uji dengan (12, 7, 3, 18, 7, 25, 3, 10).
20
3. Dengan data KRS, tampilkan: (a) pasangan mahasiswa yang memiliki mata kuliah sama (beserta mata kuliahnya), (b) mata kuliah yang hanya diambil satu orang.
20
4. Tulis fungsi kartesius(A, B) dengan comprehension yang menghasilkan himpunan pasangan (a, b). Bandingkan hasilnya dengan itertools.product.
20
5. Mengapa frozenset dapat menjadi anggota himpunan lain atau kunci dictionary, sedangkan set tidak? Hubungkan dengan konsep imutabilitas.
F. Indikator Ketercapaian
Indikator RPS: Mahasiswa diharapkan mampu menerapkan konsep list berbasis integer dan himpunan.
Indikator
Skor maks.
Mengolah list integer dengan fungsi agregat dan comprehension
40
Menerapkan operasi himpunan pada studi kasus
40
Memahami frozenset dan imutabilitas
20
G. Refleksi
Saya dapat menulis fungsi statistik tanpa perulangan eksplisit.
Saya dapat menerapkan operasi ∪, ∩, −, dan ⊕ di Python.
Saya tahu kapan memakai set dan kapan memakai list.
Pertemuan berikutnya: Resume rekursi terhadap list: menulis ulang len, sum, map, filter, reduce, dan algoritma pengurutan secara rekursif.
LKM Praktikum PL · Pertemuan 12
Resume Rekursi terhadap List
Sub-CPMK
SUB-CPMK 072.1 — Menerapkan pemrograman fungsional berbasis list
CPMK
CPMK 072
Bahan Kajian
Pemrograman resume rekursi terhadap list
Metode
Praktikum pemrograman resume dan rekursi berbasis list · 100 menit
Asesmen
Non-asesmen · 0% (materi diujikan pada UAS)
A. Tujuan Praktikum
Mahasiswa mampu menerapkan pola rekursi head/tail untuk mengolah list.
Mahasiswa mampu menulis ulang fungsi bawaan (len, sum, max, map, filter, reduce) secara rekursif.
Mahasiswa mampu menerapkan algoritma pengurutan rekursif: quicksort dan merge sort.
B. Alat dan Bahan
Python 3.10+, VS Code, tipe.py dari Pertemuan 9
C. Dasar Teori
Hampir semua fungsi rekursif pada list mengikuti satu pola:
f([]) = nilai_dasar ; kasus dasar: list kosong
f([x] + sisa) = gabungkan(x, f(sisa)) ; kasus rekursif: head + hasil tail
Fungsi
Kasus dasar
Kasus rekursif
panjang
0
1 + panjang(sisa)
jumlah
0
x + jumlah(sisa)
peta (map)
[]
[f(x)] + peta(f, sisa)
saring (filter)
[]
[x] + saring(sisa) jika p(x), selain itu saring(sisa)
lipat (reduce)
awal
lipat(f, f(awal, x), sisa)
balik
[]
balik(sisa) + [x]
Karena setiap pemanggilan membuat potongan list baru (xs[1:]) dan Python membatasi kedalaman rekursi, versi rekursif ini dipakai untuk memahami pola. Untuk data besar, gunakan fungsi bawaan yang sudah dioptimasi.
D. Langkah Praktikum
Fungsi dasar rekursif. Buat p12.py:
# p12.py
def panjang(xs) -> int:
return 0 if not xs else 1 + panjang(xs[1:])
def jumlah(xs):
return 0 if not xs else xs[0] + jumlah(xs[1:])
def maksimum(xs):
if len(xs) == 1:
return xs[0]
m = maksimum(xs[1:])
return xs[0] if xs[0] > m else m
def balik(xs) -> list:
return [] if not xs else balik(xs[1:]) + [xs[0]]
def ada(x, xs) -> bool:
return bool(xs) and (xs[0] == x or ada(x, xs[1:]))
data = [4, 9, 2, 7, 5]
print(panjang(data), jumlah(data), maksimum(data), balik(data), ada(7, data))
# hasil: 5 27 9 [5, 7, 2, 9, 4] True
map, filter, reduce versi rekursif.
def peta(f, xs) -> list:
return [] if not xs else [f(xs[0])] + peta(f, xs[1:])
def saring(p, xs) -> list:
if not xs:
return []
sisa = saring(p, xs[1:])
return [xs[0]] + sisa if p(xs[0]) else sisa
def lipat(f, awal, xs):
return awal if not xs else lipat(f, f(awal, xs[0]), xs[1:])
print(peta(lambda x: x * 10, data)) # hasil: [40, 90, 20, 70, 50]
print(saring(lambda x: x > 4, data)) # hasil: [9, 7, 5]
print(lipat(lambda a, x: a + x, 0, data)) # hasil: 27
def quicksort(xs) -> list:
if not xs:
return []
pivot, ekor = xs[0], xs[1:]
return (quicksort([x for x in ekor if x < pivot])
+ [pivot]
+ quicksort([x for x in ekor if x >= pivot]))
def gabung(a, b) -> list:
if not a:
return b
if not b:
return a
return [a[0]] + gabung(a[1:], b) if a[0] <= b[0] else [b[0]] + gabung(a, b[1:])
def merge_sort(xs) -> list:
if len(xs) <= 1:
return list(xs)
t = len(xs) // 2
return gabung(merge_sort(xs[:t]), merge_sort(xs[t:]))
acak = [38, 27, 43, 3, 9, 82, 10]
print(quicksort(acak)) # hasil: [3, 9, 10, 27, 38, 43, 82]
print(merge_sort(acak)) # hasil: [3, 9, 10, 27, 38, 43, 82]
print(acak) # list asli tidak berubah
Rekursi pada data mahasiswa.
from tipe import MHS
def total_sks(ms) -> int:
return 0 if not ms else ms[0].sks + total_sks(ms[1:])
def terbaik(ms):
if len(ms) == 1:
return ms[0]
b = terbaik(ms[1:])
return ms[0] if ms[0].nilai >= b.nilai else b
print(total_sks(MHS), terbaik(MHS).nama) # hasil: 144 Bunga Lestari
E. Tugas
20
1. Tulis fungsi rekursif hitung(x, xs) (banyaknya x di xs) dan hapus_semua(x, xs) (list tanpa x). Uji dengan [1, 3, 1, 2, 1].
20
2. Tulis fungsi rekursif zip_rek(xs, ys) dan ambil(n, xs) (n elemen pertama) tanpa memakai zip atau slicing xs[:n].
20
3. Tuliskan jejak (trace) pemanggilan quicksort([5, 2, 8, 1, 9]) sampai hasil akhir.
25
4. Ubah merge_sort menjadi merge_sort_by(kunci, xs) yang menerima fungsi kunci, lalu urutkan MHS berdasarkan nilai dan berdasarkan panjang nama.
15
5. Buat resume (ringkasan) pola rekursi list dalam bentuk tabel milik Anda sendiri: nama fungsi, kasus dasar, kasus rekursif, dan kompleksitas waktu kira-kira.
F. Indikator Ketercapaian
Indikator RPS: Mahasiswa diharapkan mampu menerapkan konsep rekursi dan resume terhadap list.
Indikator
Skor maks.
Menerapkan pola rekursi head/tail dengan benar
40
Menerapkan algoritma pengurutan rekursif
40
Menyusun resume pola rekursi list
20
G. Refleksi
Saya dapat menulis fungsi rekursif untuk list dengan pola head/tail.
Saya memahami cara kerja quicksort dan merge sort secara rekursif.
Saya tahu keterbatasan rekursi di Python.
Pertemuan berikutnya: Membangun struktur data list sendiri (linked list immutable) beserta stack dan queue fungsional.
LKM Praktikum PL · Pertemuan 13
Pemrograman Fungsional Berbasis Struktur Data List
Sub-CPMK
SUB-CPMK 072.1 — Menerapkan pemrograman fungsional berbasis list
CPMK
CPMK 072
Bahan Kajian
Pemrograman fungsional berbasis struktur data list
Metode
Praktikum konsep list dalam struktur data · 100 menit
Asesmen
Non-asesmen · 0% (materi diujikan pada UAS)
A. Tujuan Praktikum
Mahasiswa mampu mengimplementasikan linked list immutable (cons list) dengan type bentukan.
Mahasiswa mampu menulis operasi list (panjang, map, filter, fold, gabung, balik) pada struktur buatan sendiri.
Mahasiswa mampu memahami structural sharing serta mengimplementasikan stack dan queue fungsional.
B. Alat dan Bahan
Python 3.10+, VS Code, tipe.py
C. Dasar Teori
List di bahasa fungsional (Haskell, Lisp, Scala) adalah cons list: rantai simpul, masing-masing berisi kepala dan penunjuk ke ekor. List kosong ditandai nilai khusus (di sini None).
Karena simpul tidak pernah diubah, menambah elemen di depan (cons) cukup membuat satu simpul baru yang menunjuk ke list lama. List lama tetap utuh dan dipakai bersama. Teknik ini disebut structural sharing dan menjadi dasar persistent data structure.
Operasi
Kompleksitas
cons (tambah di depan), kepala, ekor
O(1)
panjang, ke-n, tambah di akhir, gabung
O(n)
D. Langkah Praktikum
Definisikan cons list. Buat daftar.py:
# daftar.py
from __future__ import annotations
from dataclasses import dataclass
from typing import Any, Callable, Optional
@dataclass(frozen=True)
class Cons:
kepala: Any
ekor: Optional[Cons]
Daftar = Optional[Cons] # Daftar = None (kosong) atau Cons
KOSONG: Daftar = None
def cons(x, xs: Daftar) -> Daftar:
return Cons(x, xs)
def dari_list(py) -> Daftar:
return KOSONG if not py else cons(py[0], dari_list(py[1:]))
def ke_list(xs: Daftar) -> list:
return [] if xs is None else [xs.kepala] + ke_list(xs.ekor)
def tampil(xs: Daftar) -> str:
return "∅" if xs is None else f"{xs.kepala} → {tampil(xs.ekor)}"
Operasi dasar. Tambahkan ke daftar.py:
def panjang(xs: Daftar) -> int:
return 0 if xs is None else 1 + panjang(xs.ekor)
def ke_n(xs: Daftar, n: int):
if xs is None:
raise IndexError("indeks di luar jangkauan")
return xs.kepala if n == 0 else ke_n(xs.ekor, n - 1)
def peta(f: Callable, xs: Daftar) -> Daftar:
return None if xs is None else Cons(f(xs.kepala), peta(f, xs.ekor))
def saring(p: Callable, xs: Daftar) -> Daftar:
if xs is None:
return None
sisa = saring(p, xs.ekor)
return Cons(xs.kepala, sisa) if p(xs.kepala) else sisa
def lipat_kanan(f: Callable, awal, xs: Daftar):
"""f(x1, f(x2, ... f(xn, awal)))"""
return awal if xs is None else f(xs.kepala, lipat_kanan(f, awal, xs.ekor))
def gabung(xs: Daftar, ys: Daftar) -> Daftar:
return ys if xs is None else Cons(xs.kepala, gabung(xs.ekor, ys))
def balik(xs: Daftar, acc: Daftar = None) -> Daftar:
return acc if xs is None else balik(xs.ekor, Cons(xs.kepala, acc))
5. Gambarkan (dengan teks/ASCII) kondisi memori setelah ys = cons(0, xs) dan zs = cons(9, xs). Berapa simpul baru yang dibuat? Jelaskan keuntungan structural sharing.
F. Indikator Ketercapaian
Indikator RPS: Mahasiswa mampu menerapkan konsep pemrograman fungsional berbasis struktur data list.
Indikator
Skor maks.
Mengimplementasikan cons list dan operasinya
40
Mengimplementasikan stack/queue fungsional
30
Memahami structural sharing dan persistensi data
30
G. Refleksi
Saya dapat membangun list sendiri dari simpul Cons.
Saya memahami mengapa cons list tidak perlu disalin saat ditambah elemen.
Saya menyimpan daftar.py untuk persiapan UAS.
Pertemuan berikutnya: Struktur data tree: pohon biner dan pohon n-ary.
LKM Praktikum PL · Pertemuan 14
Pemrograman Fungsional Berbasis Tree: Pohon N-ary & Biner
Sub-CPMK
SUB-CPMK 072.2 — Menerapkan pemrograman fungsional berbasis tree (pohon)
CPMK
CPMK 072
Bahan Kajian
Pemrograman fungsional berbasis tree untuk pohon n-ary dan biner
Metode
Praktikum pohon biner dan n-ary · 100 menit
Asesmen
Non-asesmen · 0% (materi diujikan pada UAS)
A. Tujuan Praktikum
Mahasiswa mampu mendefinisikan pohon biner dan pohon n-ary sebagai type bentukan rekursif.
Mahasiswa mampu menulis fungsi rekursif pada pohon: ukuran, tinggi, daun, dan penelusuran (preorder, inorder, postorder).
Mahasiswa mampu mentransformasi pohon secara immutable (map, mirror) dan mengevaluasi pohon ekspresi.
B. Alat dan Bahan
Python 3.10+, VS Code
C. Dasar Teori
Pohon (tree) adalah struktur hierarkis yang didefinisikan secara rekursif: pohon adalah kosong, atau simpul berisi nilai dan sejumlah subpohon. Karena definisinya rekursif, fungsi pada pohon hampir selalu rekursif.
Istilah
Arti
Akar (root)
Simpul paling atas
Daun (leaf)
Simpul tanpa anak
Tinggi (height)
Jumlah simpul pada jalur terpanjang dari akar ke daun (pohon kosong = 0)
Pohon biner
Setiap simpul punya paling banyak 2 anak: kiri dan kanan
Pohon n-ary
Setiap simpul boleh punya banyak anak (contoh: struktur folder, organisasi)
Pohon ekspresi aritmetika. Pohon biner juga dapat merepresentasikan ekspresi (3 + 4) * (5 - 2):
import operator as op
OPS = {"+": op.add, "-": op.sub, "*": op.mul, "/": op.truediv}
def evaluasi(t: Simpul):
match t:
case Simpul(nilai=n, kiri=None, kanan=None):
return n # daun = bilangan
case Simpul(nilai=o, kiri=a, kanan=b) if o in OPS:
return OPS[o](evaluasi(a), evaluasi(b)) # simpul dalam = operator
case _:
raise ValueError(f"Ekspresi tidak valid: {t}")
def ke_infix(t: Simpul) -> str:
return (str(t.nilai) if t.kiri is None and t.kanan is None
else f"({ke_infix(t.kiri)} {t.nilai} {ke_infix(t.kanan)})")
E = Simpul("*", Simpul("+", daun(3), daun(4)), Simpul("-", daun(5), daun(2)))
print(ke_infix(E), "=", evaluasi(E)) # hasil: ((3 + 4) * (5 - 2)) = 21
Pohon n-ary. Tambahkan ke pohon.py:
@dataclass(frozen=True)
class SimpulN:
nilai: Any
anak: tuple = () # tuple berisi SimpulN
def ukuran_n(t: SimpulN) -> int:
return 1 + sum(map(ukuran_n, t.anak))
def tinggi_n(t: SimpulN) -> int:
return 1 + max(map(tinggi_n, t.anak), default=0)
def daun_n(t: SimpulN) -> list:
return [t.nilai] if not t.anak else [d for a in t.anak for d in daun_n(a)]
def cetak_n(t: SimpulN, level: int = 0) -> str:
return " " * level + f"- {t.nilai}\n" + "".join(cetak_n(a, level + 1) for a in t.anak)
def jalur(t: SimpulN, target) -> Optional[list]:
if t.nilai == target:
return [t.nilai]
for a in t.anak: # berhenti pada jalur pertama yang ditemukan
j = jalur(a, target)
if j:
return [t.nilai] + j
return None
Struktur organisasi fakultas sebagai pohon n-ary. Tambahkan ke p14.py:
1. Gambarkan pohon biner dari pemanggilan berikut, lalu tuliskan hasil preorder, inorder, postorder, ukuran, dan tingginya: Simpul("A", Simpul("B", daun("D"), daun("E")), Simpul("C", None, daun("F")))
20
2. Tulis fungsi jumlah_nilai(t), maks_pohon(t), dan level(t, k) (daftar nilai pada kedalaman k, akar = kedalaman 0) untuk pohon biner. Uji dengan pohon T.
20
3. Buat pohon ekspresi untuk (10 - 4) / (1 + 2) * 7, tampilkan bentuk infix-nya, lalu evaluasi.
20
4. Modelkan struktur folder proyek sebagai pohon n-ary dengan nilai berupa tuple (nama, ukuran_kb). Tulis fungsi total_ukuran(t) dan cari_file(t, nama).
20
5. Jelaskan mengapa pohon biner sebenarnya adalah kasus khusus pohon n-ary, dan bagaimana fungsi cermin tetap bersifat murni.
F. Indikator Ketercapaian
Indikator RPS: Mahasiswa diharapkan mampu menerapkan konsep pohon biner dan n-ary pada struktur data tree.
Indikator
Skor maks.
Mendefinisikan pohon biner dan n-ary dengan type bentukan
25
Menulis fungsi rekursif dan penelusuran pohon
50
Menerapkan pohon pada studi kasus (ekspresi, organisasi, folder)
25
G. Refleksi
Saya dapat menelusuri pohon secara preorder, inorder, dan postorder.
Saya dapat menulis fungsi rekursif pada pohon biner dan n-ary.
Saya menyimpan pohon.py untuk Pertemuan 15 dan UAS.
Pertemuan berikutnya: Binary search, binary search tree, dan pohon seimbang (AVL).
LKM Praktikum PL · Pertemuan 15
Pemrograman Fungsional Berbasis Binary Search & Balanced Search
Sub-CPMK
SUB-CPMK 072.2 — Menerapkan pemrograman fungsional berbasis tree (pohon)
CPMK
CPMK 072
Bahan Kajian
Pemrograman fungsional berbasis binary search dan balance search
Metode
Praktikum binary search dan balance search · 100 menit
Asesmen
Non-asesmen · 0% (materi diujikan pada UAS)
A. Tujuan Praktikum
Mahasiswa mampu menulis binary search rekursif pada barisan terurut.
Mahasiswa mampu mengimplementasikan Binary Search Tree (BST) yang persisten (immutable).
Mahasiswa mampu menjelaskan masalah pohon tidak seimbang dan mengimplementasikan pohon seimbang (AVL) dengan rotasi.
B. Alat dan Bahan
Python 3.10+, VS Code, pohon.py dari Pertemuan 14, tipe.py
C. Dasar Teori
Binary search mencari pada barisan terurut dengan membandingkan elemen tengah, lalu membuang separuh barisan setiap langkah. Kompleksitasnya O(log n): 1.000.000 data cukup ±20 langkah.
Binary Search Tree (BST) adalah pohon biner dengan aturan: semua nilai di subpohon kiri < akar < semua nilai di subpohon kanan. Penelusuran inorder BST selalu menghasilkan data terurut. Pencarian, penyisipan, dan penghapusan memerlukan waktu sebanding dengan tinggi pohon.
Masalahnya, jika data disisipkan dalam urutan terurut (1, 2, 3, …), BST berubah menjadi "rantai" setinggi n sehingga pencarian menjadi O(n). Pohon seimbang (balanced tree) menjaga tinggi tetap O(log n). Salah satunya pohon AVL: selisih tinggi subpohon kiri dan kanan (balance factor) setiap simpul paling besar 1. Jika dilanggar, pohon diperbaiki dengan rotasi.
Sisip 1, 2, 3 ke BST biasa: Setelah rotasi kiri (AVL):
1 2
\ / \
2 1 3
\
3 tinggi 3 → tinggi 2
Kasus
Kondisi
Perbaikan
Kiri-Kiri
faktor > 1, anak kiri condong kiri
Rotasi kanan
Kiri-Kanan
faktor > 1, anak kiri condong kanan
Rotasi kiri pada anak kiri, lalu rotasi kanan
Kanan-Kanan
faktor < −1, anak kanan condong kanan
Rotasi kiri
Kanan-Kiri
faktor < −1, anak kanan condong kiri
Rotasi kanan pada anak kanan, lalu rotasi kiri
D. Langkah Praktikum
Binary search rekursif. Buat p15.py:
# p15.py
from functools import reduce
from dataclasses import dataclass, replace
from typing import Any, Optional
from pohon import Simpul, Pohon, inorder, tinggi
def cari_biner(xs, target, lo=0, hi=None, langkah=1):
"""Mengembalikan (indeks atau -1, banyak langkah)."""
hi = len(xs) - 1 if hi is None else hi
if lo > hi:
return -1, langkah
t = (lo + hi) // 2
if xs[t] == target:
return t, langkah
return (cari_biner(xs, target, t + 1, hi, langkah + 1) if xs[t] < target
else cari_biner(xs, target, lo, t - 1, langkah + 1))
data = tuple(range(0, 2_000_000, 2)) # 1 juta bilangan genap terurut
print(cari_biner(data, 1_234_566)) # hasil: (617283, langkah ≤ 20)
print(cari_biner(data, 7)) # hasil: (-1, ...) karena 7 ganjil
BST persisten.
def sisip(t: Pohon, x) -> Pohon:
if t is None:
return Simpul(x)
if x < t.nilai:
return replace(t, kiri=sisip(t.kiri, x)) # salin jalur, sisanya dipakai bersama
if x > t.nilai:
return replace(t, kanan=sisip(t.kanan, x))
return t # duplikat diabaikan
def cari(t: Pohon, x) -> bool:
if t is None:
return False
if x == t.nilai:
return True
return cari(t.kiri, x) if x < t.nilai else cari(t.kanan, x)
def minimum(t: Simpul):
return t.nilai if t.kiri is None else minimum(t.kiri)
def hapus(t: Pohon, x) -> Pohon:
if t is None:
return None
if x < t.nilai:
return replace(t, kiri=hapus(t.kiri, x))
if x > t.nilai:
return replace(t, kanan=hapus(t.kanan, x))
if t.kiri is None:
return t.kanan
if t.kanan is None:
return t.kiri
pengganti = minimum(t.kanan) # successor inorder
return Simpul(pengganti, t.kiri, hapus(t.kanan, pengganti))
def bst_dari(xs) -> Pohon:
return reduce(sisip, xs, None)
B = bst_dari([8, 3, 10, 1, 6, 14, 4, 7, 13])
print(inorder(B), tinggi(B)) # hasil: [1, 3, 4, 6, 7, 8, 10, 13, 14] 4
print(cari(B, 6), cari(B, 5)) # hasil: True False
B2 = hapus(B, 3)
print(inorder(B2), inorder(B)) # B2 tanpa 3, B tetap utuh
Amati masalah ketidakseimbangan.
def seimbang_dari_terurut(xs) -> Pohon:
if not xs:
return None
t = len(xs) // 2
return Simpul(xs[t], seimbang_dari_terurut(xs[:t]), seimbang_dari_terurut(xs[t + 1:]))
urut = list(range(1, 101))
print(tinggi(bst_dari(urut))) # hasil: 100 -> seperti linked list
print(tinggi(seimbang_dari_terurut(urut))) # hasil: 7 -> ceil(log2(101))
Pohon AVL fungsional.
@dataclass(frozen=True)
class AVL:
nilai: Any
kiri: Optional["AVL"] = None
kanan: Optional["AVL"] = None
tinggi: int = 1
def h(t) -> int:
return 0 if t is None else t.tinggi
def buat(nilai, kiri, kanan) -> AVL:
return AVL(nilai, kiri, kanan, 1 + max(h(kiri), h(kanan)))
def faktor(t: AVL) -> int:
return h(t.kiri) - h(t.kanan)
def putar_kanan(y: AVL) -> AVL:
x = y.kiri
return buat(x.nilai, x.kiri, buat(y.nilai, x.kanan, y.kanan))
def putar_kiri(x: AVL) -> AVL:
y = x.kanan
return buat(y.nilai, buat(x.nilai, x.kiri, y.kiri), y.kanan)
def seimbangkan(t: AVL) -> AVL:
f = faktor(t)
if f > 1: # berat kiri
kiri = t.kiri if faktor(t.kiri) >= 0 else putar_kiri(t.kiri)
return putar_kanan(buat(t.nilai, kiri, t.kanan))
if f < -1: # berat kanan
kanan = t.kanan if faktor(t.kanan) <= 0 else putar_kanan(t.kanan)
return putar_kiri(buat(t.nilai, t.kiri, kanan))
return t
def sisip_avl(t: Optional[AVL], x) -> AVL:
if t is None:
return AVL(x)
if x < t.nilai:
return seimbangkan(buat(t.nilai, sisip_avl(t.kiri, x), t.kanan))
if x > t.nilai:
return seimbangkan(buat(t.nilai, t.kiri, sisip_avl(t.kanan, x)))
return t
def inorder_avl(t) -> list:
return [] if t is None else inorder_avl(t.kiri) + [t.nilai] + inorder_avl(t.kanan)
for n in (7, 100, 500):
bst = bst_dari(range(1, n + 1))
avl = reduce(sisip_avl, range(1, n + 1), None)
print(n, "data | tinggi BST:", tinggi(bst), "| tinggi AVL:", avl.tinggi)
# hasil:
# 7 data | tinggi BST: 7 | tinggi AVL: 3
# 100 data | tinggi BST: 100 | tinggi AVL: 7
# 500 data | tinggi BST: 500 | tinggi AVL: 9
BST data mahasiswa berdasarkan NIM. Tuple dibandingkan elemen demi elemen, sehingga pasangan (nim, nama) terurut berdasarkan NIM:
from tipe import MHS
def cari_nim(t: Optional[AVL], nim: str):
if t is None:
return None
k, nama = t.nilai
return nama if nim == k else cari_nim(t.kiri if nim < k else t.kanan, nim)
indeks = reduce(sisip_avl, ((m.nim, m.nama) for m in MHS), None)
print(indeks.tinggi, cari_nim(indeks, "2304005"), cari_nim(indeks, "2309999"))
# hasil: 3 Eka Putri None
E. Tugas
20
1. Tuliskan jejak cari_biner((3, 8, 15, 21, 30, 42, 57), 42): nilai lo, hi, dan tengah pada setiap langkah.
20
2. Sisipkan berturut-turut 50, 30, 70, 20, 40, 60, 80, 35 ke BST. Gambarkan pohonnya, lalu gambarkan hasil hapus(B, 30).
20
3. Sisipkan 10, 20, 30, 25, 28 ke pohon AVL. Gambarkan pohon setelah setiap penyisipan dan sebutkan rotasi yang terjadi.
20
4. Tulis fungsi rentang(t, a, b) yang mengembalikan semua nilai BST dalam rentang [a, b] tanpa menelusuri subpohon yang pasti di luar rentang.
20
5. Bandingkan pencarian pada list biasa, BST tidak seimbang, dan AVL dari sisi kompleksitas waktu. Kapan pohon seimbang benar-benar diperlukan?
F. Indikator Ketercapaian
Indikator RPS: Mahasiswa diharapkan mampu menerapkan konsep binary search dan balance search.
Saya dapat menulis binary search dan BST secara rekursif.
Saya memahami mengapa BST perlu diseimbangkan.
Saya dapat menelusuri rotasi pada pohon AVL.
Pertemuan berikutnya:UAS pemrograman fungsional list dan tree. Siapkan daftar.py dan pohon.py.
LKM Praktikum PL · Pertemuan 16
UAS: Pemrograman Fungsional List & Tree
Sub-CPMK
SUB-CPMK 072.2 — Menerapkan pemrograman fungsional berbasis tree (pohon)
CPMK
CPMK 072
Bahan Kajian
Pemrograman fungsional berbasis struktur data list dan tree
Metode
Ujian pemrograman fungsional list dan tree · 100 menit
Asesmen
UAS · 100% dari CPMK 072
A. Tujuan
Mengukur kemampuan mahasiswa menerapkan list (elemen sederhana, integer, himpunan, dan struktur data list) secara fungsional.
Mengukur kemampuan mahasiswa menerapkan tree (pohon biner, n-ary, BST, dan pohon seimbang) secara fungsional.
B. Ketentuan Ujian
Waktu 100 menit, dikerjakan individu di komputer lab.
Boleh membuka modul ini, file daftar.py, pohon.py, dan dokumentasi Python. Tidak boleh berkomunikasi dengan orang lain atau memakai AI generatif.
Semua fungsi harus murni dan tanpa perulangan for/while sebagai pernyataan, kecuali disebut lain. Comprehension, map, filter, reduce, dan rekursi diperbolehkan.
Tempel kode lengkap dan keluaran program pada setiap kotak jawaban, lalu unduh LKM ini dalam format Word/PDF dan kumpulkan bersama file uas_NIM.py.
1. List integer & rekursi. Tanpa memakai sum, len, atau max bawaan, tulis fungsi rekursif jumlah, banyak, dan terbesar untuk NILAI, lalu hitung rata-rata. Tampilkan juga nilai yang lulus (≥ 60) secara terurut menggunakan merge_sort rekursif.
15
2. Himpunan. Dari PEMINAT, tentukan: (a) mahasiswa yang berminat pada ketiga bidang, (b) mahasiswa yang hanya berminat pada satu bidang, (c) seluruh mahasiswa, (d) pasangan bidang yang tidak memiliki peminat bersama.
20
3. Struktur data list. Dengan cons list dari daftar.py, tulis sisip_terurut(xs, x) dan insertion_sort(xs), lalu urutkan NILAI. Tulis juga hapus_duplikat(xs) untuk cons list.
25
4. Binary Search Tree. Bangun BST dari NIM_MASUK sesuai urutan. (a) Gambarkan pohonnya. (b) Tampilkan hasil inorder dan tinggi pohon. (c) Cari NIM "2304070" dan "2304055". (d) Bangun pohon AVL dari NIM yang sama dalam urutan terurut, lalu bandingkan tingginya dengan BST biasa dari data terurut.
15
5. Pohon n-ary. Modelkan menu aplikasi berikut sebagai pohon n-ary: Beranda → (Profil → (Ubah Profil, Ganti Password), Akademik → (KRS, KHS, Transkrip), Keluar). Tulis fungsi untuk menghitung jumlah menu, kedalaman maksimum, daftar menu yang tidak memiliki submenu, dan jalur menuju "KHS".
10
6. Analisis. Jelaskan pilihan struktur data (tuple/list, set, cons list, BST/AVL) yang tepat untuk: (a) daftar riwayat transaksi yang terus bertambah, (b) pemeriksaan keanggotaan cepat, (c) pencarian data terurut berdasarkan kunci. Sertakan alasannya.
E. Indikator & Rubrik Penilaian UAS
Indikator RPS: Mahasiswa mampu menerapkan konsep list dan tree dalam pemrograman fungsional.
Kriteria
Sangat baik (skor penuh)
Skor maks.
List integer dan rekursi
Fungsi rekursif benar, memiliki kasus dasar, hasil tepat
15
Himpunan
Operasi himpunan tepat dan ringkas
15
Struktur data list
Operasi cons list benar dan immutable
20
BST dan pohon seimbang
Gambar pohon, penelusuran, pencarian, dan perbandingan tinggi benar
25
Pohon n-ary
Pemodelan tepat dan semua fungsi benar
15
Analisis
Pemilihan struktur data disertai alasan kompleksitas
10
F. Refleksi Akhir Semester
Saya dapat membedakan paradigma fungsional, prosedural, dan OOP.
Saya dapat menulis program dengan notasi dan ekspresi fungsional (kondisional, rekursif, lambda).
Saya dapat membangun dan mengolah struktur data list secara fungsional.
Saya dapat membangun dan mengolah struktur data tree secara fungsional.
Penutup: Selamat, Anda telah menyelesaikan seluruh rangkaian Praktikum Pemrograman Lanjutan. Simpan folder praktikum_pl beserta seluruh LKM sebagai portofolio. Untuk pendalaman, baca Learn You a Haskell for Great Good! (Lipovaca, 2011) dan Programming in Haskell (Hutton, 2016).