Aynı girişin her zaman aynı çıkışı döndürdüğü, ancak aynı zamanda yan etkileri olan bir fonksiyona ne denir?


43

Mesela normal bir saf işleve sahibiz.

function add(a, b) {
  return a + b
}

Sonra onu yan etkisi olacak şekilde değiştiriyoruz

function add(a, b) {
  writeToDatabase(Math.random())
  return a + b;
}

Bildiğim kadarıyla saf bir işlev olarak görülmüyor, çünkü insanların çoğu zaman "yan etkisi olmayan işlevler" olarak adlandırdıkları saf işlevler dediğini duyuyorum. Ancak, aynı girdiler için aynı çıktıyı döndüreceği gerçeğine kadar saf bir işlev gibi davranır.

Bu tür bir işlev için farklı bir ad var mı, adlandırılmamış mı, yoksa hala tamamen saf mı ve saflığın tanımı konusunda yanıldım mı?


91
"Saf bir işlev değil".
Ross Patterson,

2
@RossPatterson benim de düşündüğüm şeydi, ancak sorarak referans şeffaflığı öğrendim, böylece kendime saklayamadığıma sevindim.
m0meni

9
Eğer writeToDatabasebu sizin ikinci yapım böylece bir istisna tetikleyebilecek başarısız add... önce sorun yoktu aynı argümanlarla denilen bile, istisna bazen işlev üretmek çoğu zaman yan etkileri tanıtır hatasız ilgili koşullar olduğu bu tür sahip molası "giriş-çıkış saflığı".
Bakuriu

25
Belirli bir giriş için her zaman aynı çıktıyı veren bir şeye deterministik denir .
njzk2

2
@ njzk2: Doğru, ama aynı zamanda durumsuz . Bir durum bilgisi belirleyici fonksiyonu, her giriş için aynı çıkış vermeyebilir. Örnek: önceki çağrı ile aynı argümanla çağrılırsa F(x)geri dönmek için tanımlanır true. Açıkçası, {1,2,2} => {undefined, false, true}bu deterministik olan dizilim ile , ancak bunun için farklı çıktılar veriyor F(2).
MSalters

Yanıtlar:


85

Genel saflık tanımlarından emin değilim, ancak Haskell (programcıların saflık ve referans saydamlığı gibi şeyleri önemsemediği bir dil) açısından sadece işlevlerinizin "saf"add olduğu görüşünde . İkinci versiyonu saf değil . Bu nedenle sorunuza cevaben, "saf";)

Bu tanıma göre, saf fonksiyon şu fonksiyondur:

  1. Sadece girişine bağlıdır. Yani, aynı giriş verildiğinde, her zaman aynı çıkışı döndürür.
  2. Referans olarak şeffaftır: fonksiyon serbest bir şekilde değeri ile değiştirilebilir ve programın "davranışı" değişmez.

Bu tanımla, ikinci işlevinizin kural 2'yi ihlal ettiği için saf olarak kabul edilemeyeceği açıktır. Yani, aşağıdaki iki program eşdeğer değildir:

function f(a, b) { 
    return add(a, b) + add(a, b);
}

ve

function g(a, b) {
    c = add(a, b);
    return c + c;
}

Bunun nedeni, her iki fonksiyonun da aynı değeri fvermesine rağmen , fonksiyon veritabanına iki kez yazacak, ancak bir gkez yazacak! Veritabanına yazmanın programınızın gözlemlenebilir davranışının bir parçası olması muhtemeldir, bu durumda ikinci sürümünüzü add"saf" olarak göstermedim.

Veritabanına yazma, programınızın davranışının gözlemlenebilir bir parçası değilse, her iki sürümü de addeşit ve saf olarak kabul edilebilir. Ancak veri tabanına yazmanın bir önemi olmadığını düşünemiyorum. Günlüğe kaydetme bile önemli!


1
Saydamlık şeffaflığı verilen "girişine bağlı değil" gereksiz değil mi? Hangisi RT'nin saflıkla eş anlamlı olduğu anlamına gelir? (Bu konuda daha fazla kaynak
buluyorum, aradığım

