İşlevsel programlama dillerini Zorunlu yerine tersine açıklayan nedir?


17

Fonksiyonel programlamanın faydalarını anlatan birçok makalede, Haskell, ML, Scala veya Clojure gibi C / C ++ / C # / Java gibi zorunlu dillerden farklı olarak "bildirimsel diller" olarak adlandırılan fonksiyonel programlama dilleri gördüm. Benim sorum, fonksiyonel programlama dillerini zorunlu kılanın aksine açıklayıcı kılan şeydir.

Bildirici ve Zorunlu programlama arasındaki farkları açıklayan sık karşılaşılan bir açıklama, Zorunlu programlamada, bildirim dillerinde "Ne yapmalı" yerine bilgisayara "Bir şey nasıl yapılır" demenizdir. Bu açıklamada yaşadığım sorun, her ikisini de tüm programlama dillerinde yapmak. En alt düzey düzene inmiş olsanız bile, bilgisayara hala "Ne yapmalı" demenize rağmen, CPU'ya iki sayı eklemesini söylersiniz, eklemeyi nasıl yapacağınız konusunda talimat vermezsiniz. Spektrumun diğer ucuna gidersek, Haskell gibi üst düzey bir saf fonksiyonel dil, aslında bilgisayara belirli bir görevi nasıl başaracağını, bu, bilgisayarınızın tek başına nasıl başaracağını bilmediği belirli bir görevi başarmak için bir dizi talimattır. Haskell, Clojure gibi dillerin C / C ++ / C # / Java'dan açıkça daha yüksek olduğunu ve tembel değerlendirme, değişmez veri yapıları, anonim işlevler, curry, kalıcı veri yapıları vb. işlevsel programlama mümkün ve verimli, ancak bunları açıklayıcı diller olarak sınıflandırmam.

Benim için saf bir bildirim dilleri, yalnızca tamamen beyanlardan oluşan bir dil olacaktır, bu tür dillerin bir örneği CSS olacaktır (Evet, CSS'nin teknik olarak bir programlama dili olmayacağını biliyorum). CSS, yalnızca sayfanın HTML ve Javascript'i tarafından kullanılan stil bildirimlerini içerir. CSS, bildirim yapmaktan başka bir şey yapamaz, sınıf işlevleri oluşturamaz, yani bazı parametreye dayalı olarak görüntülenecek stili belirleyen işlevler, CSS komut dosyaları yürütemezsiniz, vb. programlama dili).

Güncelleme:

Son zamanlarda Prolog ile oynuyorum ve bana göre, Prolog tamamen bildirimsel bir dile en yakın programlama dilidir (En azından benim görüşüme göre), eğer sadece tamamen bildirici programlama dili değilse. Prolog'da ayrıntılı programlama yapmak, bir olguyu (belirli bir girdi için doğru döndüren bir yüklem işlevi) veya bir kuralı (girdilere dayalı olarak belirli bir koşul / desen için doğru döndüren bir yüklem işlevi) bildiren kurallar kullanılarak yapılır. bir desen eşleştirme tekniği kullanılarak tanımlanır. Prologda herhangi bir şey yapmak için, bir yüklemin bir veya daha fazla girişini bir değişkenle değiştirerek bilgi tabanını sorgularsınız ve prolog yüklemin başarılı olduğu değişken (ler) için değerleri bulmaya çalışır.

Demek istediğim, prologda zorunlu talimatlar yok, temelde bilgisayara bildiklerini anlatıyor (bildiriyor) ve sonra bilgi hakkında soruyor (sorguluyor). İşlevsel programlama dillerinde, bellek konumlarını doğrudan değiştirmiyorsanız veya adım adım hesaplama yazmasanız bile, hala bir değer veriyorsunuz, örneğin X işlevini çağırın ve buna 1 ekleyin, vb. Haskell, ML, Scala veya Clojure'da programlamanın bu anlamda beyan edici olduğunu söyleyemem, ancak yanlış olabilirim. Yukarıda tarif ettiğim anlamda uygun, doğru, saf fonksiyonel programlama beyanı var.


Ona ihtiyacınız olduğunda @JimmyHoffa nerede?

2
1. C # ve modern C ++ çok işlevsel dillerdir. 2. Benzer bir amaca ulaşmak için SQL ve Java yazmanın farkını düşünün (belki birleşimlerle karmaşık bir sorgu). Ayrıca C # LINQ nasıl basit foreach döngüler yazma farklı olduğunu düşünebilirsiniz ...
AK_

