Fonksiyonel programlamanın değişmezliği gerçekten var mı?


9

Günlük hayatımda bir programcı olarak çalışmam ve tüm moda dilleri (Python, Java, C, vb.) Kullanmama rağmen, işlevsel programlamanın ne olduğu konusunda net bir fikrim yok. Okuduğum kadarıyla, işlevsel dillerin bir özelliği veri yapılarının değişmez olmasıdır . Benim için bu tek başına pek çok soru getiriyor. Ama önce değişmezliği anladığımdan biraz yazacağım ve eğer yanlışsam, beni düzeltmekten çekinmeyin.

Değişmezlik anlayışım:

  • Bir program başladığında, sabit veri ile sabit veri yapılarına sahiptir
  • Bu yapılara yeni veri eklenemez
  • Kodda değişken yok
  • Yalnızca halihazırdaki verilerden veya halihazırda hesaplanan verilerden "kopyalayabilirsiniz"
  • Yukarıdakilerin hepsinden ötürü, değişmezlik bir programa büyük alan karmaşıklığı ekler

Sorularım:

  1. Veri yapılarının oldukları gibi kalması gerekiyorsa (değişmez), birisi listeye yeni bir öğe nasıl ekler?
  2. Yeni veri alamayan bir programa sahip olmanın anlamı nedir? Bilgisayarınıza, programa veri beslemek isteyen bir sensörünüz olduğunu varsayalım. Bu, gelen verileri hiçbir yerde saklayamayacağımız anlamına mı gelir?
  3. Bu durumda fonksiyonel programlama makine öğrenimi için nasıl iyidir? Makine öğrenimi, programın şeyleri "algılaması" nı güncelleme varsayımından kaynaklandığından yeni veriler depolar.

2
İşlevsel kodda değişken olmadığını söylediğinizde size katılmıyorum. Matematiksel anlamda “bir değer kümesinden herhangi birini kabul edebilecek bir miktar” değişkenleri vardır. Değişebilir değiller , elbette, ama ikisi de matematikte değiller.
Édouard

1
Sanırım karışıklığınız, işlevsel dilleri soyut olarak düşünüyor olmanız. Haskell'deki herhangi bir programı alın - örneğin konsoldan bir sayı listesi okuyan, hızlı bir şekilde sıralayan ve çıktı veren bir program - ve nasıl çalıştığını ve şüphelerinizi nasıl çürüttüğünü anlayan bir program. Felsefe kurmak yerine gerçek program örneklerine bakmadan işleri netleştirmenin bir yolu yoktur. Herhangi bir Haskell eğitiminde birçok program bulacaksınız.
jkff

@jkff Ne söylemeye çalışıyorsun? Haskel'in işlevsel olmayan özellikleri var. Soru Haskell değil, fonksiyonel programlama ile ilgili. Yoksa tüm bunların işlevsel olduğunu mu iddia ediyorsunuz? Nasıl? Dediğiniz gibi, felsefe ile ilgili neyin yanlış olması gerektiği. Soyutlama hangi açıdan kafa karıştırıcı? OP sorusu çok mantıklı.
babou

@babou Tamamen işlevsel bir programlama dilinin algoritmaları ve veri yapılarını nasıl verimli bir şekilde uygulayabileceğini anlamanın en iyi yolunun, işlevsel bir programlama dilinde etkili bir şekilde uygulanan algoritma ve veri yapılarına örneklere bakmak olduğunu söylemeye çalışıyorum. Bana öyle geliyor ki OP kavramsal olarak nasıl mümkün olduğunu anlamaya çalışıyor - bence bunu anlamanın en hızlı yolu ne kadar ayrıntılı olursa olsun kavramsal bir açıklama okumak yerine örneklere bakmaktır.
jkff

Fonksiyonel programlamaya bakmanın bir yolu, yan etkiler olmadan programlama olduğunu söylemektir. Bunu "moda" tercih ettiğiniz dilde yapabilirsiniz. Tüm yeniden atamalardan kaçınmayın: örneğin Java'da, tüm değişkenleriniz nihai olacak ve tüm yöntemleriniz salt okunur olacaktır.
reinierpost

Yanıtlar:


10

Bir program başladığında, sabit veri ile sabit veri yapılarına sahiptir