Kafa karıştırıcı olduğuna katılıyorum. Sadece tartışılan örnekleri düşünebilirim. Söylemek f(x)sadece bağımlı değil x, aynı zamanda bazı dış küresel değişkene bağlıdır y. Daha sonra, eğer fRT'nin özelliği varsa , dokunmadığınız sürece tüm oluşumlarını geri dönüş değeri ile serbestçe değiştirebilirsiniz y. Evet, örneğim şüpheli. Fakat önemli olan şudur: eğer fveritabanına yazarsa (veya bir kütüğe yazıyorsa) RT'nin özelliğini kaybeder: şimdi global ydokunuştan bırakıp bırakmamanız farketmez , programınızın anlamının gerçekten olup olmadığına bağlı olarak değiştiğini biliyorsunuzdur . arayın fya da sadece dönüş değerini kullanın.
Andres F.

Humph. Diyelim ki yan etkiler dışında saf olan ve iki örneğinizin eşdeğer olduğu böyle bir davranışa sahip olacağımız garantilidir. (Bu davayı açtım, bu yüzden varsayımsal değildi.) Sanırım tam olarak işimiz bitmedi.
Joshua,

2
İkinci fonksiyonun 1. kuralı da bozabileceğini iddia ediyorum. Dile ve kullanılan veritabanı API'sinin hata işleme uygulamalarına bağlı olarak, veritabanı kullanılamıyorsa veya db yazma bir nedenden dolayı başarısız olursa, işlev hiçbir şey döndürmeyebilir.
Zach Lipton

1
Haskell'den bahsedildiğinden beri: Haskell'de böyle bir yan etki eklemek , fonksiyonun imzasını değiştirmeyi gerektirir . (orijinal veri tabanını ek bir giriş olarak vermek ve değiştirilmiş veri tabanını fonksiyonun geri dönüş değeri olarak almak gibi düşünün). Tipik sistemde yan etkileri oldukça zarif bir şekilde modellemek mümkün, sadece bugünün ana dilleri bunun için yan etkiler ve saflığı önemsemiyor.
ComicSansMS

19

Aynı girdi her zaman aynı çıktıyı döndürecek, ancak aynı zamanda yan etkileri olan bir fonksiyona ne denir?

Böyle bir fonksiyon denir

deterministik

Davranışı girdiden tamamen tahmin edilebilen bir algoritma.

termwiki.com

Devlet ile ilgili olarak:

Hangi işlevi tanımladığınıza bağlı olarak, bir işlevin durumu yoktur. Nesne yönelimli dünyadan geliyorsanız, bunun x.f(y)bir yöntem olduğunu unutmayın . Bir işlev olarak gibi görünüyordu f(x,y). Ve eğer kapalı sözcüksel kapsam ile kapanışlardaysanız, değişmez durumun işlevler ifadesinin bir parçası olabileceğini de unutmayın. Bu, deterministik doğanın işlevlerini etkileyebilecek tek değişken durumdur. Yani, f (x) = x + 1, değişmediği sürece deterministiktir. 1'in nerede depolandığı önemli değil.

İşlevlerin her ikisi de determinist. İlkiniz aynı zamanda saf bir fonksiyondur. İkincisi saf değil.

Saf fonksiyonu

  1. İşlev her zaman aynı argüman değerleri verilen aynı sonuç değerini değerlendirir. İşlev sonucu değeri, program yürütme devam ederken veya programın farklı yürütmeleri arasında değişebilecek herhangi bir gizli bilgiye veya duruma ya da G / Ç aygıtlarından herhangi bir harici girişe bağlı olamaz.

  2. Sonucun değerlendirilmesi, değişken nesnelerin mutasyonu veya I / O cihazlarına çıkışı gibi herhangi bir semantik olarak gözlemlenebilir yan etkiye veya çıktılığa neden olmaz.

wikipedia.org

Nokta 1, deterministik anlamına gelir . Nokta 2, referans şeffaflığı ifade eder . Birlikte, saf bir işlevin yalnızca argümanlarının ve döndürülen değerinin değişmesine izin verdiği anlamına gelir. Başka hiçbir şey değişime neden olmaz. Başka hiçbir şey değişmedi.


