Apakah fungsi yang memanggil Math.random () murni?


112

Apakah fungsi berikut murni?

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

Pemahaman saya adalah bahwa fungsi murni mengikuti kondisi berikut:

  1. Ini mengembalikan nilai yang dihitung dari parameter
  2. Itu tidak melakukan pekerjaan apa pun selain menghitung nilai pengembalian

Jika definisi ini benar, apakah fungsi saya fungsi murni? Atau apakah pemahaman saya tentang apa yang mendefinisikan fungsi murni salah?


66
"Itu tidak melakukan pekerjaan apa pun selain menghitung nilai pengembalian" Tapi itu menyebut Math.random()yang mengubah status RNG.
Paul Draper

1
Poin kedua lebih seperti "ia tidak mengubah status eksternal (ke fungsi)"; dan yang pertama harus dilengkapi seperti "mengembalikan nilai SAMA yang dihitung dari parameter SAMA", seperti yang telah ditulis orang di bawah ini
MVCDS

Apakah ada gagasan tentang fungsi semipure, yang memungkinkan keacakan? Misalnya test(a,b)selalu mengembalikan objek yang sama Random(a,b)(yang dapat mewakili bilangan beton yang berbeda)? Jika Anda menyimpan Randomsimbolik, itu murni dalam pengertian klasik, jika Anda mengevaluasinya lebih awal dan memasukkan angka, mungkin sebagai semacam pengoptimalan, fungsinya masih mempertahankan beberapa "kemurnian".
jdm

1
"Siapapun yang menganggap metode aritmatika untuk menghasilkan angka acak, tentu saja, dalam keadaan berdosa." - John von Neumann
Steve Kuo

1
@jdm jika Anda mengikuti utas "semi-murni", di mana Anda menganggap fungsi modulo murni beberapa efek samping yang terdefinisi dengan baik, Anda mungkin akan menemukan monad. Selamat Datang di sisi gelap. > :)
luqui

Jawaban:


185

Tidak, tidak. Dengan input yang sama, fungsi ini akan mengembalikan nilai yang berbeda. Dan kemudian Anda tidak dapat membuat 'tabel' yang memetakan input dan output.

Dari artikel Wikipedia untuk fungsi Pure :

Fungsi selalu mengevaluasi nilai hasil yang sama dengan nilai argumen yang sama. Nilai hasil fungsi tidak dapat bergantung pada informasi atau status tersembunyi yang dapat berubah saat eksekusi program berlangsung atau antara eksekusi program yang berbeda, juga tidak dapat bergantung pada input eksternal apa pun dari perangkat I / O

Juga, hal lain adalah bahwa fungsi murni dapat diganti dengan tabel yang mewakili pemetaan dari input dan output, seperti yang dijelaskan di thread ini .

Jika Anda ingin menulis ulang fungsi ini dan mengubahnya menjadi fungsi murni, Anda juga harus meneruskan nilai acak sebagai argumen

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

lalu menyebutnya seperti ini (contoh, dengan 2 dan 5 sebagai min dan max):

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

2
Bagaimana jika Anda harus melakukan seed ulang generator acak setiap kali berada di dalam fungsi sebelum memanggil Math.random?
cs95

16
@ cᴏʟᴅsᴘᴇᴇᴅ Meskipun demikian, itu masih memiliki efek samping (mengubah Math.randomhasil di masa mendatang ); agar menjadi murni, Anda harus entah bagaimana menyimpan status RNG saat ini, memasang kembali, memanggil Math.random, dan memulihkannya ke status sebelumnya.
LegionMammal978

2
@ cᴏʟᴅsᴘᴇᴇᴅ Semua RNG yang dihitung didasarkan pada keacakan palsu. Sesuatu harus berjalan di bawahnya yang menyebabkannya tampak acak dan Anda tidak dapat menjelaskannya, membuatnya tidak murni. Juga, dan mungkin yang lebih penting untuk pertanyaan Anda, Anda tidak bisa
memasukkan

14
@ LegionMammal978… dan lakukan secara atomis.
wchargin

2
@ cᴏʟᴅsᴘᴇᴇᴅ Ada cara untuk memiliki RNG yang beroperasi dengan fungsi murni, tetapi ini melibatkan penyampaian status RNG ke fungsi dan memiliki fungsi mengembalikan status RNG pengganti, yang merupakan cara Haskell (bahasa pemrograman fungsional yang memberlakukan kemurnian fungsional) mencapai Itu.
Pharap

50

Jawaban sederhana untuk pertanyaan Anda adalah yang Math.random()melanggar aturan # 2.

Banyak jawaban lain di sini yang menunjukkan bahwa keberadaan Math.random()sarana fungsi ini tidak murni. Tapi saya pikir ada baiknya mengatakan mengapa Math.random() fungsi taints yang menggunakannya.