Bu biraz yanlış bir fikir. Sabit bir forma ve sabit bir yeniden yazma kurallarına sahiptir, ancak bu yeniden yazma kuralları çok daha büyük bir şeye patlayabilir. Örneğin Haskell'deki [1..100000000] ifadesi çok az miktarda kodla temsil edilir, ancak normal şekli büyüktür.

Bu yapılara yeni veri eklenemez

Evet ve hayır. Haskell veya ML gibi bir dilin tamamen işlevsel alt kümesi, dış dünyadan veri alamaz, ancak pratik programlama için herhangi bir dil, dış dünyadan tamamen işlevsel alt kümeye veri eklemek için bir mekanizmaya sahiptir. Haskell'de bu çok dikkatli bir şekilde yapılır, ancak ML'de bunu istediğiniz zaman yapabilirsiniz.

Kodda değişken yok

Bu hemen hemen doğrudur, ancak hiçbir şeyin isimlendirilemeyeceği fikriyle karıştırmayın. Her zaman yararlı ifadeleri adlandırır ve sürekli olarak yeniden kullanırsınız. Ayrıca hem ML hem de Haskell, denediğim her Lisp ve Scala gibi melezlerin hepsinin değişken yaratma araçları var. Sadece yaygın olarak kullanılmazlar. Ve yine bu tür dillerin tamamen işlevsel altkümelerinde yoktur.

Yalnızca halihazırdaki verilerden veya halihazırda hesaplanan verilerden "kopyalayabilirsiniz"

Normal forma indirgeyerek hesaplama yapabilirsiniz. Yapılacak en iyi şey, aslında hesaplamaları nasıl yaptıklarını görmek için işlevsel bir dilde program yazmaya gitmektir.

Örneğin "toplam [1..1000]" yapmak istediğim bir hesaplama değil, Haskell tarafından oldukça kullanışlı bir şekilde yapılıyor. Bize anlam ifade eden küçük bir ifade verdik ve Haskell bize karşılık gelen sayıyı verdi. Bu yüzden kesinlikle hesaplama yapar.

Veri yapılarının oldukları gibi kalması gerekiyorsa (değişmez), birisi listeye yeni bir öğe nasıl ekler?

Bir listeye yeni bir öğe eklemezsiniz, eskisinden yeni bir liste oluşturursunuz. Eskisi mutasyona uğratılamadığından, yeni listede veya başka bir yerde kullanmak tamamen güvenlidir. Bu şemada çok daha fazla veri güvenle paylaşılabilir.

Yeni veri alamayan bir programa sahip olmanın anlamı nedir? Bilgisayarınıza, programa veri beslemek isteyen bir sensörünüz olduğunu varsayalım. Bu, gelen verileri hiçbir yerde saklayamayacağımız anlamına mı gelir?

Kullanıcı girdisi söz konusu olduğunda, herhangi bir pratik programlama dilinin kullanıcı girişi almanın bir yolu vardır. Bu olur. Bununla birlikte, kodlarınızın çoğunu yazdığınız bu avantajların tamamen işlevsel bir alt kümesi vardır ve avantajları bu şekilde elde edersiniz.

Bu durumda fonksiyonel programlama makine öğrenimi için nasıl iyidir? Makine öğrenimi, programın şeyleri "algılaması" nı güncelleme varsayımından kaynaklandığından yeni veriler depolar.

Bu, aktif öğrenme için geçerli olurdu, ancak birlikte çalıştığım çoğu makine öğrenimi (bir makine öğrenme grubunda kod maymunu olarak çalışıyorum ve birkaç yıl boyunca yaptım) tüm eğitim verilerinin yüklendiği tek seferlik bir öğrenme sürecine sahip hemen. Ancak aktif öğrenme için işleri% 100 tamamen işlevsel olarak yapamazsınız. Dış dünyadan bazı verileri okumak zorunda kalacaksınız.


Alan sorunu olan @ Pithikos'un gönderisindeki tartışmasız en önemli noktayı rahatlıkla görmezden geldiğinizi hissediyorum - fonksiyonel programlar zorunlu olanlardan daha fazla alan kullanıyor (yerinde algoritmalar ve benzeri
yazamazsınız

