Notlandırılmış bir saf fonksiyonun kendisi saf olarak mı kabul edilir?


47

Diyelim ki fn(x)en önemli faktörlerin bir listesini döndürmek gibi pahalı bir şey yapan saf bir işlev x.

Ve diyelim ki aynı fonksiyonun hafızaya alınmış bir versiyonunu yaptık memoizedFn(x). Belirli bir giriş için her zaman aynı sonucu döndürür, ancak performansı artırmak için önceki sonuçların özel bir önbelleğini tutar.

Resmen konuşmak, memoizedFn(x)saf olarak kabul edilir?

Yoksa FP tartışmalarında böyle bir fonksiyona atıfta bulunmak için kullanılan başka bir ad veya nitelendirici terim var mı? (diğer bir deyişle, sonraki aramaların hesaplama karmaşıklığını etkileyebilecek ancak dönüş değerlerini etkilemeyebilecek yan etkileri olan bir işlev.)


24
Belki de püristler için saf değil, pragmatik insanlar için "yeterince saf" ;-)
Doc Brown

2
@DocBrown Katılıyorum, sadece 'yeterince saf' için daha resmi bir terim olup olmadığını merak ediyorum
callum

13
Saf bir işlevin yürütülmesi büyük olasılıkla işlemcinin komut önbelleğini, dal belirleyicilerini vb. Değiştirir.
gnasher729

10
@callum Hayır, "yeterince saf" biçiminde resmi bir tanım yoktur. Saflık ve iki "referans olarak şeffaf" çağrının anlamsal denkliği hakkında tartışırken, her zaman tam olarak hangi anlambilimden yararlanacağınızı belirtmeniz gerekir. Bazı düşük uygulama detay düzeyinde, her zaman bozulup farklı bellek efektleri veya zamanlamaları olacaktır. Bu yüzden pragmatik olmanız gerekiyor: kodunuz hakkında muhakeme için hangi düzeyde ayrıntı faydalıdır?
Bergi

3
O halde pragmatizm adına, saflığın, hesaplama zamanını çıktının bir parçası olarak kabul edip etmemenize bağlı olduğunu söyleyebilirim. funcx(){sleep(cached_time--); return 0;}her seferinde aynı val değerini döndürür, ancak farklı performans gösterir
Mars

Yanıtlar:


41

Evet. Saf bir fonksiyonun hafızaya alınmış hali de saf bir fonksiyondur.

Fonksiyon saflığının umursadığı şey, girdi parametrelerinin işlevin dönüş değeri üzerindeki etkisidir (aynı girişi geçmek her zaman aynı çıktıyı vermelidir) ve genel durumlarla ilgili herhangi bir yan etki (örn. Terminal veya UI veya ağa metin) . Hesaplama süresi ve fazladan hafıza kullanımı, fonksiyon saflığı ile ilgisizdir.

Saf bir fonksiyonun önbellekleri program tarafından hemen hemen görünmez; İşlevsel bir programlama dilinin, bunun yararlı olacağına karar verebiliyorsa, saf işlevi otomatik olarak işlevin belleğe alınmış bir sürümüne göre optimize etmesine izin verilir. Uygulamada, notlandırmanın ne zaman yararlı olacağını otomatik olarak belirlemek aslında oldukça zor bir sorundur, ancak böyle bir optimizasyon geçerli olacaktır.


19

Wikipedia, bir "Saf İşlev" i aşağıdaki özelliklere sahip bir işlev olarak tanımlar :

  • Bu geri dönüş değeri ile aynıdır (yerel statik değişkenler, yerel olmayan değişken, değişken bir referans bağımsız değişken veya giriş akımları I / O cihazları ile ilgili olan herhangi bir varyasyonu) aynı bağımsız değişkenler için.

  • Değerlendirmesinin yan etkisi yoktur (yerel statik değişkenlerin mutasyonu, yerel olmayan değişkenler, değişken referans argümanları veya I / O akışları).

Aslında, saf işlev aynı girdi verilen aynı çıktıyı döndürür ve işlev dışında başka hiçbir şeyi etkilemez. Saflık açısından, aynı girdi verilen aynı çıktıyı geri döndürdüğü sürece, işlevin dönüş değerini nasıl hesapladığı önemli değildir.

Haskell gibi fonksiyonel olarak saf diller , önceden hesaplanmış sonuçlarını önbelleğe alarak bir işlevi hızlandırmak için rutin olarak kullanılır .


16
Bir şeyi özleyebilirim, ama yan etkisi olmadan önbelleği nasıl koruyacaksın?
val

1
Fonksiyonun içinde tutarak.
Robert Harvey,

4
"Yerel statik değişkenlerin mutasyonu yok" çağrıları arasında kalıcı olan yerel değişkenleri de dışlıyor gibi görünüyor.
val

3
Bu aslında soruyu cevaplamıyor, evet olduğunu ima etseniz bile, saf.
Mars

6
@ val Doğru: bu durumun biraz rahatlaması gerekiyor. Bahsettiği tamamen işlevsel olan notlaştırmada , herhangi bir statik verinin görünür bir mutasyonu yoktur . Olan şey, fonksiyonun ilk çağrıldığı zaman sonucun hesaplanıp hafızaya alınması ve çağrıldığında aynı değeri döndürmesidir. Birçok dilde bunun için bir deyim vardır: static constC ++ ' da yerel bir değişken (ancak C değil) veya Haskell'de tembel olarak değerlendirilmiş bir veri yapısı. İhtiyacınız olan bir durum daha var: başlatma işlemi güvenli bir şekilde yapılmalıdır.
Davislor