Seperti semua pembuat nomor pseudorandom, Math.random() dimulai dengan nilai "seed". Kemudian menggunakan nilai itu sebagai titik awal untuk rangkaian manipulasi bit tingkat rendah atau operasi lain yang menghasilkan keluaran yang tidak dapat diprediksi (tetapi tidak benar - benar acak ).

Dalam JavaScript, proses yang terlibat bergantung pada implementasi, dan tidak seperti banyak bahasa lain, JavaScript menyediakan tidak cara untuk memilih benih :

Implementasi memilih benih awal untuk algoritma generasi nomor acak; itu tidak dapat dipilih atau diatur ulang oleh pengguna.

Itulah mengapa fungsi ini tidak murni: JavaScript pada dasarnya menggunakan parameter fungsi implisit yang tidak dapat Anda kendalikan. Ini membaca parameter itu dari data yang dihitung dan disimpan di tempat lain, dan karena itu melanggar aturan # 2 dalam definisi Anda.

Jika Anda ingin menjadikan ini fungsi murni, Anda dapat menggunakan salah satu generator nomor acak alternatif yang dijelaskan di sini . Panggil generator itu seedable_random. Dibutuhkan satu parameter (benih) dan mengembalikan nomor "acak". Tentu saja, angka ini sama sekali tidak acak; itu secara unik ditentukan oleh benih. Itulah mengapa ini adalah fungsi murni. Keluaran dari seedable_randomhanya "acak" dalam arti sulit memprediksi keluaran berdasarkan masukan.

Versi murni dari fungsi ini perlu mengambil tiga parameter:

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

Untuk tiga kali lipat (min, max, seed)parameter apa pun, ini akan selalu mengembalikan hasil yang sama.

Catatan bahwa jika Anda ingin output seedable_randommenjadi benar-benar acak, Anda akan perlu untuk menemukan cara untuk mengacak benih! Dan strategi apa pun yang Anda gunakan pasti tidak murni, karena itu mengharuskan Anda mengumpulkan informasi dari sumber di luar fungsi Anda. Seperti yang diingatkan oleh mtraceur dan jpmc26 , ini mencakup semua pendekatan fisik: generator nomor acak perangkat keras , webcam dengan penutup lensa , pengumpul kebisingan atmosfer - bahkan lampu lava . Semua ini melibatkan penggunaan data yang dihitung dan disimpan di luar fungsi.


8
Math.random () tidak hanya membaca "seed" -nya tetapi juga memodifikasinya, sehingga panggilan berikutnya akan mengembalikan sesuatu yang berbeda. Bergantung pada, dan memodifikasi, keadaan statis pasti buruk untuk fungsi murni.
Nate Eldredge

2
@NateEldredge, lumayan! Meskipun hanya membaca nilai yang bergantung pada implementasi sudah cukup untuk merusak kemurnian. Misalnya, pernahkah memperhatikan bagaimana hash Python 3 tidak stabil di antara proses?
senderle

2
Bagaimana jawaban ini akan berubah jika Math.randomtidak menggunakan PRNG tetapi diimplementasikan menggunakan perangkat keras RNG? RNG perangkat keras tidak benar-benar memiliki status dalam arti normal, tetapi ia menghasilkan nilai acak (dan dengan demikian output fungsi masih berbeda terlepas dari inputnya), bukan?
mtraceur

@mtraceur, itu benar. Tetapi menurut saya jawabannya tidak akan banyak berubah. Sebenarnya, inilah mengapa saya tidak menghabiskan waktu berbicara tentang "negara bagian" dalam jawaban saya. Membaca dari perangkat keras RNG juga berarti membaca dari "data yang dihitung dan disimpan di tempat lain." Hanya saja data tersebut dihitung dan disimpan dalam media fisik komputer itu sendiri saat berinteraksi dengan lingkungannya.
pengirim

1
Logika yang sama ini berlaku bahkan untuk skema pengacakan yang lebih canggih, bahkan skema seperti kebisingan atmosfer Random.org . +1
jpmc26

38

Fungsi murni adalah fungsi yang nilai kembaliannya hanya ditentukan oleh nilai masukannya, tanpa efek samping yang dapat diamati

Dengan menggunakan Math.random, Anda menentukan nilainya dengan sesuatu selain nilai input. Ini bukan fungsi murni.

sumber


25

Tidak, ini bukan fungsi murni karena keluarannya tidak hanya bergantung pada masukan yang disediakan (Math.random () dapat mengeluarkan nilai apa pun), sedangkan fungsi murni harus selalu menampilkan nilai yang sama untuk masukan yang sama.

