Fungsi perintah tinggi Python. Dunia Python: fungsi dalam cara yang kecil - Python Lanjutan - Hexlet. Berfungsi sebagai objek

2010-11-17 09:47

Fungsi peta, zip dan lambda (dipanggil "fungsi tertib tinggi" atau "fungsi kelas pertama," dengan cara itu) membolehkan anda melakukan pelbagai manipulasi data dengan agak mudah, yang memerlukan menulis lebih sedikit kod dalam "biasa ” gaya prosedur. Semua yang ditulis di bawah merujuk kepada apa yang dipanggil pengaturcaraan berfungsi, cari butiran.

Fungsi peta, zip dan lambda dalam contoh.

Tugasan mudah ialah senarai a = dan senarai b = yang sama panjang dan anda perlu menggabungkannya secara berpasangan. Ia semudah membelek pear - menggunakan fungsi tersebut zip :

a = [ 1 , 2 ] b = [ 3 , 4 ] zip cetak (a , b ) [(1 , 3 ), (2, 4 )]

atau dalam kembar tiga:

a = [ 1 , 2 ] b = [ 3 , 4 ] c = [ 5 , 6 ] zip cetakan (a , b , c ) [(1 , 3 , 5 ), (2 , 4, 6 )]

atau lebih umum

senarai = [a, b, c] zip cetak (* senarai) [(1, 3, 5), (2, 4, 6)]

Asterisk * sebelum senarai nampaknya menunjukkan bahawa senarai hujah sedang diluluskan, i.e. Lakonan adalah bersamaan dengan lulus a, b, c i.e. Malah mungkin cetak zip(*) hasilnya tidak akan berubah.

def f (x): kembalikan x * x nums = [ 1 , 2 , 3 ] untuk num in nums : print f (num )

Noob yang lebih berpengalaman yang telah mempelajari pemahaman senarai:

def f (x): kembalikan x * x cetak [ f (num) untuk num dalam nombor ]

Pengaturcara akan memudahkannya:

def f (x): kembalikan x * x cetak peta (f, angka)

Dan penggodam true-medskills akan melakukannya seperti berikut (dengan syarat, sudah tentu, fungsi itu boleh ditulis sebagai lambda; fungsi itu tidak semestinya cukup mudah untuk ditulis sebagai lambda):

cetak peta (lambda x : x * x , nombor )

Entri terakhir adalah contoh pendekatan yang paling cekap. Hakikatnya ialah apabila seseorang menulis kod seperti puisi, dalam bentuk inspirasi (yang dalam erti kata lain boleh dipanggil "dalam kegilaan liar"), kelajuan menulis adalah sangat penting (di sinilah akar cinta hormat daripada banyak pembangun untuk editor teks mudah vim, emacs, sublimetext grow) , dan kekuatan Python adalah tepat saiz kod yang dihasilkan - ia sangat padat. Menulis satu baris secara semula jadi lebih pantas daripada 7, dan kod pendek lebih mudah dibaca, tetapi menulis kod tersebut memerlukan kemahiran tertentu. Sisi lain syiling ialah kadang-kadang dalam "kegilaan liar" ini keseluruhan urutan tindakan yang agak kompleks ditulis dalam satu baris, sehingga sangat sukar untuk memahami apa yang berlaku di sana dan apa yang berlaku pada akhirnya.

Daripada contoh itu jelas bahawa peta menggunakan fungsi pada senarai dan mengembalikan hasilnya, sekali lagi dalam bentuk senarai. Anda boleh lulus beberapa senarai, maka fungsi (yang merupakan parameter pertama) mesti mengambil beberapa argumen (mengikut bilangan senarai yang dihantar ke peta).

def f (x, y): kembalikan x * y a = [1, 3, 4] b = [3, 4, 5] cetak peta (f, a, b) [3, 12, 20]

Sejuk kan?

Walau bagaimanapun, jika senarai mempunyai panjang yang berbeza, i.e. Satu lebih pendek daripada yang lain, maka ia akan dipadatkan dengan nilai Tiada pada panjang yang dikehendaki. Jika dialih keluar daripada senarai b nilai terakhir - contoh tidak akan berfungsi, kerana Dalam fungsi f, percubaan akan dibuat untuk mendarab nombor dengan Tiada, dan Python tidak membenarkan ini, yang, dengan cara itu, membezakannya dengan baik daripada php, yang akan terus berfungsi dalam situasi yang sama. Oleh itu, jika fungsi f cukup besar, adalah idea yang baik untuk menyemak nilai yang diluluskan. Sebagai contoh;

Jika fungsi itu digantikan dengan Tiada, maka peta berfungsi hampir sama dengan zip, tetapi jika senarai yang dihantar mempunyai panjang yang berbeza, hasilnya akan ditulis Tiada - yang, dengan cara itu, sangat sesuai dalam beberapa kes.

a = [ 1 , 3 , 4 ] b = [ 3 , 4 ] cetak peta (Tiada , a , b ) [(1 , 3 ), (3 , 4 ), (4 , Tiada )]

Sekarang kira-kira lambda berfungsi dalam python. Ia digunakan apabila anda perlu mentakrifkan fungsi tanpa menggunakan def func_name(): ..., kerana selalunya (seperti dalam contoh sebelumnya) fungsi itu sangat kecil sehingga tidak ada gunanya mentakrifkannya secara berasingan (baris tambahan kod menjejaskan kebolehbacaan ). Oleh itu, fungsi boleh ditakrifkan "di tempat" f = lambda x: x*x seolah-olah memberitahu kami - menerima x, mengembalikan x*x

Jadi, menggunakan alat Python standard, anda boleh menulis tindakan yang agak kompleks dalam satu baris. Contohnya fungsi:

def f (x , y ): jika (y == Tiada ): y = 1 pulangan x * y

boleh diwakili sebagai:

lambda x , y : x * (y jika y bukan Tiada yang lain 1 )

Sekarang adalah baik untuk lulus senarai yang diisih mengikut panjang - len(a) > (b) - semudah mencelah pear - mari gunakan fungsi disusun :

diisih ([ a , b ], kunci = lambda x : len ( x ), reverse = True )

fungsi disusun mengambil senarai nilai ( = [,]) dan diisih mengikut kunci– yang diberikan oleh fungsi len(x) - yang mengembalikan panjang senarai, kami menyusunnya dalam tertib menurun (terbalik=Benar)

Akhirnya, keseluruhan operasi ditulis seperti ini:

peta (lambda x , y : x * (y jika y bukan Tiada yang lain 1 ), * diisih ([ a , b ], kunci = lambda x : len (x ), reverse = True ))

senarai a dan b boleh mempunyai panjang yang berbeza dan dihantar dalam sebarang susunan. Ungkapan Lambda berguna untuk menentukan fungsi yang tidak terlalu kompleks yang kemudiannya diteruskan ke fungsi lain.

3.2.3 Pemahaman Kamus

Katakan kita mempunyai kamus yang kuncinya ialah aksara dan nilai yang dipetakan kepada bilangan kali aksara itu muncul dalam beberapa teks. Kamus pada masa ini membezakan antara aksara besar dan huruf kecil.

Kami memerlukan kamus di mana kemunculan aksara besar dan huruf kecil digabungkan:

dct = ( "a" : 10 , "b" : 34 , "A" : 7 , "Z" : 3 )

