Pemrograman fungsional

Apa yang akan kita Tutupi?

  • Perbedaan antara gaya pemrograman fungsional dan lebih tradisional
  • Fungsi dan teknik Python FP
  • Fungsi lambda
  • Evaluasi Short Circuit Boolean
  • Program sebagai ekspresi
Dalam topik ini kita melihat bagaimana Python dapat mendukung gaya pemrograman lagi: Pemrograman Fungsional (FP). Seperti Rekursi ini adalah topik yang benar-benar canggih yang mungkin Anda ingin mengabaikan untuk saat ini. teknik fungsional memiliki beberapa kegunaan dalam sehari untuk pemrograman hari dan pendukung FP percaya untuk menjadi cara yang fundamental yang lebih baik untuk mengembangkan perangkat lunak.

Apa Pemrograman Fungsional?

Pemrograman fungsional tidak harus bingung dengan imperatif (atau prosedural) pemrograman. Baik itu seperti pemrograman berorientasi objek. Ini adalah sesuatu yang berbeda. Tidak secara radikal sehingga, karena konsep yang kita akan mengeksplorasi konsep-konsep pemrograman akrab, hanya menyatakan dengan cara yang berbeda. Filosofi di balik bagaimana konsep ini diterapkan untuk memecahkan masalah juga sedikit berbeda.

Pemrograman fungsional adalah semua tentang ekspresi . Bahkan cara lain untuk menggambarkan FP mungkin untuk istilah itu pemrograman berorientasi ekspresi sejak di FP segala mengurangi ke ekspresi. Anda harus ingat bahwa ekspresi adalah kumpulan operasi dan variabel yang menghasilkan nilai tunggal. Jadi x == 5 adalah ekspresi boolean. 5 + (7-Y) adalah ekspresi aritmatika. Dan "Halo dunia" .uppercase () adalah ekspresi string. Yang terakhir ini juga panggilan fungsi (Atau lebih ketat panggilan metode) pada objek string "Hello world" dan, seperti yang akan kita lihat, fungsi yang sangat penting dalam FP (Anda mungkin sudah menduga bahwa dari nama!).

Fungsi yang digunakan sebagai obyek dalam FP. Yang mereka sering melewati sekitar dalam sebuah program dalam banyak cara yang sama seperti variabel lainnya. Kita telah melihat contoh-contoh ini dalam program GUI kami di mana kami ditugaskan nama dari fungsi dari perintah atribut Tombol kontrol. Kami diperlakukan fungsi event handler sebagai objek dan ditugaskan referensi ke fungsi untuk Button. Ide ini lewat fungsi sekitar program kami adalah kunci untuk FP.

Program fungsional juga cenderung berat Membuat daftar berorientasi.

Akhirnya FP mencoba untuk fokus pada apa daripada bagaimana pemecahan masalah. Artinya, program fungsional harus menjelaskan masalah yang akan dipecahkan daripada fokus pada mekanisme solusi. Ada beberapa bahasa pemrograman yang bertujuan untuk bekerja dengan cara ini, salah satu yang paling banyak digunakan adalah Haskell dan Haskell situs web ( www.haskell.org ) memiliki banyak makalah yang menjelaskan filosofi FP serta bahasa Haskell. (Pendapat pribadi saya adalah bahwa tujuan ini, namun patut dipuji, agak dilebih-lebihkan oleh para pendukung FP ini.)

Sebuah program fungsional murni disusun dengan mendefinisikan ekspresi yang menangkap maksud dari program. Setiap istilah dari ekspresi adalah pada gilirannya pernyataan dari karakteristik masalah (mungkin dikemas sebagai ungkapan lain) dan evaluasi masing-masing istilah ini akhirnya menghasilkan solusi.

