Math.random () 'u çağıran bir işlev saf mı?


112

Aşağıdaki saf bir işlev mi?

function test(min,max) {
   return  Math.random() * (max - min) + min;
}

Anladığım kadarıyla saf bir işlev şu koşulları takip ediyor:

  1. Parametrelerden hesaplanan bir değer döndürür
  2. Dönüş değerini hesaplamak dışında herhangi bir iş yapmaz

Bu tanım doğruysa, işlevim saf bir işlev mi? Yoksa saf bir işlevi neyin tanımladığına dair anlayışım yanlış mı?


66
"Dönüş değerini hesaplamaktan başka bir iş yapmaz" Ama Math.random()RNG'nin durumunu değiştiren çağırır .
Paul Draper

1
İkinci nokta daha çok "harici (işleve) durumu değiştirmez" gibidir; ve birincisi, insanların aşağıya
yazdığı

Rasgeleliğe izin veren bir yarı-saflık işlevi kavramı var mı? Örneğin, test(a,b)her zaman aynı nesneyi döndürür Random(a,b)(hangisi farklı somut sayıları temsil edebilir)? RandomSembolik tutarsanız , klasik anlamda saftır, onu erken değerlendirip sayılar koyarsanız, belki bir tür optimizasyon olarak, işlev hala biraz "saflık" tutar.
jdm

1
"Rastgele rakamlar üretmenin aritmetik yöntemlerini düşünen herkes elbette günah durumundadır." - John von Neumann
Steve Kuo

1
@jdm "yarı saf" dizisini takip ederseniz, burada işlevlerin saf modulo bazı iyi tanımlanmış yan etkiler olduğunu düşünürseniz, monadlar icat edebilirsiniz. Karanlık tarafa hoşgeldin. > :)
luqui

Yanıtlar:


185

Hayır değil. Aynı girdi verildiğinde, bu işlev farklı değerler döndürecektir. Ve sonra, girdi ve çıktıları eşleyen bir 'tablo' oluşturamazsınız.

Pure işlevi için Wikipedia makalesinden :

İşlev, aynı bağımsız değişken değeri / değerleri verildiğinde her zaman aynı sonuç değerini değerlendirir. Fonksiyon sonuç değeri, program yürütülürken veya programın farklı yürütmeleri arasında değişebilecek herhangi bir gizli bilgiye veya duruma bağlı olamaz veya G / Ç cihazlarından gelen herhangi bir harici girişe bağlı olamaz.

Ayrıca, bu iş parçacığında açıklandığı gibi, bir başka şey, saf bir fonksiyonun, giriş ve çıkıştan eşlemeyi temsil eden bir tablo ile değiştirilebilmesidir .

Bu işlevi yeniden yazmak ve onu saf bir işleve dönüştürmek istiyorsanız, rastgele değeri de bir argüman olarak iletmelisiniz.

function test(random, min, max) {
   return random * (max - min) + min;
}

ve sonra bunu şu şekilde adlandırın (örnek, min ve maks olarak 2 ve 5 ile):

test( Math.random(), 2, 5)

2
Aramadan önce işlevin içinde her seferinde rastgele oluşturucuyu yeniden tohumlasanız ne olur Math.random?
cs95

16
@ cᴏʟᴅsᴘᴇᴇᴅ O zaman bile, yine de yan etkileri olacaktır (gelecekteki Math.randomçıktıyı değiştirir ); saf olması için, mevcut RNG durumunu bir şekilde kaydetmeniz, yeniden beslemeniz, aramanız Math.randomve önceki durumuna geri yüklemeniz gerekir.
LegionMammal978

2
@ cᴏʟᴅsᴘᴇᴇᴅ Tüm hesaplanan RNG, sahte rastlantısallığa dayanır. Altında rastgele görünmesine neden olan bir şey çalışıyor olmalı ve bunu açıklayamazsınız, bu da onu saflaştırmaz. Ayrıca ve muhtemelen sorunuz için daha önemli olan, Math.random'u
tohumlayamazsınız

14
@ LegionMammal978… ve bunu atomik olarak yapın.
wchargin

2
@ cᴏʟᴅsᴘᴇᴇᴅ Saf işlevlerle çalışan RNG'lere sahip olmanın yolları vardır, ancak bu, RNG durumunu işleve geçirmeyi ve işlevin değiştirilen RNG durumuna dönmesini içerir, bu da Haskell'in (işlevsel saflığı zorlayan işlevsel bir programlama dili) nasıl elde ettiğini gösterir. o.
Pharap

51

Sorunuzun basit cevabı, 2. kuralı ihlal etmektir Math.random().

Buradaki diğer birçok cevap Math.random(), bu fonksiyonun saf olmadığı anlamına gelen araçların varlığına işaret etti . Ama bence lekelerin neden Math.random() onu kullanan işlev gördüğünü söylemeye değer .

