Saf Fonksiyonlar: "Yan Etki Yok" "Her Zaman Aynı Çıktı, Aynı Girdi Verildi" anlamına mı geliyor?


84

Bir işlevi tanımlayan iki koşul pureaşağıdaki gibidir:

  1. Yan etki yok (yani yalnızca yerel kapsamda değişikliklere izin verilir)
  2. Aynı girdi verildiğinde her zaman aynı çıktıyı döndür

İlk koşul her zaman doğruysa, ikinci koşulun doğru olmadığı zamanlar var mı?

Yani gerçekten sadece ilk şart için gerekli mi?


3
Tesisleriniz yanlış belirlenmiş. "Giriş" çok geniştir. Fonksiyonların iki tür girdiye sahip olduğu düşünülebilir. Argümanları ve "çevresel" / "bağlamsal". Bu iki tür girdiyi ayırt etmezseniz, sistem zamanını döndüren bir işlevin saf olduğu düşünülebilir (açık olmasa da).
Alexander

4
@Alexander: "Saf fonksiyon" bağlamında, "girdi" genellikle açıkça geçirilen parametreler / bağımsız değişkenler olarak anlaşılır (programlama dili hangi mekanizma tarafından kullanılırsa kullanılsın). Bu, "saf işlev" tanımının bir parçasıdır. Ama haklısın, tanıma dikkat etmek önemli.
sleske

3
Önemsiz bir karşı örnek: küresel bir değişkenin değerini döndür. Yan etki yok (global sadece okunuyor!), Ancak yine de her seferinde potansiyel olarak farklı sonuçlar. (Küreselleri sevmiyorsanız, çalışma zamanında çağrı yığınına bağlı olan yerel bir değişkenin adresini döndürün).
Peter - Monica'yı eski

2
"Yan etkiler" tanımınızı genişletmeniz gerekiyor; Saf bir yöntem olmadığını söylemek üretmek yan etkileri, ancak saf yöntem olmadığını da not gerekir tüketmek yan etkiler başka yerde üretti.
Eric Lippert

2
@sleske Belki genel olarak anlaşılıyor, ancak bu ayrımın olmaması OP'nin kafa karışıklığının kesin nedenidir.
Alexander

Yanıtlar:


114

Dış kapsamı değiştirmeyen ancak yine de saf olmadığı kabul edilen birkaç karşı örnek:

  • function a() { return Date.now(); }
  • function b() { return window.globalMutableVar; }
  • function c() { return document.getElementById("myInput").value; }
  • function d() { return Math.random(); } (kuşkusuz PRNG'yi değiştirir, ancak gözlemlenebilir olarak kabul edilmez)

Sabit olmayan yerel olmayan değişkenlere erişim, ikinci koşulu ihlal edebilmek için yeterlidir.

Her zaman saflığın iki koşulunu tamamlayıcı olarak düşünüyorum:

  • sonuç değerlendirmesinin yan durum üzerinde etkisi olmamalıdır
  • değerlendirme sonucu yan durumdan etkilenmemelidir

Terimi, bir yan etki sadece ilk belirtir, yerel olmayan durumunu değiştirmekten fonksiyonu. Bununla birlikte, bazen okuma işlemleri de yan etkiler olarak kabul edilir: birincil amaçları bir değere erişmek olsa bile , işlem olduklarında ve yazmayı da içerdiklerinde. Bunun örnekleri, jeneratörün dahili durumunu değiştiren sözde rasgele bir sayı üretmek, okuma konumunu ilerleten bir giriş akışından okumak veya bir "ölçüm al" komutunu içeren harici bir sensörden okumaktır.


1
Teşekkürler Bergi. Bazı nedenlerden dolayı, yan etkilerin değişkenleri yerel kapsamın dışında okumayı içerdiğini düşündüm , ancak sanırım bu tür harici değişkenler yazıyorsa bu sadece bir yan etki .
Magnus

17
Eğer prompt("you choose")yan etkileri yoktur, bir adım geri götür ve yan etkileri anlamını açıklamak gerekiyor.
Holger

1
@Magnus Evet, tam olarak etkinin anlamı budur. Cevabımda da açıklığa kavuşturmaya çalışacağım, bu kadar büyük bir ilgi beklemiyordum ve onlarca oya layık bir cevap vermek istiyorum :-)
Bergi