-1. Veritabanına yazma, girdilere bakarak genellikle belirlenemeyen dış duruma bağlıdır. Veritabanı, birkaç nedenden dolayı kullanılamayabilir ve işlemin başarılı olup olmayacağı tahmin edilemez. Bu deterministik davranış değildir.
Frax

@Frax Sistem belleği kullanılamıyor olabilir. CPU kullanılamayabilir. Deterministik olmak başarıyı garanti etmez. Başarılı davranışın öngörülebilir olduğunu garanti eder.
candied_orange

OOMing herhangi bir işleve özgü değildir, farklı bir sorun kategorisidir. Şimdi, sadece "saf işlev" tanımınızın 1. noktasına bakalım (aslında "deterministik" anlamına gelir): "İşlev sonuç değeri, program yürütme devam ederken veya farklı yürütme işlemleri arasında değişebilecek herhangi bir gizli bilgiye veya duruma bağlı olamaz program ve I / O cihazlarından herhangi bir harici girişe bağlı olamaz ". Veritabanı bu tür bir durumdur, bu nedenle OP'ler işlevi bu koşulu açıkça yerine getirmiyor - belirleyici değil.
Frax

@candied_orange DB'ye yazma işleminin sadece girdilere bağlı olup olmadığını kabul ediyorum. Ama bu Math.random(). Bu yüzden hayır, bir PRNG (fiziksel bir RNG yerine) varsaymadığımızda VE PRNG'lerin girişin bir kısmını (ki bu referansın kodlanmadığını) belirtdiğini düşünmeyince belirleyici değildir.
marstato

1
@candied_o deterministik durumların atıfını " davranışları girdiden tamamen tahmin edilebilecek bir algoritma" olarak düzenleyin. IO'ya yazma, bana göre, sonuç değil, kesinlikle bir davranış.
Marstato

9

Yan etkisi umursamıyorsanız, referans olarak şeffaftır. Elbette umrunda değil ama başkasının umrunda olabilir, bu nedenle terimin uygulanabilirliği bağlama bağımlıdır.

Ben açıkladığınız tam özellikleri için genel bir terim bilmiyorum ama önemli bir alt kümesidir olanlardır idempotent . Bilgisayar bilimlerinde, matematiğe * biraz farklı bir şekilde, belirsiz bir işlev aynı etkiyle tekrarlanabilen işlevdir ; Yani, defalarca yapmanın nett yan etkisi sonucu bir kez yapmakla aynı olduğunu söylemek.

Bu nedenle, yan etkiniz belirli bir satırda belirli bir değerde bir veritabanını güncellemek veya tam olarak tutarlı içeriğe sahip bir dosya oluşturmak olsaydı, o zaman önemsiz olurdu , ancak veritabanına eklenmişse veya bir dosyaya eklenmişse , o zaman olmaz.

Bağımsız işlevlerin kombinasyonları bir bütün olarak bağımsız olabilir veya olmayabilir.

* Bilgisayar bilimlerinde matematikten farklı olarak idempotent kullanımı, kavram yararlı olduğu için kabul edilen matematiksel terimin yanlış kullanılmasından kaynaklanıyor gibi görünmektedir.


3
"Referans olarak şeffaf" terimi, "kimsenin umurunda" olup olmamasından daha kesin olarak tanımlanmaktadır. Biz vb bağlantı sorunları, eksik bağlantı dizeleri, zaman aşımları gibi IO sorunları göz ardı bile, o zaman bir programın birinci cümledeki o olduğunu göstermek kolaydır hala (f x, f x)ile let y = f x in (y, y)iki kat daha hızlı bu iddia olabilir disk alanı-istisnalar dışında içine çalışacaktır umursamamanız durumunda, bu kadar belirsiz bir tanımla new Random().Next(), referans olarak saydam olarak da adlandırılabiliriz çünkü heck, yine de hangi numarayı aldığım umrumda değil.
sara,

@kai: Bağlama bağlı olarak, yan etkileri görmezden gelebilirsiniz. Öte yandan, rasgele gibi bir fonksiyonun dönüş değeri bir yan etki değildir: onun ana etkisidir.
Giorgio