7

Evet, notlandırılmış saf işlevlere genel olarak saf denir. Bu özellikle, hafızaya alınmış, tembel olarak değerlendirilebilen, değişken sonuçların yerleşik bir özellik olduğu Haskell gibi dillerde yaygındır.

Önemli bir uyarı var: notlama işlevi iş parçacığı için güvenli olmalı, yoksa iki iş parçacığı her ikisi de aramaya çalıştığında bir yarış koşulu alabilirsiniz.

Bu şekilde “tamamen işlevsel” terimini kullanan bir bilgisayar bilimcisinin bir örneği, Conal Elliott tarafından otomatik not alma hakkında şu blog yazısı :

Belki de şaşırtıcı şekilde, notlaştırma tembel bir işlevsel dilde basit ve tamamen işlevsel olarak uygulanabilir.

Hakemli literatürde birçok örnek var ve onlarca yıldır. Örneğin, 1995 tarihli “Gerçek Dünya AI Sistemlerinde Yazılım Mühendisliği Aracı Olarak Otomatik Notlandırmayı Kullanma” başlıklı bu makalede, bugün saf işlev dediğimiz şeyi tanımlamak için bölüm 5.2'de çok benzer bir dil kullanılmıştır:

Notlandırma işlemi sadece gerçek fonksiyonlar için geçerlidir, prosedürler için değildir. Diğer bir deyişle, bir fonksiyonun sonucu tamamen ve deterministik olarak giriş parametreleri tarafından belirtilmezse, not kullanımı yanlış sonuçlar verecektir. Sistem genelinde işlevsel bir programlama stilinin kullanılmasını teşvik ederek, başarılı bir şekilde hafızaya alınabilecek fonksiyonların sayısı arttırılacaktır.

Bazı zorunlu dillerin benzer bir deyimi vardır. Örneğin, static constC ++ 'daki bir değişken, değeri kullanılmadan önce yalnızca bir kez başlatılır ve asla mutasyon olmaz.


3

Nasıl yaptığınıza bağlı.

Genellikle insanlar bir tür önbellek sözlüğünü mutasyona sokarak not almak isterler. Bu, eşzamanlılık için endişe etmek, önbellek çok büyük olmaktan endişe etmek gibi saf olmayan mutasyonlarla ilgili tüm problemlere sahiptir.

Ancak, saf bellek mutasyonu olmadan not alabilirsiniz. Bir örnek, bu yanıtta , not edilen değerleri harici olarak bir lengthsargüman ile dışarıdan izliyorum .

In Robert Harvey sağlanan bağlantı , tembel değerlendirme yan etkilerinden kaçınmak için kullanılır.

Bazen görülen diğer bir teknik, notu açıkça, bir IOkedi-efektinin not fonksiyonu gibi bir tür bağlamında saf olmayan bir yan etki olarak işaretlemektir .

Bu sonuncusu, bazen amacın onu ortadan kaldırmak yerine mutasyona kapma mutasyonu olduğu noktasını ortaya koyuyor. İşlevsel programcıların çoğu kirliliği açık ve kapalı hale getirmenin "yeterince saf" olduğunu düşünür.

Bir terimin gerçekten saf bir işlevden ayrılmasını istiyorsanız, sadece "değişken bir sözlükle notlandırılmış" demenin yeterli olduğunu düşünüyorum. Bu, insanların nasıl güvenle kullanılacağını bilmelerini sağlar.


Daha saf bir çözümün yukarıdaki sorunları çözdüğünü düşünmüyorum: Herhangi bir eşzamanlılık endişesini kaybederken, aynı anda eşzamanlı olarak başlatılan iki çağrı için collatz(100)ve collatz(200)işbirliği yapma şansını da kaybedersiniz . Ve IIUIC, önbellek büyümesiyle ilgili problemin büyük olduğunu gösteriyor (Haskell'in bunun için bazı güzel hileleri olsa da?).
maaartinus

Not: IOSaf. Tüm saf olmayan yöntemler IOve Kediler adlandırılır unsafe. Async.memoizeaynı zamanda saf, bu yüzden "yeterince saf" için yetinmek zorunda değiliz :)
Samuel

2

Genellikle, bir liste döndüren bir işlev hiç de saf değildir, çünkü depolanmasını gerektirir ve böylece başarısız olabilir (örneğin, saf olmayan bir istisna atarak). Değer türüne sahip ve sınırlı boyutlu bir değer türü olarak bir listeyi temsil edebilen bir dilde bu sorun olmayabilir. Bu nedenle, örneğiniz muhtemelen saf değil.

Genel olarak, eğer notlandırma hatasız bir şekilde yapılabiliyorsa (örneğin, not edilmiş sonuçlar için statik olarak ayrılmış depolama alanı ve eğer dil konuları kabul ederse, bunlara erişimi kontrol etmek için dahili senkronizasyon ile) yapılabilirse, böyle bir fonksiyonu dikkate almak makul olacaktır. saf.


0

Sen kullanarak yan etkileri olmayan Memoization uygulayabilir devlet monad .

[Devlet monad] temelde S => (S, A) fonksiyonudur, burada S durumunuzu temsil eden tiptir ve A fonksiyonun ürettiği sonuçtur - Cats State .

Sizin durumunuzda devlet, hafızaya alınmış değer ya da hiçbir şey olacaktır (örneğin Haskell Maybeveya Scala Option[A]). Eğer not değeri mevcut ise A, olduğu gibi döndürülür , aksi takdirde Ahem geçiş durumu hem de sonuç olarak hesaplanır ve döndürülür.

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.