2
Tüm bildiğiniz Math.random () bir termal diyot döndürür. Aslında kötü bir RNG kullanacağı belirtilmemiştir.
Joshua

1
İki koşuldan birincisinin "efektler", ikincisinin "katsayılar" olduğunu duydum. Her ikisi de "yan etkilerdir" ve saf değildir. f (katsayılar, girdi) -> etkiler, çıktı Katsayıları daha geniş ortamdaki değişikliklerden gelen girdilerdir, etkiler daha geniş ortamı değiştiren çıktılardır. Örneğin Elm ve Clojurescrips bu modelle yeniden çerçevelendiriyor.

30

Saf bir fonksiyonun ne olduğunu ifade etmenin "normal" yolu , referans şeffaflığı ile ilgilidir . Referans olarak şeffafsa bir işlev saftır .

Referans Şeffaflığı , kabaca, programın anlamını değiştirmeden programın herhangi bir noktasında fonksiyon çağrısını dönüş değeriyle değiştirebileceğiniz anlamına gelir.

Dolayısıyla, örneğin, eğer printfC'ler referans olarak şeffafsa, bu iki programın aynı anlama sahip olması gerekir:

printf("Hello");

ve

5;

ve aşağıdaki programların tümü aynı anlama sahip olmalıdır:

5 + 5;

printf("Hello") + 5;

printf("Hello") + printf("Hello");

Çünkü printfyazılan karakter sayısını döndürür, bu durumda 5.

voidİşlevlerle daha da belirginleşir . Bir işlevim varsa void foo, o zaman

foo(bar, baz, quux);

ile aynı olmalı

;

Yani foohiçbir şey döndürmediği için, programın anlamını değiştirmeden onu hiçbir şeyle değiştirebilmeliyim.

Öyleyse açıktır ki, ne referans olarak şeffaf ne printfde fooşeffaftır ve dolayısıyla ikisi de saf değildir. Aslında bir voidişlev, işlemsiz olmadığı sürece, hiçbir zaman referans olarak şeffaf olamaz.

Bu tanımı verdiğiniz tanım olarak daha kolay ele alıyorum. Ayrıca, istediğiniz herhangi bir ayrıntı düzeyinde uygulamanıza olanak tanır: bunu tek tek ifadelere, işlevlere ve tüm programlara uygulayabilirsiniz. Örneğin, böyle bir işlev hakkında konuşmanıza olanak tanır:

func fib(n):
    return memo[n] if memo.has_key?(n)
    return 1 if n <= 1
    return memo[n] = fib(n-1) + fib(n-2)

Fonksiyonu oluşturan ifadeleri analiz edebilir ve değiştirilebilir bir veri yapısı, yani memodizi kullandıkları için, referans olarak şeffaf olmadıkları ve dolayısıyla saf olmadıkları sonucuna kolayca varabiliriz . Ancak, biz de işlevi bakabilirsiniz ve bu görebilirsiniz olduğu referentially şeffaf ve böylece saf. Bu bazen dış saflık olarak adlandırılır , yani dış dünyaya saf görünen ancak dahili olarak saf olmayan bir işlevdir.

Bu tür işlevler hala kullanışlıdır, çünkü kirlilik etrafındaki her şeye bulaşsa da, dış saf arayüz bir tür "saflık engeli" oluşturur, burada kirlilik işlevin yalnızca üç satırını etkiler, ancak programın geri kalanına sızmaz. . Bu üç satırın doğruluğu tüm programdan çok daha kolaydır.


2
Bu safsızlık, eşzamanlılığa sahip olduğunuzda tüm programı etkiler.
R .. GitHub BUZA YARDIM ETMEYİ DURDUR

@R .. Eşzamanlılığın açıklanan Fibonacci işlevini harici olarak saflaştırabileceği bir yol düşünebiliyor musunuz? Yapamam. Yazmak memo[n]idempotenttir ve ondan okumamak sadece CPU döngülerini boşa harcar.
Brilliand

İkinize de katılıyorum Safsızlık olabilir eşzamanlılık sorunlarına yol, ancak bu özel durumda değildir.
Jörg W Mittag

