เป็นฟังก์ชันที่เรียก Math.random () บริสุทธิ์หรือไม่?


112

ต่อไปนี้เป็นฟังก์ชันบริสุทธิ์หรือไม่?

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

ความเข้าใจของฉันคือฟังก์ชันบริสุทธิ์เป็นไปตามเงื่อนไขเหล่านี้:

  1. ส่งคืนค่าที่คำนวณจากพารามิเตอร์
  2. มันไม่ทำงานอื่นใดนอกจากการคำนวณมูลค่าที่ส่งคืน

ถ้าคำจำกัดความนี้ถูกต้องฟังก์ชันของฉันเป็นฟังก์ชันบริสุทธิ์หรือไม่ หรือความเข้าใจของฉันเกี่ยวกับสิ่งที่กำหนดฟังก์ชันบริสุทธิ์ไม่ถูกต้อง?


66
"มันไม่ได้ทำงานอื่นใดนอกจากการคำนวณมูลค่าที่ส่งคืน" แต่มันเรียกMath.random()สิ่งที่เปลี่ยนสถานะของ RNG
Paul Draper

1
จุดที่สองเหมือน "มันไม่เปลี่ยนสถานะภายนอก (เป็นฟังก์ชัน)"; และอย่างแรกควรเติมเต็มเช่น "ส่งคืนค่า SAME ที่คำนวณจากพารามิเตอร์ SAME" ตามที่มีคนเขียนไว้ด้านล่าง
MVCDS

มีแนวคิดเรื่องฟังก์ชัน semipure ที่อนุญาตให้มีการสุ่มหรือไม่? เช่นtest(a,b)ส่งคืนวัตถุเดียวกันเสมอRandom(a,b)(ซึ่งสามารถแทนจำนวนคอนกรีตที่แตกต่างกันได้)? หากคุณใช้Randomสัญลักษณ์เป็นสิ่งที่บริสุทธิ์ในความหมายแบบคลาสสิกหากคุณประเมินค่านี้ แต่เนิ่นๆและใส่ตัวเลขอาจเป็นการเพิ่มประสิทธิภาพฟังก์ชันนี้ยังคงรักษา "ความบริสุทธิ์" ไว้
jdm

1
"ใครก็ตามที่คิดว่าวิธีการทางคณิตศาสตร์ในการสร้างตัวเลขสุ่มย่อมตกอยู่ในความบาป" - John von Neumann
Steve Kuo

1
@jdm ถ้าคุณทำตามหัวข้อ "กึ่งบริสุทธิ์" ซึ่งคุณคิดว่าฟังก์ชั่นโมดูโลบริสุทธิ์ของผลข้างเคียงบางอย่างที่กำหนดไว้อย่างดีคุณอาจต้องประดิษฐ์ monads ขึ้นมา ยินดีต้อนรับสู่ด้านมืด > :)
luqui

คำตอบ:


185

ไม่มันไม่ใช่. ด้วยอินพุตเดียวกันฟังก์ชันนี้จะส่งคืนค่าที่แตกต่างกัน จากนั้นคุณไม่สามารถสร้าง 'ตาราง' ที่แมปอินพุตและเอาต์พุตได้

จากบทความ Wikipedia สำหรับฟังก์ชัน Pure :

ฟังก์ชันจะประเมินค่าผลลัพธ์เดียวกันเสมอโดยให้ค่าอาร์กิวเมนต์เดียวกัน ค่าผลลัพธ์ของฟังก์ชันไม่สามารถขึ้นอยู่กับข้อมูลหรือสถานะที่ซ่อนอยู่ซึ่งอาจเปลี่ยนแปลงได้ในขณะที่การดำเนินการของโปรแกรมดำเนินไปหรือระหว่างการดำเนินการอื่น ๆ ของโปรแกรมและไม่สามารถขึ้นอยู่กับอินพุตภายนอกใด ๆ จากอุปกรณ์ I / O

นอกจากนี้อีกสิ่งหนึ่งคือฟังก์ชันบริสุทธิ์สามารถถูกแทนที่ด้วยตารางซึ่งแสดงถึงการแมปจากอินพุตและเอาต์พุตตามที่อธิบายไว้ใน เธรดนี้

หากคุณต้องการเขียนฟังก์ชันนี้ใหม่และเปลี่ยนเป็นฟังก์ชันบริสุทธิ์คุณควรส่งค่าสุ่มเป็นอาร์กิวเมนต์ด้วย

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