Ayrıca fark, açıkça tanımlanmış bir şeyden daha fazla bir stil meselesidir ... prolog kullanmıyorsanız ...
AK_

1
Farkı tanımanın anahtarlarından biri, bir atama işleci, bir tanım / bildirim işleci yerine bir atama işleci kullanmanızdır - örneğin Haskell'de atama işleci yoktur . Bu iki operatörün davranışı çok farklıdır ve sizi kodunuzu farklı şekilde tasarlamaya zorlar.
Jimmy Hoffa

1
Ne yazık ki görevlendirme operatörü olmadan bile bunu yapabilirim (let [x 1] (let [x (+ x 2)] (let [x (* x x)] x)))(Umarım bunu anladın, Clojure). Benim asıl sorum, bunu bu int'den farklı kılan şey, x = 1; x += 2; x *= x; return x;bence çoğunlukla aynı.
ALXGTV

Yanıtlar:


16

Bir şeyleri beyan etmekle bir makineyi öğretmek arasında bir çizgi çiziyor gibi görünüyorsunuz. Böyle sert ve hızlı bir ayrım yoktur. Zorunlu programlamada talimat verilen makinenin fiziksel donanım olması gerekmediğinden, yorum için çok fazla özgürlük vardır. Hemen hemen her şey doğru soyut makine için açık bir program olarak görülebilir. Örneğin, CSS'yi öncelikle seçicileri çözen ve bu şekilde seçilen DOM nesnelerinin niteliklerini ayarlayan bir makineyi programlamak için oldukça yüksek bir dil olarak görebilirsiniz.

Soru, böyle bir perspektifin mantıklı olup olmadığı ve tersine, talimat dizisinin hesaplanan sonucun bir deklarasyonuna ne kadar benzediğidir . CSS için, bildirimsel perspektif açıkça daha kullanışlıdır. C için zorunlu perspektif açıkça baskındır. Haskell gibi diller için…

Dil, somut anlambilim belirtmiştir. Yani, elbette, program bir operasyon zinciri olarak yorumlanabilir. İlkel operasyonları seçmek için fazla çaba harcamaz, böylece emtia donanımına güzel bir şekilde eşleşirler (STG makinesi ve diğer modeller budur).

Ancak, Haskell programlarının yazılma şekli, olabilir sonucunda bir açıklaması hesaplanmasını olarak makul okunmalıdır. Örneğin, ilk N faktörün toplamını hesaplayan programı ele alalım:

sum_of_fac n = sum [product [1..i] | i <- ..n]

Bunu desugar edebilir ve bir STG işlemleri dizisi olarak okuyabilirsiniz, ancak çok daha doğal olanı , sonucun bir açıklaması olarak okumaktır (yani, bilerek , “ne hesaplanacağı” dan daha bildirimsel programlamanın daha yararlı bir tanımıdır): Sonuç, [1..i]all i= 0,…, n için ürünlerin toplamıdır . Ve bu hemen hemen her C programından veya fonksiyonundan çok daha belirgindir.


1
Bu soruyu sormamın nedeni, gerçekte fonksiyonel programlama sadece başka bir üst düzey form olduğunda C / C ++ / C # / Java veya hatta Python paradigmalarından tamamen ayrılan bazı yeni farklı paradigmalar olarak işlevsel programlamayı teşvik etmeye çalıştığıdır. zorunlu programlama.
ALXGTV

Burada ne demek istediğimi açıklamak için sum_of_fac'ı tanımladığınızı ancak sum_of_fac'ın delik uygulamanızın yaptığı bu olmadığı sürece kendi başına işe yaramaz olduğunu açıklamak için, sonucu belirli bir göreve ulaşmak için oluşan başka bir sonucu hesaplamak için kullanmanız gerekir. programın çözmek için tasarlandığı görev.
ALXGTV

6
@ALXGTV Fonksiyonel programlama olduğunu (bundan daha paradigmaları programlama orada, çok geniş sınıflandırma) bile zorunluluk olarak okumaya üzereyiz taviz eğer, çok farklı bir paradigma. Ve bu kod üzerine inşa edilen diğer kod da deklaratif olarak okunabilir: Örneğin map (sum_of_fac . read) (lines fileContent),. Tabii ki bir noktada G / Ç devreye giriyor ama dediğim gibi bu bir süreklilik. Haskell programlarının bölümleri daha zorunludur, elbette, tıpkı C'nin bir sorta x + yload x; load y; add;