2
Bu sadece doğru değil. Mutasyon eksikliği büyük ölçüde paylaşarak oluşur ve tüm bunlara ek olarak, bahsettiğiniz boyut farkı modern derleyicilerle her zaman küçüktür. Haskell'deki listelerdeki kodların çoğu etkin bir şekilde yerinde veya hiç bellek kullanmıyor.
Jake

1
Sanırım ML'yi biraz yanlış tanıttın. Evet, G / Ç herhangi bir yerde olabilir, ancak yeni bilgilerin mevcut yapılara girme şekli sıkı bir şekilde kontrol edilir.
dfeuer

@Pithikos, Her yerde değişkenler var; Édouard'ın belirttiği gibi, alışık olduğunuzdan farklıdırlar. Ve işler sürekli olarak tahsis ediliyor ve çöp toplanıyor. Fonksiyonel programlamaya girdikten sonra, aslında nasıl çalıştığına dair daha iyi bir fikir edinirsiniz.
dfeuer

1
Bilinen en iyi zorunluluk uygulamasıyla aynı zaman karmaşıklığına sahip tamamen işlevsel bir uygulamaya sahip olmayan algoritmalar olduğu doğrudur - örneğin Union-Find veri yapısı (ve, um, diziler :)) Uzay için de böyle durumlar olduğunu düşünüyorum. karmaşıklık. Ancak bunlar istisnalardır - en yaygın algoritmalar / veri yapıları, eşdeğer zaman ve alan karmaşıklığına sahip uygulamalara sahiptir. Bu, programlama stilinin ve derleyicinin (sabit bir faktöre) kalite öznel bir meselesidir.
jkff

4

Değişmezlik veya değişebilirlik fonksiyonel programlamada anlamlı bir kavram değildir.

Hesaplamalı bağlam

Bu son zamanlarda yapılan bir başka soruya ilginç bir takip (kopya değil) olan çok iyi bir soru: Atama, değerleme ve isim bağlama arasındaki fark nedir?

İfadelerinizi tek tek yanıtlamak yerine, burada size neyin tehlikede olduğuna dair yapılandırılmış bir genel bakış sunmaya çalışıyorum.

Size cevap vermek için dikkate alınması gereken birkaç konu vardır:

  • Bir hesaplama modeli nedir ve belirli bir model için hangi kavramların anlamlı olduğu

  • Kullandığınız kelimelerin anlamı nedir ve bağlama nasıl bağlıdır?

Fonksiyonel programlama tarzı aptalca görünüyor çünkü zorunlu bir programcı gözüyle görüyorsunuz. Fakat bu farklı bir paradigmadır ve zorunlu kavramlarınız ve algılamanız yabancıdır, yersizdir. Derleyicilerin böyle bir önyargısı yoktur.

Ancak son sonuç, makine öğrenimi de dahil olmak üzere tamamen işlevsel bir şekilde program yazmanın mümkün olduğu, düşünce fonksiyonel programlamanın veri depolama kavramına sahip olmadığı düşünülmektedir. Bu konuda diğer cevaplara katılmıyorum gibi görünüyor.

Umarım bu cevabın uzunluğuna rağmen birkaç kişi ilgilenecektir.

Hesaplamalı paradigmalar

Soru, teorik ve en basit temsilcisi lambda hesabı olan belirli bir hesaplama modeli olan fonksiyonel programlama (diğer adıyla uygulamalı programlama) ile ilgilidir.

Teorik düzeyde kalırsanız, birçok hesaplama modeli vardır: Turing makinesi (TM), RAM makinesi ve diğerleri , lambda hesabı, birleştirici mantık, özyinelemeli fonksiyon teorisi, yarı-Thue sistemleri, vb. Daha güçlü hesaplama modellerin neye hitap edebildikleri açısından eşdeğer olduğu kanıtlanmıştır ve bu Kilise Turing tezinin özüdür .

Önemli bir kavram, modelleri Kilise-Turing tezine yol açan denkliklerin temelini oluşturan modelleri birbirine düşürmektir. Bir programcı bakış açısından bakıldığında, bir modeli diğerine azaltmak genellikle derleyici olarak adlandırılan şeydir. Mantıksal programlamayı hesaplama modeliniz olarak alırsanız, bir mağazadan satın aldığınız PC tarafından sağlanan modelden oldukça farklıdır ve derleyici, mantık programlama dilinde yazılmış programları PC'nizin temsil ettiği hesaplama modeline çevirir (hemen hemen RAM bilgisayarı).