kekerapan = ( k . lower () : dct. get ( k . lower () , 0 ) + dct . get ( k . upper () , 0 )

untuk k dalam dct . kunci())

kekerapan cetakan #("a": 17, "z": 3, "b": 34)

Python menyokong penciptaan fungsi tanpa nama (iaitu fungsi yang tidak terikat pada nama) pada masa jalan, menggunakan binaan yang dipanggil "lambda". Ini tidak betul-betul sama dengan lambda dalam bahasa pengaturcaraan berfungsi, tetapi ia adalah konsep yang sangat berkuasa yang disepadukan dengan baik ke dalam Python dan sering digunakan bersama-sama dengan konsep fungsi biasa seperti filter() , map() dan reduce() .

Fungsi tanpa nama dalam bentuk ungkapan boleh dibuat menggunakan lambda
kenyataan:

args ialah senarai hujah yang dipisahkan koma dan ungkapan ialah ungkapan yang melibatkan hujah tersebut. Sekeping kod ini menunjukkan perbezaan antara definisi fungsi normal dan fungsi lambda:

fungsi def (x):

pulangkan x * x

fungsi cetakan(2) #4

#-----------------------#

fungsi = lambda x : x * x

fungsi cetakan(2) #4

Seperti yang anda lihat, kedua-dua function() melakukan perkara yang sama dan boleh digunakan dengan cara yang sama. Ambil perhatian bahawa takrifan lambda tidak termasuk pernyataan "kembali" - ia sentiasa mengandungi ungkapan yang dikembalikan. Juga ambil perhatian bahawa anda boleh meletakkan definisi lambda di mana-mana sahaja fungsi dijangka, dan anda tidak perlu menetapkannya kepada pembolehubah sama sekali.

Serpihan kod berikut menunjukkan penggunaan fungsi lambda.

kenaikan def (n):

pulangkan lambda x : x + n

kenaikan cetakan(2) # di 0x022B9530>

kenaikan cetakan (2) (20) #22

Kod di atas mentakrifkan kenaikan fungsi yang mencipta fungsi tanpa nama dengan cepat dan mengembalikannya. Fungsi yang dikembalikan menambah hujahnya dengan nilai yang ditentukan apabila ia dibuat.

Anda kini boleh mencipta berbilang fungsi kenaikan yang berbeza dan menetapkannya kepada pembolehubah, kemudian menggunakannya secara bebas antara satu sama lain. Seperti yang ditunjukkan oleh pernyataan terakhir, anda tidak perlu menetapkan fungsi itu di mana-mana - anda hanya boleh menggunakannya serta-merta dan melupakannya apabila ia tidak diperlukan lagi.

S3. Apakah kebaikan lambda?
Ans.
Jawapannya ialah:

  • Kami tidak memerlukan lambda, kami boleh bergaul dengan baik tanpanya. Tetapi…
  • terdapat situasi tertentu yang sesuai - ia menjadikan penulisan kod lebih mudah dan kod bertulis sedikit lebih bersih.

S4. Apakah jenis situasi?

Nah, situasi di mana kita memerlukan fungsi sekali sahaja: fungsi yang akan digunakan sekali sahaja.

Biasanya, fungsi dicipta untuk satu daripada dua tujuan: (a) untuk mengurangkan pertindihan kod, atau (b) untuk memodulasi kod.

  • Jika aplikasi anda mengandungi pendua ketulan kod di pelbagai tempat, maka anda boleh meletakkan satu salinan kod itu ke dalam fungsi, beri nama fungsi itu, dan kemudian - menggunakan nama fungsi itu - memanggilnya dari pelbagai tempat dalam kod anda.
  • Jika anda mempunyai sebilangan kod yang melakukan satu operasi yang jelas - tetapi sangat panjang dan kasar dan mengganggu aliran program anda yang boleh dibaca - maka anda boleh mengeluarkan kod yang panjang itu dan memasukkannya ke dalam fungsi dengan sendirinya.

Tetapi katakan anda perlu mencipta fungsi yang akan digunakan sekali sahaja - dipanggil dari hanya satu letak dalam permohonan anda. Baiklah, pertama sekali, anda tidak perlu memberi nama fungsi. Ia boleh menjadi "tanpa nama". Dan anda boleh mentakrifkannya betul-betul di tempat yang anda mahu gunakan. Di situlah lambda berguna.

Biasanya, lambda digunakan dalam konteks beberapa operasi lain, seperti pengisihan atau pengurangan data:

nama = [ "David Beazley" , "Brian Jones" , "Raymond Hettinger" , "Ned Batchelder" ]

cetakan diisih (nama , kunci = nama lambda : nama . split () [ - 1 ] . lower () )

# ["Ned Batchelder", "David Beazley", "Raymond Hettinger", "Brian Jones"]

Walaupun lambda membolehkan anda mentakrifkan fungsi mudah, penggunaannya sangat terhad. Dalam
khususnya, hanya satu ungkapan boleh ditentukan, yang hasilnya adalah pulangan
nilai. Ini bermakna tiada ciri bahasa lain, termasuk berbilang pernyataan, syarat, lelaran dan pengendalian pengecualian, boleh disertakan.
Anda boleh menulis banyak kod Python dengan gembira tanpa menggunakan lambda. Walau bagaimanapun,
anda sekali-sekala akan menemuinya dalam program di mana seseorang menulis banyak perkara kecil
fungsi yang menilai pelbagai ungkapan, atau dalam program yang memerlukan pengguna membekalkan
fungsi panggil balik.

Anda telah menentukan fungsi tanpa nama menggunakan lambda, tetapi anda juga perlu menangkapnya
nilai pembolehubah tertentu pada masa definisi.

>>> x = 10

>>> a = lambda y : x + y

>>> x = 20

>>> b = lambda y : x + y

Sekarang tanya diri anda satu soalan. Apakah nilai a(10) dan b(10)? Jika anda fikir
keputusan mungkin 20 dan 30, anda akan silap:

Masalahnya di sini ialah nilai x yang digunakan dalam ungkapan lambda ialah pembolehubah bebas
yang terikat pada masa larian, bukan masa definisi. Oleh itu, nilai x dalam lambda
ungkapan ialah apa sahaja nilai pembolehubah x yang berlaku pada masa pelaksanaan.
Sebagai contoh:

Jika anda mahukan fungsi tanpa nama untuk menangkap nilai pada titik definisi dan
simpannya, masukkan nilai sebagai nilai lalai, seperti ini:

Masalah yang ditangani di sini adalah sesuatu yang cenderung muncul dalam kod itu
cuba menjadi sedikit terlalu bijak dengan penggunaan fungsi lambda. Sebagai contoh,
mencipta senarai ungkapan lambda menggunakan pemahaman senarai atau dalam gelung sejenis dan mengharapkan fungsi lambda mengingati pembolehubah lelaran pada masa definisi. Sebagai contoh:

>>> fungsi = [ lambda x : x + n untuk n dalam julat (5 ) ]

  • Terjemahan

Apabila bercakap tentang pengaturcaraan berfungsi, orang sering mula membuang sekumpulan ciri "berfungsi". Data tidak berubah, fungsi kelas pertama dan pengoptimuman rekursi ekor. Ini adalah sifat bahasa yang membantu anda menulis atur cara berfungsi. Mereka menyebut pemetaan, kari, dan menggunakan fungsi tertib lebih tinggi. Ini adalah teknik pengaturcaraan yang digunakan untuk menulis kod berfungsi. Mereka menyebut keselarian, penilaian malas, dan determinisme. Ini adalah faedah program berfungsi.