1
@ALXGTV Sezginiz doğrudur: "sadece" bildirim programlaması, bazı zorunlu ayrıntılar üzerinde daha yüksek bir soyutlama düzeyi sağlar. Bunun büyük bir anlaşma olduğunu iddia ediyorum!
Andres

1
@Brendan İşlevsel programlamanın zorunlu programlamanın bir alt kümesi olduğunu düşünmüyorum (değişmezlik de FP'nin zorunlu bir özelliği değildir). Saf, statik olarak yazılmış FP durumunda, efektlerin türlerde açık olduğu zorunlu bir programın bir üst kümesi olduğu söylenebilir. Haskell'in "dünyanın en iyi emir dili"
Andres F.

21

Zorunlu bir programın temel birimi ifadedir . İfadeler yan etkileri nedeniyle yürütülür. Aldıkları durumu değiştirirler. İfadeler dizisi, bir komutlar dizisidir ve bunu yapmayı ifade eder. Programcı hesaplamanın gerçek sırasını belirler. İnsanların bilgisayara nasıl yapılacağını söyleyerek kasteddikleri budur .

Bildirge programının temel birimi ifadedir . İfadelerin yan etkileri yoktur. Bir giriş ve çıkış arasında bir ilişki belirlerler, giriş durumlarını değiştirmek yerine girişlerinden yeni ve ayrı bir çıkış oluştururlar. Bir ifade dizisi, bazıları arasındaki ilişkiyi belirten bir ifade içermeden anlamsızdır. Programcı veriler arasındaki ilişkileri belirler ve program bu ilişkilerden hesaplamayı yapma sırasını belirler. İnsanların bilgisayara ne yapmaları gerektiğini söyleyerek kasteddikleri budur .

Zorunlu dillerin ifadeleri vardır, ancak işleri halletmenin başlıca yolları ifadelerdir. Benzer şekilde, bildirici diller, Haskell'in dogösterimi içindeki monad dizileri gibi ifadelere benzer anlambilime sahip bazı ifadelere sahiptir , ancak özlerinde büyük bir ifadedir. Bu, yeni başlayanların çok zorunlu görünen bir kod yazmasına izin verir, ancak dilin gerçek gücü o paradigmadan kaçtığınızda gelir.


1
Bir örnek içeriyorsa, bu mükemmel bir cevap olacaktır. Bildiğim kadarıyla en iyi örnek bildirim vs zorunluluk göstermek için Linq vs olduğunu foreach.
Robert Harvey

7

Bildirimi zorunlu programdan ayıran gerçek tanımlayıcı karakteristik, sıralı talimatlar vermediğiniz bildirim biçimindedir; daha düşük seviyede evet CPU bu şekilde çalışır, ancak bu derleyicinin endişe kaynağıdır.

CSS'nin "bildirici bir dil" olduğunu öneriyorsunuz, ben buna hiç dil demezdim. JSON, XML, CSV veya INI gibi bir veri yapısı formatıdır, sadece belirli bir yorumlayıcı tarafından bilinen verileri tanımlamak için bir formattır.

Atama operatörünü bir dilden çıkardığınızda bazı ilginç yan etkiler ortaya çıkar ve bu, tüm bu adım1-adım2-adım3 zorunlu talimatlarını bildirici dillerde kaybetmenin gerçek nedenidir.

Bir işlevdeki atama işleci ile ne yaparsınız? Ara adımlar oluşturursunuz. Bunun en önemli noktası, atama operatörü verileri adım adım değiştirmek için kullanılır. Artık verileri değiştiremez değiştirmez, tüm bu adımları kaybedersiniz ve aşağıdakilerle sonuçlanırsınız:

Her işlevin yalnızca bir deyimi vardır, bu deyim tek bir bildiridir

Şimdi tek bir ifadeyi birçok ifadeye benzetmenin birçok yolu var, ancak bu sadece hileli, örneğin: 1 + 3 + (2*4) + 8 - (7 / (8*3))bu açıkça tek bir ifadedir, ancak bunu şöyle yazarsanız ...

1 +
3 +
(
  2*4
) +
8 - 
(
  7 / (8*3)
)

Beynin yazarın istediği ayrışmayı tanıması daha kolay olan bir dizi işlemin ortaya çıkmasını üstlenebilir. Sık sık C # gibi kod ile bunu ..

people
  .Where(person => person.MiddleName != null)
  .Select(person => person.MiddleName)
  .Distinct();

Bu tek bir ifadedir, ancak birçok kişiye ayrıştırılmış gibi görünmektedir - ödev olmadığına dikkat edin.

Ayrıca, her iki yöntemde de - kodun gerçekte nasıl yürütüldüğünü hemen okuduğunuz sıraya göre değil; çünkü bu kod ne yapılacağını söylüyor, ama nasıl olduğunu dikte etmiyor . Yukarıdaki basit aritmetiğin açıkça soldan sağa yazıldığı sırada yürütülmeyeceğini ve yukarıdaki C # örneğinde bu 3 yöntemin aslında tamamen yeniden girildiğini ve sırayla tamamlanmayacağını unutmayın, derleyici aslında kod üretir bu ifadenin ne istediğini yapacak , ama sizin nasıl varsaydığınız değil.

Açıklayıcı kodlamanın bu ara-basamaksız yaklaşımına alışmanın gerçekten en zor kısmı olduğuna inanıyorum; ve Haskell neden bu kadar zor çünkü birkaç dil Haskell'in yaptığı gibi gerçekten izin vermiyor. Toplama gibi ara değişkenlerin yardımıyla yapacağınız bazı şeyleri başarmak için bazı ilginç jimnastik yapmaya başlamalısınız.

Bildirici bir dilde, bir ara değişkenin bir şey yapmasını istiyorsanız - bunu bir şeyi yapan bir işleve parametre olarak geçirmek anlamına gelir. Bu yüzden özyineleme bu kadar önemli hale gelir.

sum xs = (head xs) + sum (tail xs)

Bir resultSumdeğişken oluşturamazsınız ve döngü sırasında buna ekleyemezsiniz xs, sadece ilk değeri almanız ve toplamın her şeyden ne olursa olsun eklemeniz gerekir - ve diğer her şeye erişmek xsiçin bir işleve geçmek zorundasınız tailçünkü sadece xs için bir değişken oluşturamazsınız ve bu da zorunlu bir yaklaşım olacaktır. (Evet, yıkıcı kullanabileceğinizi biliyorum ama bu örnek açıklayıcı olacak)


Anladığım kadarıyla, cevabınızdan, saf fonksiyonel programlamada, tüm programın birçok tekli ifade fonksiyonundan oluşması, emir programlama fonksiyonlarında (prosedürler) tanımlanmış bir işlem sırasına sahip birden fazla ifade içermesidir
ALXGTV

1 + 3 + (2 * 4) + 8 - (7 / (8 * 3)) aslında çoklu ifadeler / ifadelerdir, elbette tek bir ifade olarak neye baktığınıza bağlıdır. Özetle fonksiyonel programlama temel olarak noktalı virgül kullanmadan programlamaktır.
ALXGTV

1
@ALXGTV bu bildirim ve zorunlu bir stil arasındaki fark, eğer bu matematiksel ifade ifadeler olsaydı , bunların yazılı olarak yürütülmesi zorunludur . Ancak bu, zorunlu ifadelerin bir sırası değil, ne istediğinizi tanımlayan bir ifade olduğu için, nasıl olduğunu söylemediği için, yazılı olarak yürütülmesi zorunlu değildir. Bu nedenle derleme ifadeleri azaltabilir, hatta bir değişmez olarak hatırlayabilir. Zorunlu diller de bunu yapar, ancak yalnızca tek bir ifadeye koyduğunuzda; bir deklarasyon.
Jimmy Hoffa

@ALXGTV Bildiriler ilişkileri tanımlar, ilişkiler biçimlendirilebilir; eğer x = 1 + 2öyleyse x = 3ve x = 4 - 1. Sıralı ifadeler talimatları tanımlar, böylece x = (y = 1; z = 2 + y; return z;)artık dövülebilir bir şeyiniz olmadığında, derleyicinin birden çok şekilde yapabileceği bir şey vardır - bunun yerine derleyicinin orada talimat verdiğiniz şeyi yapması zorunludur, çünkü derleyici talimatlarınızın tüm yan etkilerini bilemez, böylece ' t onları değiştirmeyin.
Jimmy Hoffa

Adil bir noktan var.
ALXGTV

6

Partiye geç kaldýđýmý biliyorum, ama geçen gün epifanim oldu bu yüzden gidiyor ...

Deklaratif ve zorunlu olan arasındaki farkı açıklarken veya deklaratif programlamanın ne anlama geldiğini açıklarken fonksiyonel, değişmez ve yan etkisi olmayan yorumların işareti kaçırdığını düşünüyorum. Ayrıca, sorunuzda belirttiğiniz gibi, bütün "ne yapmalı" vs "nasıl yapılır" çok belirsiz ve gerçekten de açıklamıyor.

Basit kodu a = b + ctemel alalım ve fikri edinmek için ifadeyi birkaç farklı dilde görüntüleyelim:

a = b + cC gibi zorunlu bir dilde yazdığımızda , değişkenin mevcut değerini ve daha fazlasını vermiyoruz. Ne olduğu hakkında temel bir açıklama yapmıyoruz . Aksine, bir süreçte sadece bir adım yürütüyoruz.b + caa

Biz yazarken a = b + cbildirge dilde Microsoft Excel gibi, (evet, Excel olduğunu biz edilir bir programlama dili ve hepsini muhtemelen en bildirim,) bir ilişki iddia arasında a, bve co durum her zaman böyle atoplamıdır diğer ikisi. Bu bir süreçteki adım değil, değişmez, garanti, doğruluk ilanı.

İşlevsel diller de açıklayıcıdır, ancak neredeyse kazaradır. Örneğin Haskel'de a = b + cdeğişmez bir ilişki olduğunu iddia ederler , ama sadece bve cdeğişmez oldukları için.

Bu nedenle evet, nesneler değiştirilemez ve işlevler yan etkisiz olduğunda, kod bildirimsel hale gelir (zorunlu kodla aynı görünse bile), ancak bunlar nokta değildir. Görevden kaçınmak da değildir. Deklaratif kodun amacı ilişkiler hakkında temel açıklamalar yapmaktır.


0

Bilgisayara ne yapacağını ve nasıl yapılacağını söylemek arasında net bir ayrım olmadığı konusunda haklısın .

Bununla birlikte, spektrumun bir tarafında neredeyse sadece hafızayı nasıl manipüle edeceğinizi düşünüyorsunuz. Yani, bir sorunu çözmek için, bilgisayara "bu bellek konumunu x olarak ayarlayın, sonra o bellek konumunu y olarak ayarlayın, bellek konumuna z ..." şeklinde bir formda sunuyorsunuz ve bir şekilde sonuç başka bir bellek konumunda.

Java, C # ve benzeri yönetilen dillerde artık donanım belleğine doğrudan erişiminiz yok. Zorunlu programcı şimdi tümü bellek konumları için bir dereceye kadar çıkarmalar olan statik değişkenler, referanslar veya sınıf örnekleri alanlarıyla ilgilenmektedir.

Haskell, OTOH gibi dillerde hafıza tamamen kayboluyor. Durum böyle değil

f a b = let y = sum [a..b] in y*y

a ve b argümanlarını tutan iki bellek hücresi ve ara sonucu y tutan başka bir hücre olmalıdır. Emin olmak için, bir derleyici arka ucu bu şekilde çalışan son kodu yayabilir (ve bir anlamda hedef mimari bir v. Neumann makinesi olduğu sürece bunu yapmalıdır).

Ancak mesele, yukarıdaki işlevi anlamak için v. Neumann mimarisini içselleştirmemize gerek yoktur. Çalıştırmak için çağdaş bir bilgisayara da ihtiyacımız yok. Saf bir FP dilinde programları, örneğin SKI hesabının temelinde çalışan varsayımsal bir makineye çevirmek kolay olurdu. Şimdi aynısını bir C programı ile deneyin!

Benim için saf bir bildirim dili, yalnızca tamamen beyanlardan oluşan bir dil olacaktır.

Bu yeterince güçlü değil, IMHO. Bir C programı bile sadece bir dizi açıklamadır. Beyanları daha da nitelendirmemiz gerektiğini hissediyorum. Örneğin, bir şey bize ne anlatıyorsun olan (bildirim) ya da ne yapar (zorunlu).


Fonksiyonel programlamanın C'deki programlamadan farklı olmadığını belirtmedim, aslında Haskell gibi dillerin C'den çok daha yüksek bir seviyede olduğunu ve çok daha fazla soyutlama sunduğunu söyledim.
ALXGTV
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.