İşlevsel programlama tam olarak nedir? Bu makale, kavramlarını ayrıntılı olarak açıklayacak ve Python'da işlevsel programlamanın nasıl kullanılacağını paylaşacaktır. Ana içerik, liste analizi ve diğer analiz türlerini içerir.
Fonksiyonel model
Zorunlu modelde, bir programı çalıştırmanın yolu, bilgisayara yürütmesi için bir dizi talimat vermektir. Bilgisayar, yürütme sırasında durumunu değiştirecektir. Örneğin, A'nın başlangıç değeri 5'tir ve A'nın değeri daha sonra değiştirilir. O halde A bir değişkendir ve değişkenin anlamı içerdiği değerin değişeceğidir.
İşlevsel modda, bilgisayara ne yapması gerektiğini söylemenize gerek yok, bilgisayara ne olduğunu söylüyorsunuz. Örneğin, bir sayının en büyük ortak böleni nedir, 1'den n'ye çarpımı vb.
Bu nedenle değişkenler değiştirilemez. Bir değişken bir kez ayarlandığında, her zaman aynı değeri korur (tamamen işlevsel bir dilde, değişkenler olarak adlandırılmadıklarına dikkat edin). Bu nedenle fonksiyonel modelde fonksiyonun hiçbir yan etkisi yoktur. Yan etkiler, işlevin işlevin dışındaki dünyada yaptığı değişikliklerdir. Bu Python kodunun aşağıdaki örneğine bir göz atın:
a = 3 def some_func (): küresel bir a = 5 some_func () baskı (a)Kodun çıktısı 5'tir. İşlevsel bir modelde, bir değişkenin değerini değiştirmek tamamen yasaktır ve bir işlevin işlevin dışındaki dünyayı etkilemesine izin verilmez. Bir işlevin yapabileceği tek şey, bazı hesaplamalar yapmak ve bir değer döndürmektir.
Şöyle düşünebilirsiniz: "Değişken yok ve yan etki yok? Bunun ne faydası var?" Güzel soru.
İşlev aynı parametrelerle iki kez çağrılırsa, aynı sonucu döndüreceğini garanti edebiliriz. Matematiksel fonksiyonları incelediyseniz, bunun iyi olduğunu bilmelisiniz. Buna referans şeffaflık denir. Fonksiyonların hiçbir yan etkisi olmadığından, bir şeyi hesaplama sürecini hızlandırabiliriz. Örneğin, program func (2) 'nin 3 döndürdüğünü biliyorsa, bu değeri tabloya kaydedebilir, böylece sonucu zaten bildiğimiz işlevi tekrar tekrar çalıştırmaya gerek kalmaz.
Genel olarak, işlevsel programlama döngüleri değil, özyinelemeyi kullanır. Özyineleme matematiksel bir kavramdır ve genellikle "sonucu kişinin girdisi olarak almak" anlamına gelir. Özyinelemeli işlevleri kullanarak, işlevler kendilerini tekrar tekrar çağırabilir. Python'da tanımlanan özyinelemeli bir işlevin kullanımına bir örnek:
def factorial_recursive (n): # Temel durum: 1! = 1 n == 1 ise: dönüş 1 # Özyinelemeli durum: n! = N * (n-1)! Başka: return n * factorial_recursive (n-1)Fonksiyonel programlama dilleri de tembeldir. Tembellik, son ana gelmedikçe hesap yapmayacakları veya hiçbir şey yapmayacakları anlamına gelir. Kod 2 + 2'nin hesaplanmasını gerektiriyorsa, fonksiyonel program yalnızca hesaplama sonucu gerçekten kullanıldığında hesaplama yapacaktır. Python'da bu tembelliği kısaca tanıtacağız.
Haritalama
Bir haritayı anlamak için, önce yinelenebilir bir nesnenin ne olduğunu anlamanız gerekir. Yinelenebilir nesne (yinelenebilir), yinelenebilen herhangi bir şeyi ifade eder. Genellikle listeler veya dizilerdir, ancak Python'un birçok yinelenebilir nesnesi vardır. Hatta nesneyi özelleştirebilir ve belirli sihirli yöntemleri uygulayarak onu yinelenebilir bir nesne haline getirebilirsiniz. Sihirli yöntemler, nesneleri daha Pythonic yapabilen API'ler gibidir. Bir nesneyi yinelenebilir bir nesne yapmak için aşağıdaki iki sihirli yöntemi uygulamanız gerekir:
sınıf Sayacı: def __init __ (öz, düşük, yüksek): # sihirli yöntem içinde sınıf özniteliklerini ayarlayın __init__ # "inistalise" için self.current = düşük self.high = yüksek def __iter __ (kendisi): # Bu nesneyi yinelenebilir hale getirmek için ilk sihirli yöntem kendine dön def __sonraki __ (kendi): # ikinci sihirli yöntem self.current ise > self.high: StopIteration'ı yükselt Başka: self.current + = 1 dönüş self.current-1İlk sihirli yöntem "__iter__" (çift alt çizgi yineleme) genellikle döngünün başında çağrılan yineleyiciyi döndürür. __next__, yinelemenin sonraki nesnesini döndürür.
Komut satırını açıp aşağıdaki kodu deneyebilirsiniz:
Sayaçta c için (3, 8): baskı (c)Bu kodun çıktısı:
34 56 78Python'da yineleyici, yalnızca __iter__ sihirli yöntemini uygulayan bir nesnedir. Başka bir deyişle, nesnenin içerdiği konumlara erişebilirsiniz, ancak tüm nesneyi geçemezsiniz. Bazı nesneler __next__ sihirli yöntemini uygular, ancak koleksiyonlar gibi __iter__ sihirli yöntemini uygulamaz (bu makalenin sonraki bölümlerinde açıklanmıştır). Bu makalede, ilgili tüm nesnelerin yinelenebilir nesneler olduğunu varsayıyoruz.
Artık yinelenebilir nesnenin ne olduğunu bildiğimize göre, geri dönelim ve eşleme işlevini tartışalım. Eşleme, yinelenebilir nesnedeki her öğe üzerinde belirli bir işlevi gerçekleştirebilir. Genellikle listedeki her öğe için bir işlev gerçekleştiririz, ancak eşlemenin aslında çoğu yinelenebilir nesne için kullanılabileceğini bilmeliyiz.
harita (işlev, yinelenebilir)Aşağıdaki sayılardan oluşan bir liste olduğunu varsayalım:
Her sayının karesini elde etmek istiyoruz, böylece kod şu şekilde yazılabilir:
x = def kare (num): dönüş num * num baskı (liste (harita (kare, x)))Python'daki işlevsel işlevler tembeldir. "List ()" eklemezsek, işlev sonuç listesini değil, yalnızca yinelenebilir nesneyi kaydeder. Sonucu almak için Python'a açıkça "onu bir listeye dönüştür" dememiz gerekir.
Python'da tembel olmayan bir işlev değerlendirmesinden tembel bir işleve geçmek biraz uygunsuz görünüyor. Ancak prosedürel düşünme yerine işlevsel düşünceyi kullanabilirseniz, sonunda uyum sağlamış olursunuz.
Bu "kare (num)" gerçekten çok iyi, ama her zaman biraz yanılmışımdır. Haritayı yalnızca bir kez kullanmak için tüm işlevi tanımlamak gerekli midir? Aslında lambda işlevlerini (anonim işlevler) kullanabiliriz.
Lambda ifadesi
Lambda ifadeleri, yalnızca tek satırlı işlevlerdir. Örneğin, aşağıdaki lambda ifadesi belirli bir sayının karesini bulabilir:
kare = lambda x: x * xAşağıdaki kodu çalıştırın:
> > > kare (3) 9Şu soruyu sormalısınız: "Parametreler nerede? Bu ne anlama geliyor? Hiç bir işleve benzemiyor?"
Aslında anlaşılması kolay değil ... ama anlaşılır olmalı. Yukarıdaki kodumuz "kare" değişkenine bir şey atar. Bu şey mi:
lambda x:Python'a bunun bir lambda işlevi olduğunu ve giriş adının x olduğunu söyler. İki nokta üst üste işaretinden sonraki her şey girişte bir işlemdir ve daha sonra otomatik olarak işlemin sonucunu döndürür.
Dolayısıyla, kare kodumuz tek satırda basitleştirilebilir:
x = baskı (liste (harita (lambda num: num * num, x)))Lambda ifadeleri ile tüm parametreler sola, işlemler sağ tarafa yerleştirilir. Biraz dağınık görünmesine rağmen rolü inkar edilemez. Aslında, yalnızca işlevsel programlamayı anlayan kişilerin anlayabileceği bir kod yazabilmek biraz heyecan verici. Ve işlevi tek satıra dönüştürmek de çok güzel.
indüksiyon
Azaltma, yinelenebilir bir nesneyi bir şeye dönüştüren bir işlevdir. Genellikle, listede hesaplamalar yaparız ve listeyi bir sayıya indiririz. Özetlenmiş kod şuna benzer:
azalt (işlev, liste)Yukarıdaki işlev lambda ifadelerini kullanabilir.
Listenin ürünü, tüm sayıları çarpmaktır. Şu şekilde kod yazabilirsiniz:
ürün = 1 x = x içindeki num için: ürün = ürün * numAncak tümevarım kullanarak şu şekilde yazılabilir:
functools'dan ithalat azalt ürün = azalt ((lambda x, y: x * y),)Bu aynı sonucu alabilir. Bu kod daha kısadır ve işlevsel programlama yardımıyla bu kod daha özlüdür.
filtre
Filtre işlevi yinelenebilir bir nesne alır ve ardından nesnede ihtiyaç duyulmayan her şeyi filtreler.
Genellikle filtreleme, bir işlev ve bir liste alır. Listedeki her öğe için bir işlev yürütür ve işlev True döndürürse hiçbir şey yapmaz. İşlev False döndürürse, bu öğeyi listeden kaldırın.
Sözdizimi aşağıdaki gibidir:
filtre (işlev, liste)Basit bir örneğe bakalım. Filtreleme yapılmadan kod şu şekilde yazılmalıdır:
x = aralık (-5, 5) new_list = x içindeki num için: eğer numara < 0: new_list.append (num)Kullanım filtresi şu şekilde yazılabilir:
x = aralık (-5, 5) all_less_than_zero = list (filtre (lambda num: num < 0, x))Daha yüksek sipariş işlevi
Daha yüksek sıralı işlevler, parametre olarak bir işlev alır ve başka bir işlev döndürür. Aşağıda çok basit bir örnek gösterilmektedir:
def toplamı (sayılar): dönüş toplamı (sayılar) def eylemi (func, sayılar): dönüş işlevi (sayılar) baskı (eylem (toplama,)) # Çıkış 6'dırVeya daha basit bir "dönüş işlevi" örneği:
def rtnBrandon (): "brandon" u döndür def rtnJohn (): "john" a dön def rtnPerson (): age = int (input ("Yaşınız kaç?")) yaş == 21 ise: dönüş rtnBrandon () Başka: dönüş rtnJohn ()İşlevsel programlama dillerinin değişkenleri olmadığını söylemeden önce hatırlıyor musunuz? Aslında, üst düzey işlevler bunu kolayca yapabilir. Verileri yalnızca bir dizi işlevde geçirmeniz gerekiyorsa, verilerin bir değişkende saklanması gerekmez.
Python'daki tüm işlevler üst düzey nesnelerdir. Üst düzey nesneler, aşağıdaki özelliklerden bir veya daha fazlasına sahip nesnelerdir:
Bu nedenle, Python'daki tüm işlevler nesnelerdir ve üst düzey işlevler olarak kullanılabilir.
Kısmi işlev
Bazı işlevlerin anlaşılması biraz zor ama çok güzel. Bununla birlikte, tam parametreler sağlamadan fonksiyonları çağırabilirsiniz. Bir örneğe bakalım. İki parametreyi kabul eden bir fonksiyon oluşturacağız, biri taban, diğeri üs ve ardından tabanı üs kuvvetine döndürüyor.Kod aşağıdaki gibidir:
def gücü (taban, üs): dönüş tabanı ** üsŞimdi, şöyle yazılabilen bir kareleme fonksiyonuna ihtiyacımız var:
def kare (taban): dönüş gücü (baz, 2)Bu kod iyi, ama ya bir küp işlevine ihtiyacınız varsa? Veya dördüncü güç işlevi? Her zaman yeni işlevler tanımlamam gerekiyor mu? Sorun değil, ancak programcılar her zaman tembeldir. Bir şeyin sık sık tekrarlanması gerekiyorsa, bu, hızı artırmanın ve tekrardan kaçınmanın bir yolu olması gerektiği anlamına gelir. Bunu kısmi fonksiyonlarla başarabiliriz. Aşağıda, kısmi işlevlerin kullanıldığı bir kare alma örneği verilmiştir:
functools'dan kısmi içe aktarma kare = kısmi (kuvvet, üs = 2) baskı (kare (2)) # çıktı 4'türBu acı mı? Python'a ikinci parametreyi önceden söyleriz, böylece iki parametre gerektiren bir işlevi çağırmak için yalnızca bir parametreye ihtiyaç duyulur.
1000'inci güce kadar tüm fonksiyonları oluşturmak için döngüleri de kullanabilirsiniz.
functools'dan kısmi içe aktarma güçler = aralıktaki x için (2, 1001): powers.append (kısmi (üs, üs = x)) baskı (güçler (3)) # çıktı 9'durFonksiyonel programlama yeterli değil Python
Buradaki işlevsel programlamamızın çoğunun listeleri kullandığını fark etmiş olabilirsiniz. Tümevarım ve kısmi işlevler dışında, diğer tüm işlevler listeleri oluşturur. Guido (Python'un mucidi) Python'da işlevsel şeyler kullanmaktan hoşlanmaz, çünkü Python'un kendi listeleri oluşturma yolu vardır.
Python IDLE'de "bunu içe aktar" ı tıklayın, aşağıdakileri görebilirsiniz:
> > > bunu içe aktar Python Zen, Tim Peters tarafından Güzel, çirkin olmaktan iyidir. Açık, örtük olmaktan daha iyidir. Basit, karmaşıktan daha iyidir. Karmaşık, karmaşık olmaktan daha iyidir. Düz, iç içe olmaktan daha iyidir. Seyrek yoğun olandan daha iyidir. Okunabilirlik önemlidir. Özel durumlar, kuralları çiğnemeye yetecek kadar özel değildir. Pratiklik saflığı yense de. Hatalar asla sessizce geçmemelidir. Açıkça susturulmadıkça. Belirsizlik karşısında, tahmin etme cazibesini reddedin. Bunu yapmanın bir - ve tercihen tek - bariz bir yolu olmalıdır. Yine de Hollandalı değilseniz bu yol ilk bakışta açık olmayabilir. Şimdi hiç olmadığı kadar iyi. Yine de hiçbir zaman şu anda * şu andan * daha iyi değildir. Uygulamanın açıklanması zorsa, bu kötü bir fikirdir. Uygulamanın açıklanması kolaysa, iyi bir fikir olabilir. Ad alanları korna çalmak için harika bir fikir - hadi bunlardan daha fazlasını yapalım!Bu Python'un Zenidir. Bu şiir Python stili denen şeyi gösterir. Belirtmek istediğimiz şey şu cümle:
Bunu yapmanın bir - ve tercihen tek - bariz bir yolu olmalıdır.
(Her şeye tek ve tek bir çözüm olmalıdır.)
Python'da, haritalama ve filtrelemenin yapabilecekleri liste anlamaları ile yapılabilir (daha sonra açıklanacaktır). Bu, Python Zen'i bozar, bu yüzden işlevsel programlamanın bir kısmının yeterince "Python" olmadığını söylüyoruz.
Sıklıkla bahsedilen diğer bir yer lambda'dır. Python'da lambda işlevi yalnızca sıradan bir işlevdir. Lambda sadece sözdizimsel şekerdir. İkisi eşdeğerdir:
foo = lambda a: 2 def foo (a): dönüş 2Sıradan işlevler lambda'nın yapabildiği her şeyi yapabilir, ancak tam tersi olamaz. Lambda, sıradan işlevlerin yapabildiği her şeyi yapamaz.
İşlevsel programlamanın neden Python ekosistemi için uygun olmadığı hakkında bir tartışma yapıldı. Daha önce liste anlamalarından bahsettiğimi fark etmiş olabilirsiniz, şimdi liste anlamanın ne olduğunu tanıtacağız.
Liste anlama
Daha önce haritalama veya filtreleme ile yapılabilecek her şey liste anlayışıyla yapılabilir demiştim. İşte öğreneceğimiz şey bu.
Liste anlayışları, Python'un listeleri oluşturma şeklidir. Sözdizimi aşağıdaki gibidir:
Listedeki her sayının karesini bulmak için şunları yazabilirsiniz:
Yazdır()Gördüğünüz gibi, listedeki her elemana bir işlev uyguladık. Peki filtrelemeye nasıl ulaşabiliriz? Önceki koda bir göz atalım:
x = aralık (-5, 5) all_less_than_zero = list (filtre (lambda num: num < 0, x)) baskı (all_less_than_zero)Aşağıdaki liste anlama yöntemine dönüştürülebilir:
x = aralık (-5, 5) all_less_than_zero =Bunun gibi, liste anlayışları if ifadelerini destekler. Yani başarmak için bir dizi fonksiyon yazmaya gerek yok. Aslında, bir tür liste oluşturmanız gerekiyorsa, liste anlayışlarını kullanmak muhtemelen daha uygun ve öz olacaktır.
Ya 0'dan küçük tüm sayıların karesini almak istiyorsanız? Lambda kullanılarak eşleme ve filtreleme şu şekilde yazılabilir:
x = aralık (-5, 5) all_less_than_zero = list (harita (lambda num: num * num, list (filtre (lambda num: num < 0, x))))Çok uzun ve biraz karmaşık görünüyor. Sadece liste anlayışını yazın:
x = aralık (-5, 5) all_less_than_zero =Ancak, liste anlamaları yalnızca listeler için kullanılabilir. Eşleme ve filtreleme, tüm yinelenebilir nesneler için kullanılabilir. Öyleyse neden liste anlamalarını kullanalım? Aslında, analitik formül herhangi bir yinelenebilir nesne üzerinde kullanılabilir.
Diğer analitik formüller
Yinelenebilir herhangi bir nesnede analitik ifadeler kullanabilirsiniz.
Yinelenebilir herhangi bir nesne analitik olarak oluşturulabilir. Python 2.7'den başlayarak, analitik olarak bir sözlük (karma tablo) bile oluşturulabilir.
# Luciano Ramalho tarafından Fluent Python'un 70. sayfasından 3. bölümden alınmıştır. DIAL_CODES = > > > country_code = {ülke: kod için kod, DIAL_CODES içindeki ülke} > > > ülke kodu {"Brezilya": 55, "Endonezya": 62, "Pakistan": 92, "Rusya": 7, "Çin": 86, "ABD": 1, "Japonya": 81, "Hindistan": 91, "Nijerya": 234, "Bangladeş": 880} > > > {kod: ülke için country.upper (), kod ise country_code.items () içindeki kod < 66} {1: "AMERİKA BİRLEŞİK DEVLETLERİ", 7: "RUSYA", 62: "ENDONEZYA", 55: "BREZİLYA"}Yinelenebilir bir nesne olduğu sürece analitik olarak üretilebilir. Bir koleksiyon örneğine bakalım. Setin ne olduğunu bilmiyorsanız, önce bu makaleyi (https://medium.com/brandons-computer-science-notes/a-primer-on-set-theory-746cd0b13d13) okuyabilirsiniz. Basit ifadeyle:
Gördüğünüz gibi küme, sözlükle aynı küme parantezlerini kullanıyor. Python çok akıllıdır. Bunun bir küme analizi mi yoksa bir sözlük analizi mi olduğunu belirlemek için parantezlerde ek değerler sağlayıp sağlamadığınızı kontrol edecektir. Analitik içerik hakkında daha fazla bilgi edinmek istiyorsanız, bu görsel kılavuzu inceleyebilirsiniz ( Analitik formüller ve oluşturucular hakkında daha fazla bilgi edinmek istiyorsanız, bu makaleyi okuyabilirsiniz (https://medium.freecodecamp.org/python-list-comprehensions-vs-generator-expressions-cef70ccb49db).
sonuç olarak
Fonksiyonel programlama güzel ve saftır. İşlevsel kod çok temiz olabilir, ancak karmaşık da olabilir. Bazı Python programcıları Python'da işlevsel modeller kullanmaktan hoşlanmaz, ancak işi tercihlerinize göre tamamlamak için en iyi aracı kullanmayı hatırlayabilirsiniz.
Orijinal: https://hackernoon.com/learn-functional-python-in-10-minutes-to-2d1651dece6f
Yazar: Brandon Skerritt, Liverpool Üniversitesi'nde bilgisayar bilimi öğrencisi, Hackernoon ve PoliticsMeansPolitics of muhabirler.
Çevirmen: Crescent Moon, Kurgu: Tu Min