Bununla birlikte, bu iki modelin işleri aynı şekilde yaptığı veya biri için anlamlı bir kavramın diğerine aktarılabileceği anlamına gelmez. Tipik olarak, bir TM'deki hesaplama adımı ile (β-) aralarında tercüme edilebilir olmalarına rağmen, Lambda Analizinde indirgeme adımı. Lambda ifadesinin optimal değerlendirmesi kavramı, TM modellerindeki karmaşıklık sorunlarından oldukça uzaktır.

Uygulamada, kullandığımız programlama dilleri, farklı teorik kökenlerden gelen kavramları bir araya getirme eğilimindedir ve bunu yapmaya çalışır, böylece bir programın seçilen bölümleri, uygun olduğunda bazı modellerin özelliklerinden yararlanabilir. Benzer şekilde, insanlar bina sistemleri, dilde eldeki göreve en iyi uyacak şekilde, farklı bileşenler için farklı diller seçebilir.

Bu nedenle, nadiren saf bir durumda bir programlama dilinde bir programlama paradigması görürsünüz. Programlama dilleri hâlâ baskın paradigmaya göre sınıflandırılır, ancak diğer paradigmalardan gelen kavramlar söz konusu olduğunda, genellikle ayrımları ve kavramsal konuları bulanıklaştıran dilin özellikleri etkilenebilir.

Tipik olarak, Haskell ve ML veya CAML gibi diller işlevsel kabul edilir, ancak zorunlu davranışa izin verebilirler ... Başka neden " tamamen işlevsel altkümeden bahsedelim?" "?

Daha sonra, bunu veya bunu fonksiyonel programlama dilimde yapabileceğinizi iddia edebilirsiniz, ancak ekstra fonksiyonel olarak kabul edilebilecek şeylere güvenirken fonksiyonel programlama ile ilgili bir soruyu gerçekten cevaplamıyor.

Cevaplar, ekstralar olmadan belirli bir paradigma ile daha kesin bir şekilde ilgili olmalıdır.

Değişken nedir?

Başka bir sorun terminolojinin kullanılmasıdır. Matematikte değişken, bazı alanlarda belirsiz bir değeri temsil eden bir varlıktır. Çeşitli amaçlar için kullanılır. Bir denklemde kullanıldığında, denklemin doğrulanması için herhangi bir değer olabilir. Bu vizyon, mantık programlamasında " mantıksal değişken " adı altında kullanılır , çünkü muhtemelen ad değişkeni mantık programlama geliştirilirken başka bir anlamı vardır.

Geleneksel zorunluluk programlamasında, bir değişken, bir değerin temsilini ezberleyebilen ve muhtemelen mevcut değerini başka bir değerle değiştirebilen bir tür kap (veya bellek konumu) olarak anlaşılır.

Fonksiyonel programlamada, bir değişkenin matematikte yaptığı gibi bir değer için yer tutucu ile aynı amaca sahiptir. Geleneksel zorunluluk programlamasında bu rol aslında sabittir ( değişmezlerle karıştırılmamalıdır) 123, true, ["abdcz", 3.14] gibi bu değer alanlarına özgü bir gösterimle ifade edilen değer olarak belirlenen değer ).

Her türlü ve sabit olan değişkenler tanımlayıcılarla temsil edilebilir.

Zorunlu değişkenin değeri değişebilir ve bu değişebilirliğin temelini oluşturur. Fonksiyonel değişken yapılamaz.

Programlama dilleri genellikle daha büyük varlıkların dilde daha küçük olanlardan oluşturulmasına izin verir.

Zorunlu diller, bu tür yapıların değişkenleri içermesine izin verir ve bu size değiştirilebilir veriler sağlar.

Bir program nasıl okunur

Bir program temelde algoritmanızın soyut bir tanımıdır, ister pragmatik bir tasarım ister paradigmatik olarak saf bir dil olsun, bir dildir.

Prensipte, soyutla ne demek istediğine dair her ifadeyi alabilirsiniz. Daha sonra derleyici bunu bilgisayarın yürütmesi için uygun bir forma çevirecektir, ancak ilk yaklaşımdaki sorun bu değildir.