จากนั้นเรียกแบบนี้ (ตัวอย่างเช่นโดยมี 2 และ 5 เป็น min และ max):

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

2
เกิดอะไรขึ้นถ้าคุณได้อีกครั้งเมล็ดเครื่องกำเนิดไฟฟ้าแบบสุ่มทุกครั้งที่ฟังก์ชั่นภายในก่อนที่จะเรียกMath.random?
cs95

16
@ cᴏʟᴅsᴘᴇᴇᴅถึงอย่างนั้นก็ยังคงมีผลข้างเคียง (เปลี่ยนMath.randomผลลัพธ์ในอนาคต); เพื่อให้เป็นไปอย่างบริสุทธิ์คุณจะต้องบันทึกสถานะ RNG ปัจจุบันส่งใหม่เรียกMath.randomและเรียกคืนกลับเป็นสถานะก่อนหน้า
LegionMammal978

2
@ cᴏʟᴅsᴘᴇᴇᴅ RNG ที่คำนวณทั้งหมดขึ้นอยู่กับการสุ่มหลอก จะต้องมีบางสิ่งบางอย่างกำลังทำงานอยู่ข้างใต้ซึ่งทำให้ดูเหมือนเป็นแบบสุ่มและคุณไม่สามารถอธิบายสิ่งนั้นได้ทำให้ไม่บริสุทธิ์ นอกจากนี้และอาจสำคัญกว่าสำหรับคำถามของคุณคุณไม่สามารถเริ่มต้น Math.random ได้
zfrisch

14
@ LegionMammal978 …และทำอย่างนั้น
wchargin

2
@ cᴏʟᴅsᴘᴇᴇᴅมีหลายวิธีในการมี RNG ที่ทำงานกับฟังก์ชันบริสุทธิ์ แต่เกี่ยวข้องกับการส่งผ่านสถานะ RNG ไปยังฟังก์ชันและการให้ฟังก์ชันส่งคืนสถานะ RNG ทดแทนซึ่งเป็นวิธีที่ Haskell (ภาษาโปรแกรมเชิงฟังก์ชันที่บังคับใช้ความบริสุทธิ์ของฟังก์ชัน) บรรลุผล มัน.
Pharap

51

คำตอบง่ายๆสำหรับคำถามของคุณคือMath.random()ละเมิดกฎ # 2

คำตอบอื่น ๆ อีกมากมายที่นี่ชี้ให้เห็นว่าการมีอยู่ของMath.random()วิธีการที่ฟังก์ชันนี้ไม่บริสุทธิ์ แต่ฉันคิดว่ามันคุ้มที่จะบอกว่าทำไม Math.random() taints ถึงใช้งาน

เช่นเดียวกับตัวสร้างหมายเลขหลอกทั่วไปMath.random()เริ่มต้นด้วยค่า "เมล็ดพันธุ์" จากนั้นจะใช้ค่านั้นเป็นจุดเริ่มต้นสำหรับห่วงโซ่ของการปรับบิตระดับต่ำหรือการดำเนินการอื่น ๆ ที่ส่งผลให้ผลลัพธ์ที่ไม่สามารถคาดเดาได้ (แต่ไม่ใช่แบบสุ่มจริงๆ)

ใน JavaScript กระบวนการที่เกี่ยวข้องขึ้นอยู่กับการนำไปใช้งานและแตกต่างจากภาษาอื่น ๆ JavaScript ไม่มีวิธีเลือกเมล็ดพันธุ์ :

การใช้งานจะเลือกเมล็ดเริ่มต้นให้กับอัลกอริทึมการสร้างตัวเลขแบบสุ่ม ผู้ใช้ไม่สามารถเลือกหรือรีเซ็ตได้

นั่นเป็นเหตุผลที่ฟังก์ชันนี้ไม่บริสุทธิ์: JavaScript ใช้พารามิเตอร์ของฟังก์ชันโดยนัยที่คุณไม่สามารถควบคุมได้ มันอ่านพารามิเตอร์นั้นจากข้อมูลที่คำนวณและเก็บไว้ที่อื่นดังนั้นจึงละเมิดกฎ # 2 ในคำจำกัดความของคุณ