Nah, itulah teori. Apakah itu bekerja? Ya, kadang-kadang bekerja dengan sangat baik. Untuk beberapa jenis masalah itu adalah teknik alami dan kuat. Sayangnya bagi banyak masalah lain membutuhkan gaya berpikir yang cukup abstrak, sangat dipengaruhi oleh prinsip-prinsip matematika. Kode yang dihasilkan sering jauh dari dibaca untuk programmer awam. Kode yang dihasilkan juga sangat sering jauh lebih pendek dari kode penting setara dan lebih dapat diandalkan.

Ini adalah kualitas-kualitas yang terakhir dari keringkasan dan kehandalan yang telah menarik banyak programmer penting atau berorientasi objek konvensional untuk menyelidiki FP. Bahkan jika tidak memeluk seluruh hati ada beberapa alat yang kuat yang dapat digunakan oleh semua.

FP dan Keandalan

Kehandalan Program Fungsional sebagian datang dari hubungan yang sangat erat antara FP membangun dan bahasa spesifikasi formal seperti Z atau VDM. Jika masalah ditentukan dalam bahasa formal itu adalah langkah yang cukup mudah untuk menerjemahkan spesifikasi dalam bahasa FP seperti Haskell. Tentu saja jika spesifikasi asli salah maka program yang dihasilkan akan hanya akurat mencerminkan kesalahan!

Prinsip ini dikenal dalam ilmu komputer sebagai "Sampah In, Garbage Out" . Kesulitan yang melekat mengungkapkan persyaratan sistem secara singkat dan jelas tetap menjadi salah satu tantangan terbesar dari rekayasa perangkat lunak.

 

Bagaimana Python melakukannya?

Python menyediakan beberapa fungsi yang memungkinkan pendekatan fungsional untuk pemrograman. Fungsi-fungsi ini adalah semua kemudahan fitur dalam bahwa mereka dapat ditulis dengan Python cukup mudah. Apa yang lebih penting namun adalah niat tersirat dalam ketentuan mereka, yaitu untuk memungkinkan programmer Python untuk bekerja dengan cara FP jika dia / dia ingin.

Kita akan melihat beberapa fungsi yang disediakan dan melihat bagaimana mereka beroperasi pada beberapa struktur data sampel yang kita definisikan sebagai:

spam = ['pork','ham','spices']
numbers = [1,2,3,4,5]

def eggs(item):
    return item

Peta (aFunction, aSequence)

Fungsi ini berlaku fungsi Python, aFunction untuk setiap anggota aSequence . Ekspresi:

L = map(eggs, spam)
print L

Hasil dalam daftar baru (dalam hal ini identik dengan spam) yang akan dikembalikan di L.

Kita bisa melakukan hal yang sama dengan menulis:

for i in spam:
   L.append(i)
print L

Perhatikan bagaimanapun, bahwa fungsi peta memungkinkan kita untuk menghapus kebutuhan untuk blok bersarang kode. Dari satu sudut pandang yang mengurangi kompleksitas program dengan satu tingkat. Kita akan melihat bahwa sebagai tema yang berulang dari FP, bahwa penggunaan fungsi FP mengurangi kompleksitas relatif kode dengan menghilangkan blok.

Menyaring (aFunction, aSequence)

 

Seperti namanya saringan ekstrak setiap elemen dalam urutan yang fungsi mengembalikan Benar . Pertimbangkan daftar nomor. Jika kita ingin membuat daftar baru hanya angka ganjil kita bisa memproduksinya seperti:

def isOdd(n): return (n%2 != 0) # use mod operator
L = filter(isOdd, numbers)
print L

 

Atau kita bisa menulis:

def isOdd(n): return (n%2 != 0)
for i in numbers:
   if isOdd(i):
      L.append(i)
print L

 

Sekali lagi perhatikan bahwa kode konvensional membutuhkan dua tingkat lekukan untuk mencapai hasil yang sama. Sekali lagi lekukan meningkat merupakan indikasi dari peningkatan kompleksitas kode.

Mengurangi (aFunction, aSequence)

The mengurangi fungsi sedikit kurang jelas dalam niat. Fungsi ini mengurangi daftar ke nilai tunggal dengan menggabungkan unsur-unsur melalui fungsi yang disediakan. Misalnya kita bisa menjumlahkan nilai dari daftar dan kembali total seperti ini:

 