Tüm sözde rasgele sayı üreteçleri gibi, Math.random()bir "tohum" değeriyle başlar. Daha sonra bu değeri, öngörülemeyen (ancak gerçekten rastgele olmayan ) bir çıktıyla sonuçlanan düşük seviyeli bit manipülasyonları veya diğer işlemler zinciri için başlangıç ​​noktası olarak kullanır .

JavaScript'te, ilgili süreç uygulamaya bağlıdır ve diğer birçok dilden farklı olarak JavaScript, , tohumu seçmek için hiçbir yol sunmaz :

Uygulama, rasgele sayı üretme algoritmasının ilk tohumunu seçer; kullanıcı tarafından seçilemez veya sıfırlanamaz.

Bu işlevin saf olmasının nedeni budur: JavaScript aslında üzerinde hiçbir kontrolünüz olmayan örtük bir işlev parametresi kullanıyor. Bu parametreyi başka yerde hesaplanan ve depolanan verilerden okuyor ve bu nedenle tanımınızdaki 2. kuralı ihlal ediyor.

Bunu saf bir işlev yapmak istiyorsanız, burada açıklanan alternatif rasgele sayı üreticilerinden birini kullanabilirsiniz . Jeneratörü ara seedable_random. Bir parametre (çekirdek) alır ve "rastgele" bir sayı döndürür. Tabii ki, bu sayı gerçekten rastgele değil; tohum tarafından benzersiz bir şekilde belirlenir. Bu yüzden bu saf bir işlevdir. Çıktı seedable_random, girdiye dayalı olarak çıktıyı tahmin etmenin zor olması anlamında yalnızca "rastgele" dir.

Bu işlevin saf versiyonunun üç parametre alması gerekir :

function test(min, max, seed) {
   return  seedable_random(seed) * (max - min) + min;
}

Herhangi bir üçlü için (min, max, seed) parametre için, bu her zaman aynı sonucu verecektir.

Eğer çıktısını isteseydi unutmayın seedable_randomolmak gerçekten rastgele, Eğer tohum rastgele bir yolunu bulmak gerekiyordu! Ve hangi stratejiyi kullanırsanız kullanın, kaçınılmaz olarak saf olmayacaktır, çünkü işlevinizin dışındaki bir kaynaktan bilgi toplamanızı gerektirecektir. As mtraceur ve jpmc26 : bana hatırlatmak, bu tüm fiziksel yaklaşımlar içeren donanım rasgele sayı üreteçleri , mercek kapakları ile kamerası , atmosferik gürültü toplayıcıları - hatta lav lambaları . Tüm bunlar, işlevin dışında hesaplanan ve saklanan verileri kullanmayı içerir.


8
Math.random () sadece "tohum" unu okumakla kalmaz, aynı zamanda onu değiştirir, böylece bir sonraki çağrı farklı bir şey döndürür. Statik duruma bağlı olarak ve değiştirilerek, saf bir işlev için kesinlikle kötüdür.
Nate Eldredge

2
@NateEldredge, oldukça öyle! Gerçi sadece uygulamaya bağlı bir değeri okumak saflığı kırmak için yeterlidir. Örneğin, Python 3 hashlerinin süreçler arasında nasıl kararlı olmadığını fark ettiniz mi?
2017

2
Math.randomBir PRNG kullanmasaydı, bunun yerine bir donanım RNG kullanılarak uygulansaydı bu cevap nasıl değişirdi ? Donanım RNG'si normal anlamda bir duruma sahip değil, ancak rastgele değerler üretiyor (ve bu nedenle işlev çıkışı, girdiden bağımsız olarak hala farklıdır), değil mi?
mtraceur

@mtraceur, bu doğru. Ama cevabın pek değişeceğini sanmıyorum. Aslında bu yüzden cevabımda "durum" hakkında konuşmak için zaman harcamıyorum. Bir donanım RNG'sinden okumak aynı zamanda "başka yerde hesaplanan ve saklanan verilerden" okumak anlamına gelir. Sadece verilerin hesaplanması ve bilgisayarın kendi ortamıyla etkileşime girerken fiziksel ortamında depolanmasıdır.
17:28 gönderen

1
Aynı mantık, Random.org'un atmosferik gürültüsü gibi daha karmaşık randomizasyon şemaları için bile geçerlidir . +1
jpmc26

38

Saf fonksiyon, dönüş değerinin gözlemlenebilir yan etkiler olmaksızın yalnızca girdi değerleriyle belirlendiği bir fonksiyondur.

Math.random kullanarak, değerini girdi değerlerinden başka bir şeyle belirliyorsunuz. Bu saf bir işlev değil.

kaynak


25

Hayır, saf bir işlev değildir çünkü çıktısı yalnızca sağlanan girdiye bağlı değildir (Math.random () herhangi bir değeri verebilir), ancak saf işlevler aynı girdiler için her zaman aynı değeri vermelidir.

Bir işlev safsa, birden fazla aramayı aynı girişlerle optimize etmek ve yalnızca önceki bir aramanın sonucunu yeniden kullanmak güvenlidir.

Not: Redux, en azından benim için ve diğerleri için saf fonksiyon terimini popüler hale getirdi . Redux belgelerinden doğruca :