Skornya. Kod fungsian dibezakan oleh satu sifat: ketiadaan kesan sampingan. Ia tidak bergantung pada data di luar fungsi semasa, dan tidak mengubah data di luar fungsi. Semua "sifat" lain boleh disimpulkan daripada ini.

Fungsi tidak berfungsi:

A = 0 def increment1(): global a a += 1

Ciri Fungsian:

Def increment2(a): kembalikan a + 1

Daripada lelaran melalui senarai, gunakan peta dan kurangkan

Peta

Menerima fungsi dan set data. Mencipta koleksi baharu, melaksanakan fungsi pada setiap kedudukan data dan menambah nilai pulangan pada koleksi baharu. Mengembalikan koleksi baharu.

Peta ringkas yang mengambil senarai nama dan mengembalikan senarai panjang:

Nama_panjang = peta(len, ["Masha", "Petya", "Vasya"]) cetak nama_panjang # =>

Peta ini mengduakan setiap elemen:

Petak = peta(lambda x: x * x, ) petak cetak # =>

Ia tidak menerima fungsi bernama, tetapi mengambil fungsi tanpa nama yang ditakrifkan melalui lambda. Parameter Lambda ditakrifkan di sebelah kiri kolon. Badan fungsi berada di sebelah kanan. Hasilnya dikembalikan secara tersirat.

Kod tidak berfungsi dalam contoh berikut mengambil senarai nama dan menggantikannya dengan nama panggilan rawak.

Import nama rawak = ["Masha", "Petya", "Vasya"] nama_kod = ["Shpuntik", "Vintik", "Funtik"] untuk i dalam julat(len(nama)): nama[i] = rawak. pilihan(nama_kod) cetakan nama # => ["Shpuntik", "Vintik", "Shpuntik"]

Algoritma boleh memberikan nama panggilan yang sama kepada ejen rahsia yang berbeza. Semoga ini tidak menimbulkan masalah semasa misi rahsia.

Mari kita tulis semula ini menggunakan peta:

Import nama rawak = ["Masha", "Petya", "Vasya"] secret_names = map(lambda x: random.choice(["Shpuntik", "Vintik", "Funtik"]), nama)

Latihan 1. Cuba tulis semula kod berikut menggunakan peta. Ia memerlukan senarai nama sebenar dan menggantikannya dengan nama panggilan menggunakan kaedah yang lebih dipercayai.

Nama = ["Masha", "Petya", "Vasya"] untuk i dalam julat(len(nama)): nama[i] = hash(nama[i]) nama cetakan # =>

Keputusan saya:

nama = ["Masha", "Petya", "Vasya"] secret_names = peta(hash, nama)

Kurangkan

Mengurangkan mengambil fungsi dan satu set item. Mengembalikan nilai yang diperoleh dengan menggabungkan semua item.

Contoh pengurangan mudah. Mengembalikan jumlah semua item dalam satu set:

Jumlah = kurangkan(lambda a, x: a + x, ) cetak jumlah # => 10

X ialah item semasa, dan ialah bateri. Ini ialah nilai yang dikembalikan dengan melaksanakan lambda pada item sebelumnya. reduce() melelaran melalui semua nilai, dan menjalankan lambda untuk setiap satu pada nilai semasa a dan x, dan mengembalikan keputusan dalam a untuk lelaran seterusnya.

Apakah nilai a dalam lelaran pertama? Ia sama dengan elemen pertama koleksi, dan reduce() mula berfungsi dari elemen kedua. Iaitu, x pertama akan sama dengan item kedua set.

Contoh berikut mengira kekerapan perkataan "kapten" muncul dalam senarai rentetan:

Ayat = ["Kapten Jack Sparrow", "kapten laut", "bot anda sudah sedia, kapten"] cap_count = 0 untuk ayat dalam ayat: cap_count += sentence.count("captain") print cap_count # => 3

Kod yang sama menggunakan reduce:

Ayat = ["kapten jack sparrow", "kapten laut", "bot anda sudah sedia, kapten"] cap_count = kurangkan(lambda a, x: a + x.count("kapten"), ayat, 0)

Di manakah nilai awal a datang dari sini? Ia tidak boleh dikira daripada bilangan ulangan dalam baris pertama. Oleh itu, ia diberikan sebagai hujah ketiga kepada fungsi reduce().

Mengapa peta dan kurangkan lebih baik?

Pertama, mereka biasanya muat pada satu baris.

Kedua, bahagian penting lelaran—pengumpulan, operasi, dan nilai pulangan—sentiasa berada di tempat yang sama, petakan dan kurangkan.

Ketiga, kod dalam gelung boleh mengubah nilai pembolehubah yang ditakrifkan sebelum ini, atau menjejaskan kod selepasnya. Mengikut konvensyen, peta dan pengurangan berfungsi.

Keempat, peta dan pengurangan adalah operasi asas. Daripada membaca gelung baris demi baris, lebih mudah bagi pembaca untuk melihat peta dan mengurangkan algoritma terbina dalam kompleks.

Kelima, mereka mempunyai ramai kawan yang membenarkan tingkah laku yang berguna dan diubah suai bagi fungsi ini. Contohnya, tapis, semua, mana-mana dan cari.

Latihan 2: Tulis semula kod berikut menggunakan peta, kurangkan dan tapis. Penapis menerima fungsi dan koleksi. Mengembalikan koleksi perkara yang fungsi mengembalikan True.

Orang = [("nama": "Masha", "tinggi": 160), ("tinggi": "Sasha", "tinggi": 80), ("nama": "Pasha")] tinggi_jumlah = 0 tinggi_kira = 0 untuk orang dalam orang: jika "ketinggian" secara peribadi: height_total += person["height"] height_count += 1 if height_count > 0: average_height = height_total / height_count print average_height # => 120

Keputusan saya:

orang = [("nama": "Masha", "tinggi": 160), ("tinggi": "Sasha", "tinggi": 80), ("nama": "Pasha")] ketinggian = peta(lambda x: x["ketinggian"], penapis(lambda x: "ketinggian" dalam x, orang)) jika len(ketinggian) > 0: daripada import operator tambahkan purata_tinggi = kurangkan(tambah, ketinggian) / len(ketinggian)

Tulis secara deklaratif, bukan imperatif.

Program berikut mencontohi perlumbaan tiga kereta. Pada setiap saat, kereta itu sama ada bergerak ke hadapan atau tidak. Setiap kali program memaparkan jarak yang dilalui oleh kereta. Selepas lima selang masa perlumbaan tamat.

Contoh output:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

Teks program:

Dari masa rawak import import rawak = 5 kedudukan_kereta = sementara masa: # masa berkurangan -= 1 cetakan "" untuk i dalam julat(len(kedudukan_kereta)): # gerakkan kereta jika rawak() > 0.3: kedudukan_kereta[i] += 1 # lukis cetakan kereta "-" * kedudukan_kereta[i]

Kod itu penting. Versi berfungsi adalah deklaratif - ia akan menerangkan perkara yang perlu dilakukan, bukan bagaimana ia harus dilakukan.

Menggunakan fungsi