def add(i,j): return i+j
print reduce(add, numbers)

Seperti sebelumnya kita bisa melakukan ini lebih konvensional seperti ini:

 

res = 0
for i in range(len(numbers)): # use indexing
   res = res + numbers[i]
print res

 

Sementara yang menghasilkan hasil yang sama dalam kasus ini, tidak selalu begitu mudah. Apa mengurangi sebenarnya dilakukan adalah memanggil fungsi yang disediakan melewati dua anggota pertama dari urutan dan menggantikan mereka dengan hasilnya. Dengan kata lain representasi yang lebih akurat mengurangi adalah seperti ini:

 

def reduce(numbers):
  L = numbers[:] # make a copy of original
  while len(L) >= 2:
     i,j = L[0],L[1] # use tuple assignment
     L = [i+j] + L[2:]
  return L[0]

Sekali lagi kita melihat teknik FP mengurangi kompleksitas kode dengan menghindari kebutuhan untuk blok indentasi kode.

lambda

Salah satu fitur Anda mungkin telah memperhatikan dalam contoh sejauh ini adalah bahwa fungsi dilewatkan ke fungsi FP cenderung sangat singkat, sering hanya satu baris kode. Untuk menyimpan upaya mendefinisikan banyak fungsi yang sangat kecil Python memberikan bantuan lain untuk FP - lambda . Nama lambda berasal dari sebuah cabang matematika yang disebut lambda kalkulus yang menggunakan huruf Yunani lambda untuk mewakili konsep serupa.

Lambda adalah istilah yang digunakan untuk merujuk ke sebuah fungsi anonim , yaitu, blok kode yang dapat dieksekusi seolah-olah itu fungsi tapi tanpa nama. Lambdas dapat didefinisikan di mana saja dalam sebuah program yang ekspresi Python hukum dapat terjadi, yang berarti kita dapat menggunakannya dalam fungsi FP kami.

Sebuah Lambda terlihat seperti ini:

lambda <aParameterList> : <a Python expression using the parameters>

Jadi add fungsi di atas bisa ditulis sebagai:

add = lambda i,j: i+j

Dan kita bisa menghindari garis definisi sepenuhnya dengan menciptakan lambda dalam panggilan untuk mengurangi , seperti:

print reduce(lambda i,j:i+j, numbers)

Demikian pula kita dapat menulis ulang kami peta dan penyaring contoh seperti:

 

L = map(lambda i: i, spam)
print L
L = filter(lambda i: (i%2 != 0), numbers)
print L

 

Daftar Pemahaman

Daftar pemahaman adalah teknik untuk membangun daftar baru dipinjam dari Haskell dan diperkenalkan di Python sejak versi 2.0. Ini memiliki sintaks yang sedikit tidak jelas, mirip dengan notasi set matematika. Ini terlihat seperti ini:

 

[<expression> for <value> in <collection> if <condition>]

 

Yang setara dengan:

 

L = []
for value in collection:
    if condition:
        L.append(expression)

 

Seperti dengan lainnya FP konstruksi ini menghemat beberapa baris dan dua tingkat lekukan. Mari kita melihat beberapa contoh praktis.

Pertama mari kita membuat daftar semua nomor bahkan:

>>> [n for n in range(10) if n % 2 == 0 ]
[0, 2, 4, 6, 8]

Yang mengatakan kami ingin daftar nilai ( n ) di mana n dipilih dari berbagai 0-9 dan n bahkan ( i% 2 == 0 ).

Kondisi di akhir bisa, tentu saja, akan digantikan oleh fungsi, tersedia fungsi mengembalikan nilai yang Python dapat menafsirkan sebagai boolean. Jadi melihat lagi contoh sebelumnya kita bisa menulis ulang sebagai:

>>>def isEven(n): return ((n%2) == 0)
>>> [ n for n in range(10) if isEven(n) ]
[0, 2, 4, 6, 8]