Jika suatu fungsi murni, aman untuk mengoptimalkan beberapa panggilan dengan input yang sama dan hanya menggunakan kembali hasil panggilan sebelumnya.

PS bagi saya setidaknya dan bagi banyak orang lainnya, redux membuat istilah fungsi murni populer.Langsung dari dokumen redux :

Hal-hal yang tidak boleh Anda lakukan di dalam peredam:

  • Mutasikan argumennya;

  • Lakukan efek samping seperti panggilan API dan transisi perutean;

  • Panggil fungsi non-murni, misalnya Date.now () atau Math.random ().


3
Meskipun orang lain telah memberikan jawaban yang bagus, tetapi saya tidak bisa menahan diri ketika redux doc muncul di benak saya dan Math.random () secara khusus disebutkan di dalamnya :)
Shubhnik Singh

20

Dari sudut pandang matematis, tanda tangan Anda tidak

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

tapi

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

dimana environmentmampu memberikan hasilMath.random() . Dan sebenarnya menghasilkan nilai acak akan mengubah lingkungan sebagai efek samping, jadi Anda juga mengembalikan lingkungan baru, yang tidak sama dengan yang pertama!

Dengan kata lain, jika Anda memerlukan jenis input yang tidak berasal dari argumen awal ( <number, number>bagian), maka Anda perlu disediakan lingkungan eksekusi (yang dalam contoh ini menyediakan status untuk Math). Hal yang sama berlaku untuk hal lain yang disebutkan oleh jawaban lain, seperti I / O atau semacamnya.


Sebagai analogi, Anda juga dapat memperhatikan ini adalah bagaimana pemrograman berorientasi objek dapat direpresentasikan - jika kita katakan, mis

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

lalu sebenarnya kami menggunakan

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

dengan objek yang metodenya dipanggil menjadi bagian dari lingkungan. Dan mengapa SomeClassbagian dari hasil? Karena somethingkeadaan bisa berubah juga!


7
Lebih buruk lagi, lingkungan juga bermutasi, jadi test: <environment, number, number> -> <environment, number>seharusnya
Bergi

1
Saya tidak yakin contoh OO sangat mirip. a.F(b, c)dapat dilihat sebagai gula sintaksis F(a, b, c)dengan aturan khusus untuk dikirim ke definisi yang kelebihan beban Fberdasarkan jenis a(ini sebenarnya bagaimana Python merepresentasikannya). Tetapi amasih eksplisit di kedua notasi, sedangkan lingkungan dalam fungsi non-murni tidak pernah disebutkan dalam kode sumber.
IMSoP


10

Selain jawaban lain yang dengan benar menunjukkan bagaimana fungsi ini non-deterministik, ini juga memiliki efek samping: ini akan menyebabkan panggilan di masa mendatang math.random()untuk mengembalikan jawaban yang berbeda. Dan generator bilangan acak yang tidak memiliki properti itu biasanya akan melakukan beberapa jenis I / O, seperti membaca dari perangkat acak yang disediakan oleh OS. Salah satunya adalah verboten untuk fungsi murni.


7

Tidak, tidak. Anda tidak dapat mengetahui hasilnya sama sekali, jadi potongan kode ini tidak dapat diuji. Untuk membuat kode itu dapat diuji, Anda perlu mengekstrak komponen yang menghasilkan nomor acak:

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

Sekarang, Anda dapat memalsukan generator dan menguji kode Anda dengan benar:

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

Dan di kode "produksi" Anda:

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

1
▲ untuk pemikiran Anda tentang kemampuan untuk diuji. Dengan sedikit hati-hati, Anda juga dapat membuat pengujian berulang sambil menerima a util.Random, yang dapat Anda seed di awal uji coba untuk mengulangi perilaku lama atau untuk pengujian baru (tetapi berulang). Jika multi-threading, Anda mungkin dapat melakukan ini di utas utama dan menggunakannya Randomuntuk menyemai utas-lokal berulang Random. Namun, seperti yang saya pahami, test(int,int,Random)tidak dianggap murni karena mengubah keadaan Random.
PJTraill

2

Apakah Anda setuju dengan yang berikut:

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

setara dengan

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

?

Soalnya, definisi murni adalah fungsi yang keluarannya tidak berubah dengan apa pun selain masukannya. Jika kami mengatakan bahwa JavaScript memiliki cara untuk memberi tag pada fungsi murni dan memanfaatkannya, pengoptimal akan diizinkan untuk menulis ulang ekspresi pertama sebagai yang kedua.

Saya memiliki pengalaman praktis dengan ini. SQL server diperbolehkan getdate()dan newid()dalam fungsi "murni" dan pengoptimal akan menghapus panggilan sesuka hati. Terkadang ini akan melakukan sesuatu yang bodoh.

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.