.NET'te @Giorgio'nun Random.Nextgerçekten de yan etkileri var. Çok çok. Yapabiliyorsanız Next, onu bir değişkene atayın ve sonra Nexttekrar arayın ve başka bir değişkene atayın, olasılıklar eşit olmazlar. Neden? Çünkü çağrı yapmak nesnedeki Nextbazı gizli iç durumları değiştirir Random. Bu, referanssal şeffaflığın zıttı olan kutuptur. "Ana etkilerin" yan etki olamayacağı iddiasını anlamıyorum. Zorunlu kodda, ana etkinin bir yan etki olmasından daha yaygındır, çünkü zorunlu programlar doğası gereği belirtilmiştir.
sara,

3

Bu tür işlevlerin nasıl çağrıldığını bilmiyorum (ya da bazı sistematik adlar var mı bile yok), ancak saf olmayan (diğer cevaplar cevaplandığı gibi) fonksiyonu çağırırım, ancak aynı parametrelerle birlikte verilirse her zaman aynı sonucu verir. parametreler "(parametrelerinin ve bazı diğer durumların işlevleriyle karşılaştırıldığında). Ben sadece fonksiyon derdim, fakat ne yazık ki programlama bağlamında "işlev" derken, gerçek işlev olmak zorunda olmayan bir şeyi kastediyoruz.


1
Kabul! Bu (gayrı resmi olarak) "işlev" in matematiksel tanımıdır, ancak sizin de dediğiniz gibi, ne yazık ki "işlev", programlama dillerinde "bir değer elde etmek için gereken bir adım adım prosedür" e daha yakın olan farklı bir şey anlamına gelir.
Andres F.

2

Temel olarak kirlilik umurunda olup olmadığına bağlıdır. Bu tablonun anlambilimi kaç giriş olduğunu umursamıyorsanız, o zaman saf. Başka, saf değil.

Ya da başka bir deyişle, saflığa dayalı optimizasyonlar program anlamını bozmadığı sürece sorun değil.

Daha gerçekçi bir örnek, eğer bu fonksiyonda hata ayıklamaya çalışıyor olsaydınız ve günlük ifadeleri eklerseniz, bu daha gerçekçi bir örnek olurdu. Teknik olarak, kayıt işlemi bir yan etkidir. Kayıtlar onu engelliyor mu? Hayır.


Şey, buna bağlı. Belki de kayıtlar bunu engeller. Örneğin, kaç kez ve ne zaman, “INFO f ()” adında kaygılarınız varsa, kayıtlarınızda belirir. Sık yaptığınız :)
Andres F.

8
-1 Günlükler önemli. Çoğu platformda, her türlü çıktı, yürütme iş parçacığını dolaylı olarak senkronize eder. Programınızın davranışı, dosya tanımlayıcılarının durumuna bağlı olarak, diğer günlük yazarlarına, bazen harici günlük yazarlarına, bazen günlük okumalarına bağlıdır. Kir kovası kadar saf.
Basilevs

@AndresF. Muhtemelen, kelimenin tam anlamıyla kaç kere umursamıyorsun. Muhtemelen sadece fonksiyon çağrıldığı kadar çok kez kaydedilmiş olmasına dikkat ediyorsundur.
DeadMG

@Basilevs İşlevin davranışı onlara bağlı değildir. Eğer log yazma başarısız olursa, devam edin.
DeadMG

2
Bu, kaydediciyi uygulama ortamının bir parçası olarak tanımlamayı seçip seçmemeniz meselesidir. Başka bir örnek olarak, sürece bir hata ayıklayıcı ekleyip bir kesme noktası koyarsam saf işlevim hala saf mı? Hata ayıklayıcısını kullanan kişinin POV'sinden, işlevin açıkça yan etkileri olduğu açıktır, ancak normalde bunun "sayılmayacağı" sözleşmesiyle bir programı analiz ederiz. Aynısı, hata ayıklama amacıyla kullanılan kütük kaydı için de geçerli olabilir (ancak buna gerek kalmaz), izlemin safsızlığı neden sakladığını tahmin ediyorum. Kritik günlüğü, örneğin denetim için, tabii ki olan önemli bir yan etki.
Steve Jessop