Sekarang mari kita membuat daftar kuadrat 5 angka pertama:

>>> [n*n for n in range(5)]
[0, 1, 4, 9, 16]

Perhatikan bahwa akhir jika klausa tidak diperlukan dalam setiap kasus. Berikut ungkapan awal adalah n * n dan kita menggunakan semua nilai dari jangkauan.

Akhirnya mari kita gunakan koleksi yang ada bukannya fungsi kisaran:

>>> values = [1, 13, 25, 7]
>>> [x for x in values if x < 10]
[1, 7]

Ini dapat digunakan untuk menggantikan fungsi filter berikut:

>>> filter(lambda x: x < 10, values)
[1, 7]

Comprehensions daftar tidak terbatas pada satu variabel atau satu tes namun kode mulai menjadi sangat kompleks karena lebih banyak variabel dan tes diperkenalkan.

Apakah comprehensions atau fungsi tradisional tampak paling alami atau tepat untuk Anda adalah murni subjektif. Ketika membangun koleksi baru berdasarkan koleksi yang ada Anda dapat menggunakan salah satu fungsi FP sebelumnya atau comprehensions daftar baru. Ketika membuat koleksi benar-benar baru biasanya lebih mudah untuk menggunakan pemahaman a.

Ingat meskipun bahwa sementara konstruksi ini mungkin tampak menarik, ekspresi yang dibutuhkan untuk mendapatkan hasil yang diinginkan dapat menjadi sangat kompleks sehingga lebih mudah untuk hanya memperluas mereka untuk tradisional setara python mereka. Tidak ada rasa malu dalam melakukannya - pembacaan selalu lebih baik daripada ketidakjelasan, terutama jika ketidakjelasan ini hanya demi menjadi pintar!

Konstruksi lainnya

Tentu saja sementara fungsi-fungsi ini berguna dalam hak mereka sendiri mereka tidak cukup untuk memungkinkan gaya FP penuh dalam Python. Struktur kontrol dari bahasa juga perlu diubah, atau setidaknya diganti, dengan pendekatan FP. Salah satu cara untuk mencapai ini adalah dengan menerapkan efek samping dari bagaimana Python mengevaluasi ekspresi boolean.

Evaluasi Short Circuit

Karena Python menggunakan evaluasi hubungan pendek ekspresi boolean sifat tertentu dari ekspresi ini dapat dimanfaatkan. Untuk rekap evaluasi sirkuit pendek: ketika ekspresi boolean dievaluasi evaluasi dimulai pada ekspresi tangan kiri dan hasil ke kanan, berhenti ketika tidak lagi diperlukan untuk mengevaluasi lebih jauh untuk menentukan hasil akhir.

Mengambil beberapa contoh spesifik mari kita lihat bagaimana evaluasi hubungan pendek bekerja:

>>> def TRUE():
...   print 'TRUE'
...   return True
...  
>>>def FALSE():
...   print 'FALSE'
...   return False
...

Pertama kita mendefinisikan dua fungsi yang memberitahu kita ketika mereka sedang dieksekusi dan mengembalikan nilai dari nama mereka. Sekarang kita menggunakan ini untuk menjelajahi bagaimana ekspresi boolean dievaluasi:

>>>print TRUE() and FALSE()
TRUE
FALSE
False
>>>print TRUE() and TRUE()
TRUE
TRUE
True
>>>print FALSE() and TRUE()
FALSE
False
>>>print TRUE() or FALSE()
TRUE
True
>>>print FALSE() or TRUE()
FALSE
TRUE
True
>>>print FALSE() or FALSE()
FALSE
FALSE
False

Perhatikan bahwa hanya JIKA bagian pertama dari sebuah DAN ekspresi Benar maka dan hanya maka akan bagian kedua dievaluasi. Jika bagian pertama adalah False maka bagian kedua tidak akan dievaluasi karena ekspresi secara keseluruhan tidak dapat menjadi Benar .