Pengisytiharan boleh dicapai dengan memasukkan kod ke dalam fungsi:

Daripada import rawak def move_cars(): untuk i, _ dalam enumerate(kedudukan_kereta): jika rawak() > 0.3: car_positions[i] += 1 def draw_car(car_position): print "-" * car_position def run_step_of_race(): masa masa global -= 1 move_cars() def draw(): print "" untuk car_position in car_positions: draw_car(car_position) masa = 5 car_positions = while time: run_step_of_race() draw()

Untuk memahami program, pembaca melihat gelung utama. “Jika ada masa yang tinggal, kami akan melalui satu langkah perlumbaan dan mempamerkan keputusan. Mari kita semak masa sekali lagi." Jika pembaca perlu memahami cara langkah perlumbaan berfungsi, mereka boleh membaca kod secara berasingan.

Tiada ulasan diperlukan, kod itu menerangkan sendiri.

Pemecahan kod kepada fungsi menjadikan kod lebih mudah dibaca. Teknik ini menggunakan fungsi, tetapi hanya sebagai subrutin. Mereka membungkus kod, tetapi tidak menjadikannya berfungsi. Fungsi mempengaruhi kod di sekelilingnya dan menukar pembolehubah global dan bukannya mengembalikan nilai. Jika pembaca menemui pembolehubah, mereka perlu mencari dari mana asalnya.

Di sini versi berfungsi program ini:

Daripada import rawak def move_cars(kedudukan_kereta): peta pulangan(lambda x: x + 1 jika rawak() > 0.3 x lain, kedudukan_kereta) def output_kereta(kedudukan_kereta): pulangkan "-" * kedudukan_kereta def larian_langkah_perlumbaan(negeri): pulangkan ( "masa": nyatakan["masa"] - 1, "kedudukan_kereta": move_cars(nyatakan["kedudukan_kereta"])) def draw(negeri): cetak "" cetak "\n".join(peta(output_car, state[ "kedudukan_kereta"])) def perlumbaan(negeri): seri(nyatakan) jika negeri["masa"]: perlumbaan(lari_langkah_perlumbaan(negeri)) perlumbaan(("masa": 5, "kedudukan_kereta": ))

Kod itu kini dipecahkan kepada fungsi berfungsi. Terdapat tiga tanda ini. Yang pertama ialah tiada pembolehubah yang dikongsi. masa dan kedudukan_kereta dihantar terus ke perlumbaan(). Kedua, fungsi mengambil parameter. Ketiga, pembolehubah tidak berubah dalam fungsi; semua nilai dikembalikan. Setiap kali run_step_of_race() melakukan langkah seterusnya, ia dihantar kembali ke langkah seterusnya.

Berikut ialah dua fungsi sifar() dan satu():

Def zero(s): if s == "0": return s def one(s): if s == "1": return s

Zero() mengambil rentetan s. Jika aksara pertama ialah 0, kemudian mengembalikan seluruh rentetan. Jika tidak, maka Tiada. one() melakukan perkara yang sama jika aksara pertama ialah 1.

Mari bayangkan fungsi rule_sequence(). Ia memerlukan rentetan dan senarai fungsi peraturan, yang terdiri daripada fungsi sifar dan satu. Ia memanggil peraturan pertama, menghantarnya sebagai rentetan. Jika Tiada dikembalikan, kemudian ambil nilai yang dikembalikan dan panggil peraturan seterusnya. Dan sebagainya. Jika Tiada dikembalikan, rule_sequence() berhenti dan mengembalikan Tiada. Jika tidak - maksud peraturan terakhir.

Contoh data input dan output:

Print rule_sequence("0101", ) # => 1 print rule_sequence("0101", ) # => Tiada

Versi imperatif rule_sequence():

Def rule_sequence(s, rules): untuk peraturan dalam rules: s = rule(s) if s == Tiada: break return s

Latihan 3. Kod ini menggunakan gelung. Tulis semula secara deklaratif menggunakan rekursi.

Keputusan saya:

def rule_sequence(s, rules): if s == Tiada atau tidak peraturan: return s else: return rule_sequence(rules(s), rules)

Gunakan saluran paip

Sekarang mari kita tulis semula jenis gelung lain menggunakan teknik yang dipanggil saluran paip.

Gelung seterusnya mengubah suai kamus yang mengandungi nama, negara asal yang salah dan status beberapa kumpulan.

Band = [("name": "sunset rubdown", "country": "UK", "active": Palsu), ("name": "wanita", "country": "Jerman", "active": Palsu ), ("name": "a silver mt. zion", "country": "Sepanyol", "active": True]] def format_bands(bands): untuk band dalam band: band["country"] = "Canada " band["name"] = band["name"].replace(".", "") band["name"] = band["name"].title() format_bands(bands) print band # => [("name": "Sunset Rubdown", "active": Palsu, "country": "Canada"), # ("name": "Wanita", "active": Palsu, "country": "Canada" ) , # ("nama": "A Silver Mt Zion", "aktif": Benar, "negara": "Kanada")]

Nama fungsi "format" terlalu umum. Secara umum, kod tersebut menimbulkan kebimbangan. Tiga perkara berbeza berlaku dalam satu kitaran. Nilai kunci "negara" ditukar kepada "Kanada". Titik dibuang dan huruf pertama nama ditukar kepada huruf besar. Sukar untuk memahami apa yang sepatutnya dilakukan oleh kod itu, dan sukar untuk mengetahui sama ada ia melakukannya. Sukar untuk digunakan, diuji dan disejajarkan.

Bandingkan:

Cetak talian paip_setiap(jalur, )

Mudah sahaja. Fungsi pembantu kelihatan berfungsi kerana ia dirantai bersama. Output dari yang sebelumnya adalah input dari yang berikutnya. Ia mudah untuk diuji, digunakan semula, disahkan dan disejajarkan.

Pipeline_each() berulang melalui kumpulan satu demi satu dan menghantarnya ke fungsi penukaran seperti set_canada_as_country(). Selepas menggunakan fungsi kepada semua kumpulan, pipeline_each() membuat senarai mereka dan meneruskannya ke yang seterusnya.

Mari kita lihat fungsi transformasi.

Def assoc(_d, kunci, nilai): daripada copy import deepcopy d = deepcopy(_d) d = value return d def set_canada_as_country(band): return assoc(band, "country", "Canada") def strip_punctuation_from_name(band): return assoc(band, "name", band["name"].replace(".", "")) def capitalize_names(band): return assoc(band, "name", band["name"].title( ))

Setiap satu mengaitkan kunci kumpulan dengan nilai baharu. Ini sukar dilakukan tanpa mengubah data asal, jadi kami menyelesaikannya dengan assoc(). Ia menggunakan deepcopy() untuk mencipta salinan kamus yang diluluskan. Setiap fungsi mengubah salinan dan mengembalikan salinan itu.

Semuanya nampak baik-baik saja. Data asal dilindungi daripada perubahan. Tetapi terdapat dua tempat yang berpotensi dalam kod untuk perubahan data. Dalam strip_punctuation_from_name() nama tanpa titik dicipta dengan memanggil memanggil replace() dengan nama asal. capitalize_names() mencipta nama dengan huruf besar pertama berdasarkan title() dan nama asal. Jika penggantian dan masa tidak berfungsi, maka strip_punctuation_from_name() dan capitalize_names() tidak berfungsi.