1

Sorulması gereken en iyi şeyin, onu nasıl arayacağımız değil, böyle bir kod parçasını nasıl analiz edeceğimiz olduğunu söyleyebilirim . Ve böyle bir analizde ilk kilit sorum şu olurdu:

  • Yan etki, fonksiyonun argümanına mı, yoksa yan etkinin sonucuna mı bağlı?
    • Hayır: "Etkili işlev", saf bir işleve, etkili bir eylem ve bunları birleştirmek için bir mekanizmaya yeniden yansıtılabilir.
    • Evet: "Etkin işlev", monadik bir sonuç veren bir işlevdir.

Bu Haskell'de açıklamak kolaydır (ve bu cümle sadece şakanın yarısıdır). "Hayır" davasına bir örnek şöyle olur:

double :: Num a => a -> IO a
double x = do
  putStrLn "I'm doubling some number"
  return (x*2)

Bu örnekte yaptığımız eylemin (satırı basmak "I'm doubling some number") xsonuç ve sonuç arasındaki ilişki üzerinde hiçbir etkisi yoktur . Bu , fonksiyonun ve etkinin aslında ortogonal olduğunu gösteren, bu şekilde yeniden sınıflandırma yapabileceğimiz anlamına gelir ( Applicativesınıfı ve *>işlecini kullanarak ):

double :: Num a => a -> IO a
double x = action *> pure (function x)
  where
    -- The pure function 
    function x = x*2  
    -- The side effect
    action = putStrLn "I'm doubling some number"

Yani bu durumda ben şahsen, bunun saf bir işlevi hesaba katabileceğiniz bir durum olduğunu söyleyebilirim. Birçok Haskell programlaması bununla ilgilidir - saf parçaların etkili koddan nasıl çıkarılacağını öğrenmek.

Saf ve etkili parçaların ortogonal olmadığı "evet" sınıfına bir örnek:

double :: Num a => a -> IO a
double x = do
  putStrLn ("I'm doubling the number " ++ show x)
  return (x*2)

Şimdi, yazdırdığınız dize, değerine bağlıdır x. Fonksiyon parçası (çarpma xikişer) hala bunu faktör böylece Ancak, tüm yürürlükte bağlı değildir:

logged :: (a -> b) -> (a -> IO x) -> IO b
logged function logger a = do
  logger a
  return (function a)

double x = logged function logger
  where function = (*2) 
        logger x putStrLn ("I'm doubling the number " ++ show x)

Başka örnekleri de söylemeye devam edebilirdim, ama umarım bu başladığım noktayı göstermek için yeterlidir: bir şeyi "çağırmazsınız", saf ve etkili parçaların ne kadar ilgili olduğunu analiz eder ve ne zaman çıkardıklarını analiz edersiniz. Avantajınız için.

Bu, Haskell'in Monadsınıfını bu kadar yaygın kullanmasının nedenlerinden biri . Monad'lar (diğer şeylerin yanı sıra) bu tür analizleri ve yeniden yapılanmaları yapmak için bir araçtır.


-2

Neden yan etkileri amaçlanan işlevleri genellikle denir effectful . Örnek https://slpopejoy.github.io/posts/Effectful01.html


Yaygın olarak kabul edilen terimden bahseden tek cevap etkilidir ve oy kullanmaya başlar .... Cehalet sanırım mutluluktur. ..
Ben Hutchison

"etkili", söz konusu yazının yazarının "yan etkilere sahip olmak" anlamına geldiğini belirten bir kelimedir. Kendisi diyor.
Robert Harvey

Googling effectful işlevini delil onun yaygın olarak kullanılan terim bol ortaya koymaktadır. Blog yazısı, tanım olarak değil, pek çok örnekten biri olarak verildi. Saf fonksiyonların varsayılan olduğu fonksiyonel programlama çevrelerinde, kasıtlı olarak yan etki fonksiyonlarını tanımlamak için pozitif bir terime ihtiyaç vardır. Yani sadece saflığın yokluğundan daha fazlası . Etkili bu terimdir. Şimdi, kendinizi eğitimli olarak düşünün.
Ben Hutchison,

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.