หากคุณต้องการที่จะทำให้เรื่องนี้เป็นฟังก์ชั่นที่บริสุทธิ์คุณสามารถใช้เป็นหนึ่งในทางเลือกที่เครื่องกำเนิดไฟฟ้าจำนวนสุ่มอธิบายไว้ที่นี่ เรียกเครื่องกำเนิดไฟฟ้าseedable_randomนั้น ใช้เวลาหนึ่งพารามิเตอร์ (เมล็ดพันธุ์) และส่งกลับตัวเลข "สุ่ม" แน่นอนว่าตัวเลขนี้ไม่ได้สุ่มเลย มันถูกกำหนดโดยเมล็ดพันธุ์โดยเฉพาะ นั่นเป็นเหตุผลว่าทำไมจึงเป็นฟังก์ชันที่บริสุทธิ์ ผลลัพธ์ของseedable_randomเป็นเพียง "สุ่ม" ในแง่ที่ว่าการคาดเดาผลลัพธ์ตามอินพุตนั้นทำได้ยาก

เวอร์ชันบริสุทธิ์ของฟังก์ชันนี้จะต้องใช้พารามิเตอร์สามตัว :

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

สำหรับ(min, max, seed)พารามิเตอร์สามตัวใด ๆ ที่กำหนดสิ่งนี้จะให้ผลลัพธ์เดียวกันเสมอ

โปรดทราบว่าหากคุณต้องการให้ผลลัพธ์seedable_randomเป็นแบบสุ่มอย่างแท้จริงคุณต้องหาวิธีสุ่มเมล็ดพันธุ์! และกลยุทธ์ใดก็ตามที่คุณใช้ย่อมไม่บริสุทธิ์เพราะคุณต้องรวบรวมข้อมูลจากแหล่งที่มานอกหน้าที่ของคุณ ในฐานะที่เป็นmtraceurและjpmc26เตือนฉันนี้รวมถึงทุกวิธีทางกายภาพ: เครื่องกำเนิดไฟฟ้าจำนวนสุ่มฮาร์ดแวร์ , เว็บแคมกับหมวกเลนส์ , สะสมเสียงบรรยากาศ - แม้โคมไฟลาวา สิ่งเหล่านี้เกี่ยวข้องกับการใช้ข้อมูลที่คำนวณและจัดเก็บนอกฟังก์ชัน


8
Math.random () ไม่เพียง แต่อ่าน "seed" ของมันเท่านั้น แต่ยังแก้ไขด้วยเพื่อให้การเรียกครั้งถัดไปจะส่งคืนสิ่งที่แตกต่างออกไป ขึ้นอยู่กับและการปรับเปลี่ยนสถานะคงที่ไม่ดีอย่างแน่นอนสำหรับฟังก์ชันบริสุทธิ์
Nate Eldredge

2
@NateEldredge เลยทีเดียว! แม้ว่าการอ่านค่าขึ้นอยู่กับการนำไปใช้งานก็เพียงพอแล้วที่จะทำลายความบริสุทธิ์ ตัวอย่างเช่นเคยสังเกตไหมว่าการแฮช Python 3 ไม่เสถียรระหว่างกระบวนการอย่างไร
ส่ง

2
คำตอบนี้จะเปลี่ยนไปอย่างไรหากMath.randomไม่ได้ใช้ PRNG แต่ใช้ฮาร์ดแวร์ RNG แทน ฮาร์ดแวร์ RNG ไม่มีสถานะในความหมายปกติ แต่จะสร้างค่าแบบสุ่ม (ดังนั้นฟังก์ชันเอาต์พุตยังคงแตกต่างกันโดยไม่คำนึงถึงอินพุต) ใช่ไหม
mtraceur

@mtraceur ถูกต้อง แต่ฉันไม่คิดว่าคำตอบจะเปลี่ยนไปมากนัก อันที่จริงนี่คือสาเหตุที่ฉันไม่ใช้เวลาพูดถึง "รัฐ" ในคำตอบของฉัน การอ่านจากฮาร์ดแวร์ RNG ยังหมายถึงการอ่านจาก "ข้อมูลที่คำนวณและจัดเก็บไว้ที่อื่น" เป็นเพียงข้อมูลที่คำนวณและจัดเก็บไว้ในสื่อทางกายภาพของคอมพิวเตอร์เองเมื่อมีการโต้ตอบกับสภาพแวดล้อม
ส่ง

1
ตรรกะเดียวกันนี้ใช้แม้แผนการสุ่มความซับซ้อนมากขึ้นคนที่ชอบเสียงบรรยากาศ Random.org ของ +1
jpmc26