Nasib baik, mereka berfungsi. Dalam Python, rentetan tidak boleh diubah. Fungsi ini berfungsi dengan salinan rentetan. Uff, alhamdulillah.

Perbezaan antara rentetan dan kamus (sifatnya yang boleh berubah) dalam Python menunjukkan kelebihan bahasa seperti Clojure. Di sana, pengaturcara tidak perlu memikirkan sama ada dia akan menukar data. Ia tidak akan berubah.

Latihan 4. Cuba buat fungsi pipeline_each. Fikirkan tentang urutan operasi. Kumpulan berada dalam tatasusunan, dihantar satu demi satu ke fungsi penukaran pertama. Kemudian tatasusunan yang terhasil dihantar satu bahagian pada satu masa ke fungsi kedua, dan seterusnya.

Keputusan saya:

def pipeline_each(data, fns): return reduce(lambda a, x: map(x, a), fns, data)

Ketiga-tiga fungsi transformasi mengakibatkan perubahan medan khusus untuk kumpulan. call() boleh digunakan untuk membuat abstraksi untuk ini. Ia menerima fungsi dan kunci yang akan digunakan.

Set_canada_as_country = panggilan(lambda x: "Kanada", "negara") strip_punctuation_from_name = panggilan(lambda x: x.replace(".", ""), "name") capitalize_names = panggilan(str.title, "nama") cetak saluran paip_setiap(jalur, )

Atau, mengorbankan kebolehbacaan:

Cetak talian paip_setiap(jalur, )

Kod untuk panggilan():

Def assoc(_d, kunci, nilai): daripada copy import deepcopy d = deepcopy(_d) d = nilai pulangan d def call(fn, key): def apply_fn(rekod): return assoc(rekod, kunci, fn(rekod. get(key))) return apply_fn

Apa yang berlaku di sini?

satu. panggilan adalah fungsi pesanan yang lebih tinggi, kerana mengambil fungsi lain sebagai hujah dan mengembalikan fungsi tersebut.

dua. apply_fn() adalah serupa dengan fungsi penukaran. Mendapat kemasukan (kumpulan). Mencari rekod nilai. Memanggil fn. Berikan keputusan kepada salinan rekod dan mengembalikannya.

Tiga. memanggil dirinya tidak melakukan apa-apa. apply_fn() melakukan semua kerja. Dalam contoh pipeline_each(), satu contoh apply_fn() menetapkan "negara" kepada "Kanada". Yang satu lagi menggunakan huruf besar huruf pertama.

Empat. Apabila melaksanakan contoh apply_fn(), fungsi fn dan kekunci tidak akan tersedia dalam skop. Ini bukan hujah untuk apply_fn() atau pembolehubah setempat. Tetapi akan ada akses kepada mereka. Apabila fungsi ditakrifkan, ia mengekalkan rujukan kepada pembolehubah yang ditutup—yang ditakrifkan di luar fungsi dan digunakan secara dalaman. Apabila fungsi dijalankan, pembolehubah dicari antara yang tempatan, kemudian antara argumen, dan kemudian antara rujukan kepada penutupan. Di sana anda akan menemui fn dan kunci.

lima. Tiada disebut kumpulan dalam panggilan. Ini kerana panggilan boleh digunakan untuk membuat sebarang saluran paip, tanpa mengira kandungannya. Pengaturcaraan fungsional, khususnya, membina perpustakaan fungsi umum, sesuai untuk gubahan dan guna semula.

Bagus. Penutupan, fungsi tertib dan skop yang lebih tinggi - semuanya dalam beberapa perenggan. Anda juga boleh minum teh dan biskut.

Masih ada satu lagi pemprosesan data kumpulan. Alih keluar semua kecuali nama dan negara. fungsi extract_name_and_country():

Def extract_name_and_country(band): plucked_band = () plucked_band["name"] = band["name"] plucked_band["country"] = band["country"] return plucked_band print pipeline_each(bands, ) # => [(" name": "Sunset Rubdown", "country": "Canada"), # ("name": "Wanita", "country": "Canada"), # ("name": "A Silver Mt Zion", " negara": "Kanada")]

Extract_name_and_country() boleh ditulis dalam bentuk generik yang dipanggil pluck(). Ia akan digunakan seperti ini:

Cetak talian paip_setiap(jalur, )])

Latihan 5. pluck menerima senarai kunci untuk diekstrak daripada rekod. Cuba menulisnya. Ini akan menjadi fungsi tertib yang lebih tinggi.

Bukan tanpa alasan bahawa bahasa Python popular di kalangan pengaturcara Google dan editor Penggodam pada masa yang sama :). Bahasa yang benar-benar berkuasa ini membolehkan anda menulis kod mengikut beberapa paradigma, dan hari ini kami akan cuba mengetahui perbezaan antara mereka dan yang mana satu lebih baik untuk diikuti.

Paradigma apa?! Jom kod!

Apabila anda perlu menulis sesuatu, perkara terakhir yang mungkin anda risaukan ialah paradigma pengaturcaraan yang hendak dipilih. Sebaliknya, anda sama ada memilih bahasa yang paling sesuai, atau segera memulakan pengekodan dalam kegemaran anda, pilihan dan terbukti selama ini. Memang benar, biar ahli ideologi berfikir tentang ideologi, tugas kita ialah memprogram :). Namun, apabila pengaturcaraan, anda semestinya mengikuti beberapa jenis paradigma. Mari kita lihat contoh. Mari kita cuba menulis sesuatu yang mudah... baik, sebagai contoh, mari kita mengira luas bulatan.

Anda boleh menulisnya seperti ini:

Luas bulatan (pilihan satu)

dua luas_bulatan(ganda r) (
kembalikan M_PI*pow(r,2);
}
int main() (
ganda r = 5;
cout<< "Площадь: "<< area_of_circle(r)<< endl;
}

Atau anda boleh melakukan ini:

Luas bulatan (pilihan dua)

Bulatan kelas(
ganda r;
awam:
Bulatan(berganda r) ( ini->r = r; )
kawasan berganda() ( kembalikan M_PI*pow(this->r,2); )
void print_area() (
cout<< "Площадь: "<< this->kawasan()<< endl;
}
};
int main() ((Bulatan baharu(5))->print_area();)

Ia boleh dilakukan secara berbeza... tetapi tidak kira betapa sukarnya anda mencuba, kod itu akan menjadi sama ada penting (seperti dalam kes pertama) atau berorientasikan objek (seperti dalam kedua).
Ini bukan disebabkan oleh kekurangan imaginasi, tetapi semata-mata kerana C++ disesuaikan dengan paradigma ini.

Dan yang terbaik (atau yang paling teruk, bergantung pada kelurusan tangan anda) yang boleh anda lakukan dengannya ialah mencampurkan beberapa paradigma.

Paradigma

Seperti yang anda mungkin telah meneka, anda boleh menulis dalam bahasa yang sama mengikut beberapa paradigma, kadang-kadang malah beberapa sekali gus. Mari kita lihat wakil utama mereka, kerana tanpa pengetahuan ini anda tidak akan dapat menganggap diri anda seorang pengkod profesional, dan kemungkinan besar anda perlu melupakan bekerja dalam satu pasukan.

Pengaturcaraan imperatif

“Mula-mula kita buat ini, kemudian ini, kemudian ini”

Bahasa: Hampir semua