Bir düşürücü içinde asla yapmamanız gereken şeyler:

  • Argümanlarını değiştir;

  • API çağrıları ve yönlendirme geçişleri gibi yan etkiler gerçekleştirin;

  • Saf olmayan işlevleri çağırın, örneğin Date.now () veya Math.random ().


3
Başkaları harika cevaplar vermesine rağmen, redux doc's aklıma geldiğinde ve Math.random () özellikle bunlarda bahsedildiğinde kendime karşı koyamadım :)
Shubhnik Singh

20

Matematiksel açıdan, imzanız değil

test: <number, number> -> <number>

fakat

test: <environment, number, number> -> <environment, number>

nerede environmentsonuç verebilir Math.random(). Ve aslında rastgele değerin üretilmesi ortamı bir yan etki olarak değiştirir, bu yüzden birincisine eşit olmayan yeni bir ortam da döndürürsünüz!

Diğer bir deyişle, ilk argümanlardan ( <number, number>kısım) gelmeyen herhangi bir girdiye ihtiyacınız varsa , o zaman yürütme ortamının (bu örnekte durumu sağlayan Math) sağlanması gerekir. Aynısı, G / Ç veya benzeri diğer yanıtlarda belirtilen diğer şeyler için de geçerlidir.


Bir benzetme olarak, nesneye yönelik programlamanın nasıl temsil edilebileceğini de fark edebilirsiniz - örneğin,

SomeClass something
T result = something.foo(x, y)

o zaman aslında kullanıyoruz

foo: <something: SomeClass, x: Object, y: Object> -> <SomeClass, T>

Metodu çağrılan nesne ortamın bir parçası olarak. Ve neden SomeClasssonucun parçası? Çünkü somethingdurumu da değişebilirdi!


7
Daha da kötüsü, ortam da mutasyona uğradı, bu yüzden test: <environment, number, number> -> <environment, number>öyle olmalı
Bergi

1
OO örneğinin çok benzer olduğundan emin değilim. türüne bağlı olarak aşırı yüklenmiş tanımları göndermek için özel bir kural ile a.F(b, c)sözdizimsel şeker olarak görülebilir (bu aslında Python'un temsil ettiği şekildedir). Ancak , her iki gösterimde de hala açıktır, oysa saf olmayan bir işlevdeki ortam hiçbir zaman kaynak kodunda belirtilmez. F(a, b, c)Faa
IMSoP


10

Bu fonksiyonun ne kadar deterministik olmadığını doğru bir şekilde gösteren diğer cevaplara ek olarak, bir de yan etkisi vardır: Gelecekteki çağrıların math.random()farklı bir cevap vermesine neden olacaktır . Ve bu özelliğe sahip olmayan bir rastgele sayı üreteci, genellikle işletim sistemi tarafından sağlanan rastgele bir cihazdan okumak gibi bir tür G / Ç gerçekleştirir. Ya saf bir işlev için verboten.


7

Hayır, değil. Sonucu hiçbir şekilde anlayamazsınız, bu nedenle bu kod parçası test edilemez. Bu kodu test edilebilir hale getirmek için, rastgele sayıyı oluşturan bileşeni çıkarmanız gerekir:

function test(min, max, generator) {
  return  generator() * (max - min) + min;
}

Şimdi, jeneratörle dalga geçebilir ve kodunuzu doğru şekilde test edebilirsiniz:

const result = test(1, 2, () => 3);
result == 4 //always true

Ve "üretim" kodunuzda:

const result = test(1, 2, Math.random);

1
▲ test edilebilirlik düşünceniz için. Biraz özen util.Randomgöstererek, bir test çalışmasının başlangıcında eski davranışı tekrarlamak veya yeni (ancak tekrarlanabilir) bir çalışma için tohumlayabileceğiniz, a'yı kabul ederken tekrarlanabilir testler de üretebilirsiniz . Çoklu iş parçacığı kullanılıyorsa, bunu ana iş parçacığında yapabilir ve Randomyinelenebilir iş parçacığı yerel'leri tohumlamak için kullanabilirsiniz Random. Ancak, anladığım kadarıyla test(int,int,Random)saf kabul edilmiyor çünkü Random.
PJTraill

2

Aşağıdakilerden memnun musunuz:

return ("" + test(0,1)) + test(0,1);

eşdeğer olmak

var temp = test(0, 1);
return ("" + temp) + temp;

?

Safın tanımı, çıktısı girdilerinden başka hiçbir şeyle değişmeyen bir fonksiyondur. JavaScript'in bir işlevi saf etiketlemek ve bundan yararlanmak için bir yolu olduğunu söylersek, optimize edicinin ilk ifadeyi ikinci ifade olarak yeniden yazmasına izin verilir.

Bununla ilgili pratik tecrübem var. SQL sunucusuna izin verilen getdate()ve newid()"saf" işlevlerde ve eniyileyici, çağrıları istediği zaman tekilleştirebilir. Bazen bu aptalca bir şey yapar.

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.