38

ฟังก์ชันบริสุทธิ์คือฟังก์ชันที่ค่าที่ส่งคืนจะถูกกำหนดโดยค่าอินพุตเท่านั้นโดยไม่มีผลข้างเคียงที่สังเกตได้

ด้วยการใช้ Math.random คุณกำลังกำหนดมูลค่าโดยสิ่งอื่นที่ไม่ใช่ค่าอินพุต ไม่ใช่หน้าที่เพียว ๆ

แหล่ง


25

ไม่มีก็ไม่ได้เป็นฟังก์ชั่นที่บริสุทธิ์เพราะผลลัพธ์ที่ได้ไม่ได้ขึ้นอยู่เพียงกับการป้อนข้อมูลที่ให้ไว้ (Math.random () สามารถส่งออกค่าใด ๆ ) ในขณะที่ฟังก์ชั่นที่บริสุทธิ์ควรเอาท์พุทเสมอค่าเดียวกันสำหรับปัจจัยการผลิตเดียวกัน

หากฟังก์ชั่นบริสุทธิ์ก็ปลอดภัยที่จะเพิ่มประสิทธิภาพการโทรหลายสายด้วยอินพุตเดียวกันและใช้ผลลัพธ์ของการโทรก่อนหน้านี้ซ้ำ

PS สำหรับฉันอย่างน้อยและสำหรับคนอื่น ๆ อีกมากมาย redux ทำให้คำว่าฟังก์ชันบริสุทธิ์เป็นที่นิยม ตรงจากเอกสาร redux :

สิ่งที่คุณไม่ควรทำภายในตัวลด:

  • เปลี่ยนข้อโต้แย้ง

  • ทำผลข้างเคียงเช่นการเรียก API และการเปลี่ยนเส้นทาง

  • เรียกฟังก์ชันที่ไม่บริสุทธิ์เช่น Date.now () หรือ Math.random ()


3
แม้ว่าคนอื่น ๆ จะให้คำตอบที่ดี แต่ฉันไม่สามารถต้านทานตัวเองได้เมื่อเอกสาร redux เข้ามาในความคิดของฉันและ Math.random () กล่าวถึงเป็นพิเศษในพวกเขา :)
Shubhnik Singh

20

จากมุมมองทางคณิตศาสตร์ลายเซ็นของคุณไม่ใช่

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

แต่

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

ที่environmentสามารถให้ผลลัพธ์ของMath.random()มีความสามารถในการให้ผลของการและการสร้างค่าสุ่มทำให้สภาพแวดล้อมกลายเป็นผลข้างเคียงดังนั้นคุณจึงคืนสภาพแวดล้อมใหม่ซึ่งไม่เท่ากับค่าแรก!

กล่าวอีกนัยหนึ่งคือหากคุณต้องการอินพุตชนิดใดก็ตามที่ไม่ได้มาจากอาร์กิวเมนต์เริ่มต้น ( <number, number>ส่วน) คุณจะต้องจัดเตรียมสภาพแวดล้อมการดำเนินการ (ซึ่งในตัวอย่างนี้ระบุสถานะสำหรับMath) เช่นเดียวกันกับสิ่งอื่น ๆ ที่กล่าวถึงโดยคำตอบอื่น ๆ เช่น I / O หรือสิ่งนั้น


ในการเปรียบเทียบคุณสามารถสังเกตได้ว่านี่คือวิธีการเขียนโปรแกรมเชิงวัตถุ - ถ้าเราพูดเช่น

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

ที่จริงแล้วเรากำลังใช้

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

กับออบเจ็กต์ที่มีเมธอดที่เรียกว่าเป็นส่วนหนึ่งของสภาพแวดล้อม และทำไมSomeClassส่วนของผลลัพธ์? เพราะsomethingสถานะอาจมีการเปลี่ยนแปลงเช่นกัน!


7
ที่แย่กว่านั้นสภาพแวดล้อมก็กลายพันธุ์ด้วยดังนั้นจึงtest: <environment, number, number> -> <environment, number>ควรจะเป็น
Bergi