Paradigma yang benar-benar boleh difahami oleh mana-mana pengaturcara: "Seseorang memberikan satu set arahan kepada mesin."
Semua orang mula belajar/memahami pengaturcaraan daripada paradigma imperatif.

Pengaturcaraan berfungsi

"Kami mengira ungkapan dan menggunakan hasilnya untuk sesuatu yang lain."

Bahasa: Haskell, Erlang, F#

Paradigma yang sama sekali tidak dapat difahami oleh pengaturcara baru. Kami menerangkan bukan urutan keadaan (seperti dalam paradigma imperatif), tetapi urutan tindakan.

Pengaturcaraan berorientasikan objek

"Kami bertukar-tukar mesej antara objek, mensimulasikan interaksi di dunia nyata."

Bahasa: Hampir semua

Dengan kedatangannya, paradigma berorientasikan objek telah memasuki kehidupan kita.
Hampir semua proses perniagaan moden dibina atas OOP.

Pengaturcaraan logik

"Kami menjawab soalan dengan mencari penyelesaian."

Bahasa: Prolog

Pengaturcaraan logik adalah perkara yang agak khusus, tetapi pada masa yang sama, menarik dan intuitif.
Contoh mudah sudah memadai:

(tetapkan peraturan)
ahli sihir(X)<= burns(X) and female(X).
melecur(X)<= wooden(X).
kayu(X)<= floats(X).
terapung (X)<= sameweight(duck, X).
(tetapkan pemerhatian)
perempuan (perempuan).
sama berat (itik, perempuan).
(tanya soalan)
? ahli sihir (gadis).

Walaupun setiap pengaturcara mengikut definisi biasa dengan pengaturcaraan imperatif dan berorientasikan objek, kami jarang menemui pengaturcaraan berfungsi dalam bentuk tulennya.

Pengaturcaraan fungsional berbeza dengan pengaturcaraan imperatif.

Pengaturcaraan imperatif melibatkan urutan perubahan kepada keadaan program, dan pembolehubah digunakan untuk menyimpan keadaan ini.

Pengaturcaraan fungsional, sebaliknya, melibatkan urutan tindakan pada data. Ini serupa dengan matematik - kita menulis formula f(x) di papan untuk masa yang lama, dan kemudian menggantikan x dan mendapatkan hasilnya.

Dan intipati pengaturcaraan berfungsi ialah di sini formula adalah alat yang kami gunakan untuk X.

Ular sawa bermuka dua

Tidak ada teori yang lebih baik daripada latihan, jadi mari kita menulis sesuatu. Lebih baik lagi, tulis dalam Python :).
Mari kita hitung jumlah kuasa dua unsur tatasusunan "data" secara imperatif dan berfungsi:

Python Imperatif

data = [...]
jumlah = 0
untuk elemen dalam:
jumlah += unsur ** 2
jumlah cetak

Python Berfungsi

data = [...]
persegi = lambda x: x**2
jumlah = lambda x,y: x+y
cetak kurangkan(jumlah, peta(persegi, data))

Kedua-dua contoh adalah dalam Python, walaupun saya tidak memasukkannya dalam senarai bahasa berfungsi. Ini bukan satu kemalangan, kerana bahasa yang berfungsi sepenuhnya adalah perkara yang agak khusus dan jarang digunakan. Bahasa berfungsi pertama ialah Lisp, tetapi ia tidak berfungsi sepenuhnya (membingungkan, bukan?). Bahasa berfungsi sepenuhnya digunakan untuk semua jenis aplikasi saintifik dan belum digunakan secara meluas.

Tetapi jika "fungsi" itu sendiri tidak meluas, idea-idea tertentu telah berhijrah daripadanya kepada skrip (dan bukan sahaja) bahasa pengaturcaraan.
Ternyata sama sekali tidak perlu menulis kod berfungsi sepenuhnya; cukup untuk menghiasi kod imperatif dengan unsur kod berfungsi.

Python sedang beraksi

Ternyata konsep FP dilaksanakan dalam Python dengan lebih elegan. Mari kita lihat mereka dengan lebih dekat.

?-kalkulus

Kalkulus Lambda ialah konsep matematik yang membayangkan bahawa fungsi boleh diambil sebagai hujah dan mengembalikan fungsi lain.
Fungsi sedemikian dipanggil fungsi perintah yang lebih tinggi. ?-calculus adalah berdasarkan dua operasi: aplikasi dan abstraksi.
Saya telah pun memberikan contoh permohonan dalam penyenaraian sebelum ini. Fungsi peta dan pengurangan ialah fungsi tertib tinggi yang sama yang "digunakan", atau digunakan, fungsi yang diluluskan sebagai hujah kepada setiap elemen senarai (untuk peta) atau setiap pasangan elemen senarai berturut-turut (untuk pengurangan).

Bagi abstraksi, ia adalah sebaliknya: fungsi mencipta fungsi baharu berdasarkan hujah mereka.

Abstraksi Lambda

def tambah(n):
pulangkan lambda x: x + n

menambah =

Di sini kami telah mencipta senarai fungsi, setiap satunya menambah nombor tertentu pada hujah.
Contoh kecil ini juga mengandungi beberapa definisi yang lebih menarik tentang pengaturcaraan berfungsi - penutupan dan kari.

Penutupan ialah takrifan fungsi yang bergantung pada keadaan dalaman fungsi lain. Dalam contoh kami ini ialah lambda x. Dengan teknik ini, kami melakukan sesuatu yang serupa dengan menggunakan pembolehubah global, hanya pada peringkat tempatan.

Membawa ialah transformasi fungsi yang mengambil sepasang argumen menjadi fungsi yang mengambil argumennya satu demi satu. Inilah yang kami lakukan dalam contoh, hanya kami yang berakhir dengan pelbagai fungsi sedemikian.

Dengan cara ini kita boleh menulis kod yang berfungsi bukan sahaja dengan pembolehubah, tetapi juga dengan fungsi, yang memberi kita beberapa lagi "darjah kebebasan".

Fungsi tulen dan penyusun malas

Fungsi imperatif boleh mengubah pembolehubah luaran (global), yang bermaksud bahawa fungsi boleh mengembalikan nilai yang berbeza untuk nilai argumen yang sama pada peringkat pelaksanaan program yang berbeza.

Kenyataan ini sama sekali tidak sesuai untuk paradigma fungsional. Di sini, fungsi dilihat sebagai matematik, hanya bergantung pada hujah dan fungsi lain, itulah sebabnya ia digelar "fungsi tulen."

Seperti yang telah kami ketahui, dalam paradigma berfungsi anda boleh menguruskan fungsi mengikut kehendak anda. Tetapi kami mendapat faedah yang paling banyak apabila kami menulis "fungsi tulen". Fungsi tulen ialah fungsi tanpa kesan sampingan, yang bermaksud ia tidak bergantung pada persekitarannya dan tidak mengubah keadaannya.

Menggunakan fungsi tulen memberi kita beberapa kelebihan:

  • Pertama, jika fungsi tidak bergantung pada pembolehubah persekitaran, maka kami mengurangkan bilangan ralat yang berkaitan dengan nilai yang tidak diingini bagi pembolehubah yang sama ini. Bersama-sama dengan bilangan ralat, kami juga mengurangkan masa yang diperlukan untuk menyahpepijat atur cara dan lebih mudah untuk menyahpepijat fungsi tersebut.
  • Kedua, jika fungsinya bebas, maka pengkompil mempunyai ruang untuk berkeliaran. Jika fungsi bergantung hanya pada hujah, maka ia hanya boleh dinilai sekali. Lain kali anda boleh menggunakan nilai cache. Selain itu, jika fungsi tidak bergantung antara satu sama lain, ia boleh ditukar dan juga secara automatik selari.