@R .. Eşzamanlılık farkında bir sürüm hayal etmek zor değil.
user253751

1
@Brilliand Örneğin, memo[n] = ...önce bir sözlük girişi oluşturabilir ve ardından değeri içine kaydedebilir. Bu, başka bir iş parçacığının başlatılmamış bir girişi görebileceği bir pencere bırakır.
user253751

12

Bana öyle geliyor ki, tarif ettiğiniz ikinci durum, birincisinden daha zayıf bir kısıtlama.

Size bir örnek vereyim, ayrıca konsola da günlüğü tutan bir fonksiyona sahip olduğunuzu varsayalım:

function addOneAndLog(x) {
  console.log(x);
  return x + 1;
}

Sağladığınız ikinci koşul karşılanmıştır: bu işlev, aynı girdi verildiğinde her zaman aynı çıktıyı döndürür. Ancak, konsolda oturum açmanın yan etkisini içerdiği için saf bir işlev değildir.

Saf işlev, tam anlamıyla, referans şeffaflık özelliğini karşılayan bir işlevdir . Bu, bir işlev uygulamasını, programın davranışını değiştirmeden ürettiği değerle değiştirebileceğimiz özelliktir.

Basitçe ekleyen bir fonksiyonumuz olduğunu varsayalım:

function addOne(x) {
  return x + 1;
}

Biz yerini alabilir addOne(5)ile 6programımız ve değişecek hiçbir şey her yerde.

Bunun aksine, davranışı değiştirmeden programımızın herhangi bir yerindeki addOneAndLog(x)değerle 6değiştiremeyiz çünkü ilk ifade konsola bir şeyin yazılmasıyla sonuçlanırken ikincisi olmaz.

addOneAndLog(x)Çıktı döndürmenin yanı sıra performans gösteren bu ekstra davranışlardan herhangi birini bir yan etki olarak görüyoruz .


"Bana öyle geliyor ki tarif ettiğiniz ikinci koşul, birincisinden daha zayıf bir kısıtlama." Hayır, iki koşul mantıksal olarak bağımsızdır.
sleske

@sleske yanılıyorsunuz. Saf ve yan etki terimleri için net tanımlar sağladım. Bu kısıtlamalar dahilinde, yan etkisi olmayan bir fonksiyonun belirli bir girdi için aynı çıktıyı döndürmesinin yanı sıra hiçbir şey yoktur. Bununla birlikte, ikinci koşulun birincisi olmadan karşılanabileceği örnekler verdim. Saflık kavramını anlamak için temel kavram, referans şeffaflıktır.
TheInnerLight

Küçük yazım hatası: Belirli bir girdi için aynı çıktıyı döndürmek dışında hiçbir yan etkisi olmayan bir işlevin yapabileceği hiçbir şey yoktur .
TheInnerLight

Şimdiki zamana dönmek gibi bir şeye ne dersiniz? Bunun yan etkileri yoktur, ancak aynı girdi için farklı bir çıktı döndürür. Veya daha genel olarak, sonucun yalnızca girdi parametrelerine değil, aynı zamanda (değiştirilebilir) bir genel değişkene bağlı olduğu herhangi bir işlev.
sleske

2
Görünüşe göre, yaygın olarak kullanılandan farklı bir "yan etki" tanımı kullanıyorsunuz. Bir yan etkisi yaygın olarak veya bir "durumunda gözlemlenebilir değişim" "Bir değer dönen yanında gözlemlenebilir bir etkisi" olarak tanımlanır - örneğin bkz Wikipedia , softwareengineering.SE bu gönderiye . Tamamen haklısınız Date.now(), bu saf / referans olarak şeffaf değil, ancak yan etkileri olduğu için değil, sonucu sadece girdiden daha fazlasına bağlı olduğu için.
sleske

7

Sistemin dışından bir rastgelelik kaynağı olabilir. Hesaplamanızın bölümünün oda sıcaklığını içerdiğini varsayalım. Daha sonra fonksiyonun yürütülmesi, oda sıcaklığının rastgele dış elemanına bağlı olarak her seferinde farklı sonuçlar verecektir. Program çalıştırılarak durum değiştirilmez.

Zaten tek düşünebildiğim.