1
ฉันไม่แน่ใจว่าตัวอย่าง OO นั้นคล้ายกันมาก a.F(b, c)สามารถมองเห็นได้ว่าเป็นน้ำตาลในF(a, b, c)รูปแบบที่มีกฎพิเศษในการส่งออกไปยังคำจำกัดความที่มากเกินไปFตามประเภทของa(นี่คือวิธีที่ Python แสดงถึงมัน) แต่aยังคงมีความชัดเจนในทั้งสองสัญกรณ์ในขณะที่สภาพแวดล้อมในฟังก์ชันที่ไม่บริสุทธิ์จะไม่ถูกกล่าวถึงในซอร์สโค้ด
IMSoP

11

ฟังก์ชัน Pure จะคืนค่าเดียวกันสำหรับอินพุตเดียวกันเสมอ ฟังก์ชันบริสุทธิ์สามารถคาดเดาได้และมีความโปร่งใสในการอ้างอิงซึ่งหมายความว่าเราสามารถแทนที่การเรียกฟังก์ชันด้วยเอาต์พุตที่ส่งคืนและจะไม่เปลี่ยนแปลงการทำงานของโปรแกรม

https://github.com/MostlyAdequate/mostly-adequate-guide/blob/master/ch3.md


10

นอกเหนือจากคำตอบอื่น ๆ ที่ชี้ให้เห็นอย่างถูกต้องว่าฟังก์ชันนี้ไม่ใช่ปัจจัยที่กำหนดได้อย่างไรแล้วยังมีผลข้างเคียงอีกด้วยซึ่งจะทำให้การโทรในอนาคตmath.random()ส่งคืนคำตอบที่ต่างออกไป และตัวสร้างตัวเลขสุ่มที่ไม่มีคุณสมบัตินั้นโดยทั่วไปจะทำ I / O บางประเภทเช่นอ่านจากอุปกรณ์สุ่มที่ OS ให้มา verboten สำหรับฟังก์ชันบริสุทธิ์


7

ไม่มันไม่ใช่ คุณไม่สามารถหาผลลัพธ์ได้เลยดังนั้นจึงไม่สามารถทดสอบโค้ดส่วนนี้ได้ ในการทำให้โค้ดนั้นสามารถทดสอบได้คุณต้องแยกองค์ประกอบที่สร้างตัวเลขสุ่ม:

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

ตอนนี้คุณสามารถจำลองเครื่องกำเนิดไฟฟ้าและทดสอบรหัสของคุณได้อย่างถูกต้อง:

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

และในรหัส "การผลิต" ของคุณ:

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

1
▲สำหรับความคิดของคุณสำหรับการทดสอบ ด้วยความระมัดระวังเพียงเล็กน้อยคุณยังสามารถสร้างการทดสอบที่ทำซ้ำได้ในขณะที่ยอมรับ a util.Randomซึ่งคุณสามารถเริ่มต้นเมื่อเริ่มต้นการทดสอบเพื่อทำซ้ำพฤติกรรมเก่าหรือสำหรับการรันใหม่ (แต่ทำซ้ำได้) หากมีเธรดหลายเธรดคุณอาจสามารถทำได้ในเธรดหลักและใช้สิ่งนั้นRandomเพื่อวางเธรดโลคัลที่ทำซ้ำRandomได้ อย่างไรก็ตามตามที่ฉันเข้าใจมันtest(int,int,Random)ไม่ถือว่าบริสุทธิ์เนื่องจากมันเปลี่ยนแปลงสถานะของRandom.
PJTraill

2

คุณจะสบายดีไหมกับสิ่งต่อไปนี้:

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

เทียบเท่ากับ

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

?

คุณจะเห็นว่านิยามของ pure คือฟังก์ชันที่เอาต์พุตไม่เปลี่ยนแปลงด้วยสิ่งอื่นใดนอกจากอินพุต ถ้าเราบอกว่า JavaScript มีวิธีในการแท็กฟังก์ชันที่บริสุทธิ์และใช้ประโยชน์จากสิ่งนี้เครื่องมือเพิ่มประสิทธิภาพจะได้รับอนุญาตให้เขียนนิพจน์แรกใหม่เป็นนิพจน์ที่สอง

ฉันมีประสบการณ์จริงกับสิ่งนี้ เซิร์ฟเวอร์ SQL อนุญาตgetdate()และnewid()อยู่ในฟังก์ชัน "บริสุทธิ์" และเครื่องมือเพิ่มประสิทธิภาพจะกรองการโทรออกตามต้องการ บางครั้งสิ่งนี้อาจทำอะไรโง่ ๆ

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.