Untuk meningkatkan prestasi, FP juga menggunakan penilaian malas. Contoh yang menarik:

panjang cetakan()

Secara teori, kita harus mendapatkan pembahagian dengan ralat sifar pada output. Tetapi pengkompil Python yang malas tidak akan mengira nilai setiap elemen senarai, kerana ia tidak diminta berbuat demikian. Perlu panjang senarai - sila!
Prinsip yang sama digunakan untuk konstruk bahasa lain.

Akibatnya, bukan sahaja pengaturcara, tetapi juga pengkompil menerima beberapa "darjah kebebasan".

Senaraikan ungkapan dan pernyataan bersyarat

Supaya kehidupan (dan pengaturcaraan) tidak kelihatan seperti madu kepada anda, pembangun Python menghasilkan sintaks "pemanis" khas, yang oleh borjuasi panggil "gula sintaksis".
Ia membolehkan anda menyingkirkan kenyataan dan gelung bersyarat... baik, jika tidak menyingkirkannya, maka sudah tentu mengurangkannya ke tahap minimum.

Pada dasarnya, anda sudah melihatnya dalam contoh sebelumnya - ia menambah = . Di sini kami segera mencipta dan memulakan senarai dengan nilai fungsi. Mudah, bukan?
Terdapat juga perkara seperti operator dan dan atau, yang membolehkan anda melakukan tanpa binaan yang menyusahkan seperti if-elif-else.

Oleh itu, menggunakan kit alat Python, anda boleh menukar sekeping kod penting yang menyusahkan menjadi kod berfungsi yang cantik.

Kod imperatif

L=
untuk x dalam xrange(10):
jika x % 2 == 0:
jika x**2>=50:
L.tambah(x)
lain:
L.tambah(-x)
cetak L

Kod fungsi

cetak

Keputusan

Seperti yang anda sudah faham, tidak perlu mengikuti sepenuhnya paradigma berfungsi; cukup untuk menggunakannya dengan mahir dalam kombinasi dengan yang penting untuk memudahkan hidup anda. Walau bagaimanapun, saya terus bercakap tentang paradigma imperatif... dan tidak berkata apa-apa tentang OOP dan FP.

Sebenarnya, OOP adalah superstruktur di atas paradigma imperatif, dan jika anda telah berpindah dari IP ke OOP, maka langkah seterusnya ialah menggunakan FP dalam OOP. Sebagai kesimpulan, saya akan mengatakan beberapa perkataan tentang tahap abstraksi. Jadi, lebih tinggi ia, lebih baik, dan gabungan OOP dan FPlah yang memberikan kita tahap ini.

CD

Pada cakera saya meletakkan pengedaran Python baru untuk pengguna Windows. Orang Linux tidak memerlukan bantuan :).

WWW

Beberapa sumber yang baik untuk mereka yang ingin mengetahui lebih lanjut:

INFO

Jika anda tidak menyukai Python, jangan risau - anda boleh berjaya menggunakan idea pengaturcaraan berfungsi dalam bahasa peringkat tinggi yang lain.

Terdapat beberapa paradigma dalam pengaturcaraan, contohnya, OOP, berfungsi, imperatif, logik, dan banyak daripada mereka. Kami akan bercakap tentang pengaturcaraan berfungsi.

Prasyarat untuk pengaturcaraan berfungsi sepenuhnya dalam Python ialah: fungsi peringkat tinggi, alat pemprosesan senarai yang dibangunkan, rekursi dan keupayaan untuk mengatur pengiraan malas.

Hari ini kita akan berkenalan dengan elemen mudah, dan reka bentuk yang kompleks akan berada dalam pelajaran lain.

Teori dalam teori

Seperti OOP dan pengaturcaraan berfungsi, kami cuba mengelakkan definisi. Namun, sukar untuk memberikan definisi yang jelas, jadi tidak akan ada definisi yang jelas di sini. Walau bagaimanapun! Mari kita serlahkan hasrat untuk bahasa berfungsi:

  • Fungsi Pesanan Tinggi
  • Fungsi tulen
  • Data tidak berubah

Ini bukan senarai lengkap, tetapi ini pun sudah cukup untuk menjadikannya "cantik". Jika pembaca mahukan lebih banyak lagi, berikut ialah senarai yang diperluaskan:

  • Fungsi Pesanan Tinggi
  • Fungsi tulen
  • Data tidak berubah
  • Penutupan
  • Kemalasan
  • Rekursi ekor
  • Jenis data algebra
  • Padanan corak

Mari kita pertimbangkan secara beransur-ansur semua perkara ini dan cara menggunakannya dalam Python.

Dan hari ini, secara ringkas, apakah yang terdapat dalam senarai pertama.

Fungsi tulen

Fungsi tulen tidak menghasilkan kesan sampingan yang boleh diperhatikan, hanya mengembalikan hasilnya. Mereka tidak mengubah pembolehubah global, tidak menghantar atau mencetak apa-apa di mana-mana, tidak menyentuh objek, dan sebagainya. Mereka menerima data, mengira sesuatu, hanya mengambil kira hujah, dan mengembalikan data baharu.

  • Lebih mudah untuk membaca dan memahami kod
  • Lebih mudah untuk diuji (tidak perlu membuat "syarat")
  • Lebih dipercayai kerana mereka tidak bergantung pada "cuaca" dan keadaan persekitaran, hanya pada hujah
  • Boleh dijalankan secara selari, keputusan boleh dicache

Data tidak berubah

Struktur data tidak berubah ialah koleksi yang tidak boleh diubah. Hampir seperti nombor. Nombor itu hanya wujud, ia tidak boleh diubah. Begitu juga, tatasusunan tidak berubah adalah cara ia dicipta dan akan sentiasa seperti itu. Jika anda perlu menambah elemen, anda perlu mencipta tatasusunan baharu.

Kelebihan struktur tidak berubah:

  • Kongsi rujukan dengan selamat antara benang
  • Mudah untuk diuji
  • Mudah untuk menjejaki kitaran hayat (sepadan dengan aliran data)

Fungsi Pesanan Tinggi

Fungsi yang mengambil fungsi lain sebagai hujah dan/atau mengembalikan fungsi lain dipanggil fungsi pesanan yang lebih tinggi:

Def f(x): pulangkan x + 3 def g(fungsi, x): kembalikan fungsi(x) * fungsi(x) cetak(g(f, 7))

Setelah mempertimbangkan teori, mari kita mulakan untuk berlatih, dari yang mudah kepada yang kompleks.

Senaraikan kemasukan atau penjana senarai

Mari lihat satu reka bentuk bahasa yang akan membantu mengurangkan bilangan baris kod. Ia bukan perkara biasa untuk menentukan tahap pengaturcara Python menggunakan konstruk ini.

Contoh kod:

Untuk x dalam xrange(5, 10): jika x % 2 == 0: x =* 2 else: x += 1