Demikian juga dalam OR ekspresi berdasarkan jika bagian pertama adalah benar maka bagian kedua tidak perlu dievaluasi karena keseluruhan harus menjadi Benar .

Ada satu fitur lain dari evaluasi Piton ekspresi boolean yang kita dapat memanfaatkan, yaitu bahwa ketika mengevaluasi sebuah ekspresi Python tidak hanya kembali Benar atau Salah , melainkan mengembalikan nilai sebenarnya dari ekspresi. Jadi jika pengujian untuk string kosong (yang akan dihitung sebagai False ) seperti ini:

if "This string is not empty": print "Not Empty"
else: print "No string there"

Python hanya mengembalikan string itu sendiri!

Kita dapat menggunakan properti ini untuk mereproduksi bercabang seperti perilaku. Misalnya misalkan kita memiliki sepotong kode seperti berikut:

if TRUE(): print "It is True"
else: print "It is False"

Kita bisa menggantikan dengan gaya FP membangun:

V =  (TRUE() and "It is True") or ("It is False")
print V

Coba bekerja melalui contoh yang kemudian mengganti panggilan untuk BENAR () dengan panggilan untuk SALAH () . Jadi dengan menggunakan evaluasi hubungan pendek dari ekspresi boolean kami telah menemukan cara untuk menghilangkan konvensional jika / laporan lain dari program kami. Anda mungkin ingat bahwa dalam topik rekursi kami mengamati bahwa rekursi dapat digunakan untuk menggantikan loop membangun. Sehingga menggabungkan dua efek ini dapat menghapus semua struktur kontrol konvensional dari program kami, menggantinya dengan ekspresi murni. Ini adalah langkah besar menuju memungkinkan murni solusi gaya FP.

Untuk menempatkan semua ini ke dalam praktek mari kita menulis sebuah program faktorial gaya yang sama sekali fungsional menggunakan lambda bukan def , rekursi bukan loop dan pendek sirkuit evaluasi bukan jika / lain:

>>> factorial = lambda n: ( (n <= 1) and 1) or
...                       (factorial(n-1) * n)
>>> factorial(5)
120

Dan itu benar-benar semua yang ada untuk itu. Ini mungkin tidak begitu dipahami sebagai kode Python lebih konvensional tetapi bekerja dan merupakan fungsi gaya murni fungsional dalam bahwa itu adalah ekspresi murni.

Kesimpulan

Pada titik ini Anda mungkin bertanya-tanya apa sebenarnya adalah titik semua ini? Anda tidak akan sendirian. Meskipun FP menarik bagi banyak akademisi Ilmu Komputer (dan sering matematika) yang paling berlatih programmer tampaknya menggunakan teknik FP hemat dan dalam semacam mode hybrid mencampurnya dengan gaya imperatif yang lebih tradisional karena mereka merasa sesuai.

Bila Anda harus menerapkan operasi untuk elemen dalam daftar sehingga peta, mengurangi atau filter tampaknya cara alami untuk mengekspresikan solusi maka dengan segala cara menggunakannya. Hanya kadang-kadang Anda mungkin menemukan bahwa rekursi lebih tepat daripada loop konvensional. Bahkan lebih jarang akan Anda menemukan gunakan untuk evaluasi hubungan pendek daripada konvensi jika / lain - terutama jika diperlukan dalam ekspresi. Seperti halnya alat pemrograman, tidak terbawa dengan filosofi, bukan menggunakan mana alat yang paling tepat untuk tugas di tangan. Setidaknya Anda tahu bahwa ada alternatif!

Ada satu titik akhir untuk membuat tentang lambda . Ada satu daerah di luar lingkup FP yang lambda menemukan penggunaan nyata dan itu untuk mendefinisikan event handler dalam pemrograman GUI. Event handler sering fungsi yang sangat pendek, atau mungkin mereka hanya memanggil beberapa fungsi yang lebih besar dengan beberapa nilai argumen keras-kabel. Dalam kedua kasus fungsi lambda dapat digunakan sebagai event yang menghindari kebutuhan untuk mendefinisikan banyak fungsi individu kecil dan mengisi ruang nama dengan nama-nama yang akan hanya dapat digunakan sekali. Ingat bahwa pernyataan lambda mengembalikan fungsi objek. Fungsi objek ini adalah salah satu diteruskan ke widget dan disebut pada saat peristiwa itu terjadi. Jika Anda ingat bagaimana kita mendefinisikan widget Tombol di Tkinter, maka lambda akan muncul seperti ini:

def write(s): print s
b = Button(parent, text="Press Me",
           command = lambda : write("I got pressed!"))
b.pack()

Tentu saja dalam hal ini kita bisa melakukan hal yang sama dengan hanya menetapkan nilai parameter default menulis () dan menugaskan menulis dengan perintah nilai Tombol . Namun bahkan di sini menggunakan lambda bentuk memberi kita keuntungan bahwa satu write () fungsi sekarang dapat digunakan untuk beberapa tombol hanya dengan melewati sebuah string yang berbeda dari lambda . Dengan demikian kita dapat menambahkan tombol kedua:

b2 = Button(parent, text="Or Me",
           command = lambda : write("So did I!"))
b2.pack()

Kami juga dapat menggunakan lambda ketika menggunakan teknik mengikat, yang mengirim sebuah objek acara sebagai argumen:

b3 = Button(parent, text="Press me as well")
b3.bind(<Button-1>, lambda ev : write("Pressed"))

Nah, yang benar-benar adalah bahwa untuk Pemrograman Fungsional. Ada banyak sumber lain jika Anda ingin melihat lebih dalam ke dalamnya, beberapa tercantum di bawah ini. Baik VBScript atau JavaScript langsung mendukung FP namun keduanya dapat digunakan dalam gaya fungsional oleh programmer ditentukan. Fitur kunci yang untuk struktur program Anda sebagai ekspresi dan tidak membiarkan efek samping untuk memodifikasi variabel program.

Sumber lainnya

  • Ada sebuah artikel yang sangat baik oleh David Mertz di situs web IBM tentang FP dengan Python. Ia pergi ke detail lebih lanjut tentang struktur kontrol dan memberikan contoh yang lebih rinci dari konsep.
  • Bahasa lainnya mendukung FP bahkan lebih baik daripada Python. Contohnya termasuk :: Lisp, Scheme, Haskell, ML dan beberapa orang lain. Situs web Haskell khususnya mencakup kekayaan informasi tentang FP.
  • Ada juga newsgroup, comp.lang.functional di mana Anda dapat mengejar ketinggalan pada kejadian terbaru dan menemukan FAQ berguna.
  • Ada beberapa referensi buku dapat ditemukan di situs referensi di atas. Salah satu buku klasik, yang tidak sepenuhnya tentang FP tetapi tidak menutupi prinsip-prinsip dengan baik adalah Struktur & Interpretasi Program Komputer oleh Abelman, Sussman dan Sussman. Teks ini berfokus pada Skema versi diperpanjang dari Lisp. Sumber utama pribadi saya telah buku The Haskell School of Expression oleh Paul Hudak yang, tentu cukup, tentang Haskell.

Jika orang lain menemukan referensi yang baik drop me email melalui link di bawah ini.

Hal-hal untuk diingat

  • Program fungsional adalah ekspresi murni
  • Python menyediakan peta , penyaring dan mengurangi serta daftar comprehensions untuk mendukung gaya pemrograman FP
  • lambda ekspresi anonim (yaitu tidak disebutkan namanya) blok kode yang dapat ditugaskan untuk variabel atau digunakan sebagai fungsi
  • ekspresi Boolean dievaluasi hanya sejauh diperlukan untuk menjamin hasil yang sebenarnya memungkinkan mereka untuk digunakan sebagai struktur kontrol
  • Dengan menggabungkan fitur FP Python dengan rekursi adalah mungkin (tetapi biasanya tidak dianjurkan) untuk menulis hampir setiap fungsi dalam gaya FP dengan Python.