Tabii ki, gerçeklik biraz daha zordur ve derleyicinin verimli yürütme için nasıl başa çıkacağını bilmeyeceği yapılardan kaçınmak için neler olduğu hakkında bazı fikirlere sahip olmak genellikle iyidir. Ama bu zaten optimizasyon ... hangi derleyiciler çok iyi olabilir, genellikle programcılardan daha iyidir.

Fonksiyonel programlama ve değiştirilebilirlik

Değişebilirlik, atama ile değiştirilecek değerleri içerebilecek zorunlu değişkenlerin varlığına dayanır. Bunlar fonksiyonel programlamada mevcut olmadığından, her şey değişmez olarak görülebilir.

Fonksiyonel programlama sadece değerlerle ilgilidir.

Değişmezlik hakkındaki ilk dört ifadeniz çoğunlukla doğrudur, ancak zorunlu görüşle zorunlu olmayan bir şeyi açıklayın. Her birinin kör olduğu bir dünyada renklerle tanımlamaya biraz benziyor. Fonksiyonel programlamaya yabancı kavramlar kullanıyorsunuz.

Yalnızca saf değerleriniz var ve bir tamsayı dizisi saf bir değerdir. Yalnızca bir öğede farklılık gösteren başka bir dizi elde etmek için farklı bir dizi değeri kullanmanız gerekir. Bir öğeyi değiştirmek sadece bu bağlamda var olmayan bir kavramdır. Bir diziyi ve bazı dizinleri bağımsız değişken olarak içeren bir işleviniz olabilir ve yalnızca dizinler tarafından belirtildiği yerde farklılık gösteren neredeyse aynı dizidir. Ancak yine de bağımsız bir dizi değeridir. Bu değerin nasıl temsil edildiği sizin probleminiz değil. Belki bilgisayar için zorunlu çeviride çok şey paylaşıyorlar ... ama bu derleyicinin işi ... ve ne tür bir makine mimarisi için derlendiğini bile bilmek istemiyorsunuz.

Sen yok kopya değerleri (hiçbir mantıklı, bu yabancı bir kavramdır). Yalnızca programınızda tanımladığınız etki alanlarında bulunan değerleri kullanırsınız. Ya bunları (değişmez değerler olarak) tanımlarsınız ya da bunlar, başka bazı değerlere bir işlev uygulamanın sonucudur. Aynı değerin programdaki farklı yerlerde kullanıldığından emin olmak için onlara bir ad verebilirsiniz (böylece bir sabit tanımlar). İşlev uygulamasının bir hesaplama olarak değil, verilen bağımsız değişkenlere yapılan uygulamanın sonucu olarak algılanması gerektiğini unutmayın. Yazma 5+2veya yazma7 aynıdır. Önceki paragrafla tutarlıdır.

Zorunlu değişken yoktur. Atama yapılamaz. Adları atanabilir değişkenlere bağlayabileceğiniz zorunlu dillerden farklı olarak adları yalnızca değerlere (sabitler oluşturmak için) bağlayabilirsiniz.

Bunun karmaşıklıkta bir maliyeti olup olmadığı tamamen belirsizdir. Bir kere, karmaşıklık ile ilgili zorunluluk paradigmalarına atıfta bulunuyorsunuz. Fonksiyonel programınızı, tasarımcının amacı olmayan zorunlu bir program olarak okumayı seçmediğiniz sürece, fonksiyonel programlama için böyle tanımlanmamıştır. Gerçekten de fonksiyonel görüş, bu tür konular hakkında endişelenmenize ve hesaplananlara konsantre olmanıza izin vermemeyi amaçlamaktadır. Uygulamaya karşı biraz şartname gibi.

Derleyici, uygulamaya dikkat etmeli ve ne yapacaksa, ne yapacaksa donanıma en iyi şekilde uyarlanabilecek kadar akıllı olmalıdır.

Programcıların asla bunun için endişelenmediğini söylemiyorum. Ayrıca programlama dillerinin ve derleyici teknolojisinin olmasını istediğimiz kadar olgun olduğunu söylemiyorum.