Kitaran dengan keadaan seperti ini bukan perkara biasa. Sekarang mari cuba ubah 5 baris ini menjadi satu:

>>>

Tidak buruk, 5 baris atau 1. Selain itu, ekspresif telah meningkat dan kod sedemikian lebih mudah difahami - satu komen boleh ditambah untuk berjaga-jaga.

Secara umum, reka bentuk ini adalah seperti berikut:

Perlu difahami bahawa jika kod itu tidak boleh dibaca sama sekali, maka lebih baik untuk meninggalkan reka bentuk sedemikian.

Fungsi tanpa nama atau lambda

Kami terus mengurangkan jumlah kod.

Def calc(x, y): kembalikan x**2 + y**2

Fungsi ini pendek, tetapi sekurang-kurangnya 2 baris telah dibazirkan. Adakah mungkin untuk memendekkan fungsi kecil itu? Atau mungkin tidak memformatnya sebagai fungsi? Lagipun, anda tidak selalu mahu mencipta fungsi yang tidak perlu dalam modul. Dan jika fungsi itu mengambil satu baris, maka lebih-lebih lagi. Oleh itu, dalam bahasa pengaturcaraan terdapat fungsi tanpa nama yang tidak mempunyai nama.

Fungsi tanpa nama dalam Python dilaksanakan menggunakan kalkulus lambda dan kelihatan seperti ungkapan lambda:

>>> lambda x, y: x**2 + y**2 di 0x7fb6e34ce5f0>

Untuk pengaturcara, ini adalah fungsi yang sama dan anda juga boleh bekerja dengannya.

Untuk mengakses fungsi tanpa nama beberapa kali, kami menetapkannya kepada pembolehubah dan menggunakannya untuk kelebihan kami.

>>> (lambda x, y: x**2 + y**2)(1, 4) 17 >>> >>> func = lambda x, y: x**2 + y**2 >>> func(1, 4) 17

Fungsi Lambda boleh bertindak sebagai hujah. Malah untuk lambda lain:

Pengganda = lambda n: lambda k: n*k

Menggunakan lambda

Kami belajar cara mencipta fungsi tanpa nama, tetapi kini kami akan mengetahui tempat untuk menggunakannya. Pustaka standard menyediakan beberapa fungsi yang boleh mengambil fungsi sebagai hujah - map(), filter(), reduce(), apply().

peta()

Fungsi map() memproses satu atau lebih jujukan menggunakan fungsi yang diberikan.

>>> senarai1 = >>> senarai2 = [-1, 1, -5, 4, 6] >>> senarai(peta(lambda x, y: x*y, senarai1, senarai2)) [-7, 2, -15, 40, 72]

Kami telah pun berkenalan dengan penjana senarai, mari gunakannya jika panjang senarai itu sama):

>>> [-7, 2, -15, 40, 72]

Jadi, adalah ketara bahawa menggunakan kemasukan senarai adalah lebih pendek, tetapi lambdas lebih fleksibel. Mari pergi lebih jauh.

penapis()

Fungsi penapis() membolehkan anda menapis nilai jujukan. Senarai yang terhasil hanya mengandungi nilai-nilai yang mana nilai fungsi untuk elemen adalah benar:

>>> nombor = >>> senarai(penapis(lambda x: x< 5, numbers)) # В результат попадают только те элементы x, для которых x < 5 истинно

Perkara yang sama dengan ungkapan senarai:

>>> nombor = >>>

mengurangkan()

Untuk mengatur pengiraan rantaian dalam senarai, anda boleh menggunakan fungsi reduce(). Sebagai contoh, hasil darab unsur-unsur senarai boleh dikira seperti ini (Python 2):

>>> nombor = >>> kurangkan(lambda res, x: res*x, nombor, 1) 720

Pengiraan berlaku dalam susunan berikut:

((((1*2)*3)*4)*5)*6

Rantaian panggilan dipautkan menggunakan hasil perantaraan (res). Jika senarai itu kosong, parameter ketiga hanya digunakan (dalam kes produk dengan faktor sifar, ini ialah 1):

>>> kurangkan(lambda res, x: res*x, , 1) 1

Sudah tentu keputusan pertengahan tidak semestinya nombor. Ini boleh menjadi mana-mana jenis data lain, termasuk senarai. Contoh berikut menunjukkan kebalikan senarai:

>>> kurangkan(lambda res, x: [x]+res, , )

Python mempunyai fungsi terbina dalam untuk operasi yang paling biasa:

>>> nombor = >>> jumlah(nombor) 15 >>> senarai(terbalik(nombor))

Python 3 tidak mempunyai fungsi reduce() terbina dalam, tetapi ia boleh didapati dalam modul functools.

memohon ()

Fungsi untuk menggunakan fungsi lain pada argumen kedudukan dan diberi nama masing-masing diberikan senarai dan kamus (Python 2):

>>> def f(x, y, z, a=Tiada, b=Tiada): ... cetak x, y, z, a, b ... >>> guna(f, , ("a": 4, "b": 5)) 1 2 3 4 5

Dalam Python 3, anda harus menggunakan sintaks khas dan bukannya fungsi apply():

>>> def f(x, y, z, a=Tiada, b=Tiada): ... print(x, y, z, a, b) ... >>> f(*, **(" a": 4, "b": 5)) 1 2 3 4 5

Mari selesaikan semakan dengan fungsi terbina dalam ini. perpustakaan standard dan mari kita beralih kepada pendekatan berfungsi terakhir untuk hari ini.

Penutupan

Fungsi yang ditakrifkan di dalam fungsi lain ialah penutupan. Mengapa ini perlu? Mari lihat contoh untuk menerangkan:

Kod (fiksyen):

Pemprosesan def(elemen, type_filter, all_data_size): penapis = Penapis(all_data_size, type_filter).get_all() untuk penapisan dalam penapis: elemen = filt.filter(elemen) def main(): data = DataStorage().get_all_data() untuk x dalam data: pemprosesan(x, "semua", len(data))

Perkara yang anda boleh perhatikan dalam kod: dalam kod ini terdapat pembolehubah yang pada asasnya hidup secara kekal (iaitu, serupa), tetapi pada masa yang sama kami memuatkan atau memulakan beberapa kali. Hasilnya, kami mendapat pemahaman bahawa permulaan pembolehubah mengambil bahagian terbesar masa dalam proses ini; ia berlaku malah memuatkan pembolehubah ke dalam skop mengurangkan prestasi. Untuk mengurangkan penutupan penggunaan overhed.

Penutupan memulakan pembolehubah sekali, yang kemudiannya boleh digunakan tanpa overhed.

Mari belajar cara membuat penutupan:

Def multiplier(n): "multiplier(n) mengembalikan fungsi yang mendarab dengan n" def mul(k): return n*k return mul # kesan yang sama boleh dicapai dengan # multiplier = lambda n: lambda k: n* k mul2 = multiplier(2) # mul2 - fungsi yang mendarab dengan 2, contohnya, mul2(5) == 10

Kesimpulan

Dalam pelajaran kami menyemak konsep asas FP, dan juga menyusun senarai mekanisme yang akan dibincangkan dalam pelajaran berikut. Kami bercakap tentang cara untuk mengurangkan jumlah kod, seperti kemasukan senarai (penjana senarai), fungsi lamda dan penggunaannya, dan akhirnya terdapat beberapa perkataan tentang penutupan dan untuk kegunaannya.