3
Bana göre, "sistemin dışından gelen bu rastgelelik" bir tür yan etki. Bu davranışlara sahip işlevler "püre" değildir.
Joseph M. Dion

2

FP tanımlarıyla ilgili sorun, çok yapay olmalarıdır. Her değerlendirme / hesaplamanın değerlendirici üzerinde yan etkileri vardır. Teorik olarak doğrudur. Bunun reddi, yalnızca FP savunucularının felsefeyi ve mantığı görmezden geldiğini gösterir: "değerlendirme", bazı akıllı ortamların (makine, beyin vb.) Durumunun değiştirilmesi anlamına gelir. Bu, değerlendirme sürecinin doğasıdır. Değişiklik yok - "kalkül" yok. Etki çok görünür olabilir: CPU'nun ısıtılması veya arızalanması, aşırı ısınma durumunda anakartın kapatılması vb.

Referans şeffaflık hakkında konuştuğunuzda, bu tür bir şeffaflık hakkındaki bilgilerin tüm sistemin yaratıcısı ve anlamsal bilginin sahibi olarak insanlar için mevcut olduğunu ve derleyici için mevcut olmayabileceğini anlamalısınız. Örneğin, bir işlev bazı harici kaynakları okuyabilir ve imzasında IO monad bulunur, ancak her zaman aynı değeri döndürür (örneğin, sonucu current_year > 0). Derleyici, işlevin her zaman aynı sonucu döndüreceğini bilmez, bu nedenle işlev saf değildir, ancak referans olarak şeffaf özelliğe sahiptir ve Truesabit ile değiştirilebilir .

Dolayısıyla, bu tür bir yanlışlıktan kaçınmak için, programlama dillerindeki matematiksel fonksiyonları ve "fonksiyonları" birbirinden ayırmalıyız. Haskell'deki işlevler her zaman saf değildir ve bunlarla ilgili saflık tanımı her zaman çok koşulludur: gerçek yan etkileri ve fiziksel özellikleri olan gerçek donanım üzerinde çalışıyorlar, bu matematiksel işlevler için yanlış. Bu, "printf" işlevine sahip örneğin tamamen yanlış olduğu anlamına gelir.

Ancak tüm matematiksel fonksiyonlar da saf değildir: tbir parametre olarak (zaman) 'a sahip olan her fonksiyon saf olmayabilir: tfonksiyonun tüm etkilerini ve stokastik doğasını tutar: genel durumda giriş sinyaliniz vardır ve gerçek değerler hakkında fikriniz olmaz, gürültü bile ol.


2

İlk koşul her zaman doğruysa, ikinci koşulun doğru olmadığı zamanlar var mı?

Evet

Aşağıdaki basit kod parçacığını düşünün

public int Sum(int a, int b) {
    Random rnd = new Random();
    return rnd.Next(1, 10);
}

Bu kod, verilen aynı girdi kümesi için rastgele çıktı döndürür - ancak herhangi bir yan etkisi yoktur.

Bahsettiğiniz her iki nokta # 1 & # 2 birlikte birleştirildiğinde genel etkisi şu anlama gelir: Herhangi bir zamanda Sum, aynı i / p'ye sahip işlev bir programda sonucuyla değiştirilirse, programın genel anlamı değişmez . Bu, Referans şeffaflıktan başka bir şey değildir .


Ancak bu durumda, ilk koşul doğrulanmaz: konsola yazmak, makinenin durumunu değiştirdiği için bir yan etki olarak kabul edilir.
Sağ bacak

@Rightleg thx onu işaret ettiğin için. Her nasılsa OP'yi tamamen başka bir şekilde yanlış anladım. düzeltilmiş cevap.
rahulaga_dev

2
Rastgele oluşturucunun durumunu değiştirmiyor mu?
Eric Duminil

1
Rasgele sayı üretecinin durum fonksiyonu tatmin durum 2. bulunması şeklinde açık sağlanan sürece bir rasgele sayı üretmek için kendisi bir yan etki olduğunu
TheInnerLight

1
rndişlevden kaçmaz, bu nedenle durumunun değişmesi, işlevin saflığı açısından önemli değildir, ancak kurucunun Randomgeçerli zamanı bir çekirdek değer olarak kullanması, ave dışında "girdiler" olduğu anlamına gelir b.
Sneftel
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.