Soruları cevaplama

  1. Varolan değeri değiştirmeyin (uzaylı kavramı), ancak istendiği yerde farklılık gösteren yeni değerleri hesaplayın, muhtemelen bir ekstra elemana sahip bir liste.

  2. Program yeni veriler alabilir. Bütün mesele bunu dilde nasıl ifade ettiğinizdir. Örneğin, programın giriş akışı olarak adlandırılan büyük olasılıkla boyutlandırılmamış belirli bir değerle çalıştığını düşünebilirsiniz. Orada oturması gereken bir değerdir (zaten tam olarak bilinip bilinmediği sorun değil). Ardından, akışın ilk öğesinden ve akışın geri kalanından oluşan bir çifti döndüren bir işleve sahipsiniz.

    Tamamen uygulanabilir bir yolla iletişim bileşenlerinin ağlarını oluşturmak için kullanabilirsiniz (ortak programlar)

  3. Makine öğrenimi, verileri toplamanız ve değerleri değiştirmeniz gerektiğinde başka bir sorundur. Fonksiyonel programlamada bunu yapmazsınız: sadece egzersiz verilerine göre uygun şekilde farklılık gösteren yeni değerleri hesaplarsınız. Ortaya çıkan makine de çalışacaktır. Endişelendiğiniz şey, zaman ve alan verimliliğini hesaplamak. Ancak, yine, bu farklı bir konudur, bu ideal olarak derleyici tarafından ele alınmalıdır.

Son açıklamalar

Yorumlardan veya diğer cevaplardan, pratik fonksiyonel programlama dillerinin tamamen işlevsel olmadığı açıktır. Bu, özellikle derleme söz konusu olduğunda teknolojimizin hala geliştirileceği gerçeğinin bir yansımasıdır.

Tamamen uygulanabilir bir tarzda yazmak mümkün mü? Cevap yaklaşık 40 yıldır biliniyor ve "evet". 1970'lerde ortaya çıktığı gibi anlamsal semantiklerin amacı, dilleri tamamen işlevsel bir stile çevirmek (derlemek), matematiksel olarak daha iyi anlaşılan ve böylece programların semantiğini tanımlamak için daha iyi bir temel olarak kabul edildi.

İlginç olan, değişkenler de dahil olmak üzere zorunlu programlama yapısının, veri deposu gibi uygun değer alanlarını tanıtarak işlevsel bir stile dönüştürülebilmesidir. Ve işlevsel stile rağmen, zorunlu bir şekilde yazılmış gerçek derleyicilerin koduna şaşırtıcı bir şekilde benzer kalır.


0

İşlevsel programların veri depolayamadığı yanlış bir kanıdır ve Jakes cevabının bunu gerçekten çok iyi açıkladığını sanmıyorum.

İşlevsel programlar, diğer programlar gibi tamsayıların tamsayılarla eşleştirilmesidir. Değişken veri yapıları üzerinde çalışan herhangi bir zorunlu programın işlevsel bir karşılığı vardır. Bu, aynı sonuca ulaşmanın başka bir yoludur.

Bazı kaynaklardan deney verilerinin depolanmasının fonksiyonel yolu, veri yapısı ile birlikte depolama fonksiyonunu argüman olarak çağırmak ve mevcut veri yapısının ve yeni verilerin bir birleşimini çıktılamak ve böylece veriler değiştirilebilir veri yapıları kavramı olmadan saklanır.

Kendi deneyimlerime göre , değişmez veri yapıları kavramı, geleneksel geliştiricileri işlevsel bir ortamda pratik ve hatta imkansız bazı şeyler olduğunu düşünmeye yönlendiriyor. Olay bu değil.


"Fonksiyonel programlar, herhangi bir program gibi, tamsayıların tamsayılarla eşleştirilmesidir." Diyelim ki, Minecraft, tamsayıları tamsayılarla eşleyen bir işlev nasıl?
David Richerby

Kolayca. Her bayt bir ikili tamsayı olarak yorumlanabilir. Bilgisayardaki bir durum bayt topluluğudur. Bir program, hatta Minecraft, bir bilgisayarın durumunu manipüle ederek bir durumdan diğerine eşleştirir.
Jeppe Hartmund

Kullanıcı girişi bu dünyaya uygun görünmüyor.
David Richerby

Kullanıcı girişi bilgisayar durumunun bir parçasıdır. Sadece ekranınızda mevcut değil.
Jeppe Hartmund
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.