เหตุใดวันนี้ () เป็นตัวอย่างของฟังก์ชันที่ไม่บริสุทธิ์


38

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

บทความ Wikipedia กล่าวว่า "เวลาที่ต่างกันมันจะให้ผลลัพธ์ที่แตกต่างกัน" แต่นั่นก็เหมือนกับการบอกว่าต่างกันx sin(x)จะให้อัตราส่วนต่างกัน และsin(x)เป็นตัวอย่างของฟังก์ชันบริสุทธิ์


8
หากคุณผ่านช่วงเวลาของวันฟังก์ชันจะทำอะไร
JB King

1
ฉันคาดหวังให้คุณให้เวลากับวัน (ไม่ใช่ฟังก์ชันที่มีประโยชน์ที่สุด) แต่มันไม่มีข้อโต้แย้งใด ๆ ซึ่งฉันคิดว่าเป็นรากฐานของคำตอบ
แบรด

3
คุณสามารถทำนายผลลัพธ์ของมันได้หรือไม่ (ขึ้นอยู่กับพารามิเตอร์อินพุตที่คุณระบุ)
Daniel B

1
@DanielB ไม่มีอำนาจการคาดเดาพารามิเตอร์อินพุตขาด / null ที่ปรากฎ สิ่งเดียวที่ฉันทำได้คือดูที่นาฬิกาข้อมือของฉัน (jk โทรศัพท์มือถือ)
แบรด

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

คำตอบ:


103

เป็นเพราะไม่มีอาร์กิวเมนต์อินพุตที่เป็นทางการหรือไม่

เป็นเพราะผลผลิตขึ้นอยู่กับสิ่งที่ไม่ใช่อินพุตกล่าวคือเวลาปัจจุบัน

ทำไมเวลาที่แท้จริงของวันไม่ถือเป็น "อินพุตไปยังฟังก์ชัน"

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

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


2
ดังนั้นแม้ว่าเวลาของความเป็นจริงคือการเรียงลำดับของการป้อนข้อมูลเพราะมันไม่ได้รับเป็น input และมันอยู่นอกการควบคุมของฟังก์ชั่น (ทั้งภายในเพื่อฟังก์ชั่นและนอกการควบคุมของใครก็ตามที่จะเรียกToday()) Today()จะกลายเป็นที่ไม่บริสุทธิ์ Today()ฟังก์ชั่นอาจจะมีบิตของตัวอย่างที่โง่ เหมาะสมกว่าอาจเป็นCount()ฟังก์ชั่นบางอย่าง การกำหนดจำนวนไอเท็มที่เหมือนกันเพื่อนับCount()จะส่งคืนหมายเลขเดิมเสมอ แต่เนื่องจากว่าอยู่นอกขอบเขตของCount()มัน
แบรด

1
@brad ที่มีพื้นที่สีเทา - มีอาร์กิวเมนต์จริงโดยนัย - อาร์เรย์หรือรายการ รับรายการที่ไม่เปลี่ยนรูปและอาร์กิวเมนต์เดียวกันทุกครั้งที่มันจะส่งคืนค่าเดิมเสมอ
Max

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

4
@Brad count()สำหรับภาษาการเขียนโปรแกรมส่วนใหญ่นั้นบริสุทธิ์แน่นอน มันมีค่าอินพุตที่ชัดเจน: คอลเล็กชันที่คุณต้องการนับ อย่าสับสนกับไวยากรณ์เช่นmyCollection.count(); count(myCollection)ที่น้ำตาลเพียงสำหรับ
Andres F.

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

24

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


"จะคืนค่าที่ต่างออกไปเสมอ" เป็นบิต ... ถ้อยคำที่ไม่บริสุทธิ์ Wikipedia กล่าวว่า "คืนวันปัจจุบันของสัปดาห์" หมายความว่าค่าที่ได้รับในวันจันทร์จะไม่แตกต่างกัน
ริ้น

7
@gnat: จริงเว้นแต่มีบางสิ่งภายนอกโปรแกรมของคุณเปลี่ยนปฏิทินภายในของคอมพิวเตอร์ของคุณเพื่อให้มันคิดว่ามันเป็นวันพฤหัสบดี จากนั้นโทรToday()จะกลับ "วันพฤหัสบดี" ในวันจันทร์
FrustratedWithFormsDesigner

3
@gnat มันจะไม่ส่งกลับค่าที่แตกต่างกันเสมอ (มีฟังก์ชันที่มีประโยชน์ใด ๆ แต่เช่นเดียวกับฟังก์ชั่นที่ไม่บริสุทธิ์ส่วนใหญ่ค่าส่งคืนอาจแตกต่างกันแม้ในระหว่างการดำเนินการของโปรแกรมเดียว (เช่นถ้ามันทำงานข้ามคืน)

3
@delnan: ใช่นั่นคือความหายนะของผู้เขียนสคริปต์ฐานข้อมูลไร้เดียงสา! : P "แต่มันจะพลาด 300 ระเบียนได้อย่างไรสคริปต์ทำงานได้ดีเมื่อฉันทดสอบเมื่อเช้าวานนี้!"
FrustratedWithFormsDesigner

@delnan แน่นอน ฉันชี้ให้เห็นว่าการใช้เสมอในข้อความเริ่มต้น (แก้ไขในคำตอบฉบับปัจจุบันได้ ) ค่อนข้างไม่แน่ชัด
gnat

13

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

int Add(int a, int b) {return a + b;}ฟังก์ชั่นที่บริสุทธิ์จะเป็น ฟังก์ชั่นใช้งานได้เฉพาะกับสิ่งที่ได้รับและไม่ใช้ข้อมูลสถานะภายนอกอื่น ๆ ผลลัพธ์ตามธรรมชาติของสิ่งนี้คือคุณสามารถAdd(2,2)และรับ 4 จากนี้ไปจนสิ้นสุด นอกจากนี้เนื่องจากฟังก์ชั่นจะไม่เปลี่ยนแปลงสถานะภายนอกใด ๆ (มันไม่มี "ผลข้างเคียง"), เพิ่ม () ระหว่าง 2 และ 2 ต่อจากนี้ไปจนถึงเวลาสิ้นสุดจะไม่เปลี่ยนแปลงสิ่งอื่นใดในระบบเว้นแต่คุณจะ กำหนดผลลัพธ์ของฟังก์ชันให้กับตัวแปรหรือใช้ค่าเพื่ออัพเดตสถานะ (ซึ่งไม่ใช่การดำเนินการที่ดำเนินการโดยฟังก์ชันเอง) การดำเนินการทางคณิตศาสตร์แบบคลาสสิกแทบทั้งหมดเป็นฟังก์ชั่นที่บริสุทธิ์และสามารถใช้งานได้

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

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

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


8

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

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

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

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


@ JörgWMittagฉันไม่แน่ใจว่าที่ฉันยืนยันว่าฟังก์ชั่นที่ไม่มีข้อโต้แย้งไม่สามารถส่งกลับค่า
AakashM

ผายลมสมอง ผมอ่าน "มีเพียงหนึ่งชุดเป็นไปได้ของผลตอบแทนค่า"
Jörg W Mittag

8

() => 1จะเป็นฟังก์ชั่นที่บริสุทธิ์เนื่องจากมันจะคืนค่า 1 เสมอToday()อาจส่งคืน "วันจันทร์" หรือ "วันอังคาร" หรือเกือบจะเป็นค่าอื่น ๆ

อีกวิธีที่จะคิดว่ามันเป็นฟังก์ชั่นที่บริสุทธิ์ไม่ได้ขึ้นอยู่กับสถานะ โลกนี้ถือว่าเป็นสภาวะปกติ คุณจำเป็นต้องรู้สถานะของความเป็นจริงที่จะรู้ว่าวันนี้วันอะไร

อย่างไรก็ตามคุณไม่จำเป็นต้องรู้อะไรเป็นพิเศษเกี่ยวกับสถานะของโลกที่จะรู้ว่าอะไรsin(x)คืออะไร และเคยเรียกร้องให้sin(x)ได้รับxจะส่งกลับค่าเดียวกัน


Wikipedia กล่าวว่า "คืนวันที่ปัจจุบันของสัปดาห์" ซึ่งหมายความว่าสามารถส่งคืนวันจันทร์วันอังคารและอื่น ๆ แต่ไม่ใช่ "1/23/2013" หรือ "1/24/2013"
gnat

7
@gnat: อัปเดต แต่ความแตกต่างไม่ได้เป็นสาระสำคัญจริงๆ
Guvante

2

Date(timestamp)จะเป็นฟังก์ชั่นที่บริสุทธิ์ เพราะความเฉื่อยชาของมัน และเพราะจะไม่มีผลข้างเคียง

Today()สามารถแตกต่างกันไปขึ้นอยู่กับผลเมื่อคุณเรียกมัน นั่นคือสิ่งที่ทำให้มันไม่บริสุทธิ์ มันไม่ใช่ idempotent มันไม่มีผลข้างเคียงแม้ว่า แต่นั่นไม่ได้ทำให้บริสุทธิ์


2

นี่คือรหัสหลอกที่ฉันคิดเมื่อพูดถึงฟังก์ชั่นแท้ ๆ

newValue = Function();
while(true)
{
   oldValue = newValue;
   newValue = Function();
   assert( newValue == oldValue );
}

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

oldValue = Function( importantVariableToYourApp );
newValue = Function( importantVariableToYourApp );
assert( newValue == oldValue );

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


2

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

ดังนั้นtodayไม่ว่าจะเป็นฟังก์ชั่น แต่อย่างใดไม่มีฟังก์ชันที่แท้จริง หรือเราอาจตีความไวยากรณ์

today()

เล็กน้อยเพื่อให้มันหมายถึง

today   ()      -- today, applied to the value ()

ตัวอย่างเช่นใน Haskell สิ่งนี้จะใช้ได้:

data Day = Mon | Tue | Wed | Thu | Fri | Sat | Sun deriving Show
today :: () -> Day
today () = ....?
main = print (today())

เนื่องจากมี type () ที่มีค่าเดียว ()

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

ตัวจับเวลาระบบเป็นตัวอย่างที่ยอดเยี่ยมสำหรับสถานะโกลบอล


1

ปัญหาtoday()คือมันสามารถให้ผลลัพธ์ที่แตกต่างหากเรียกว่าสองครั้งหรือมากกว่าในฟังก์ชั่น

นี่คือตัวอย่างโค้ดที่สามารถแนะนำบั๊ก

function doSomething(when)
{
     if(today() == when)
     {
           // open a resource or create a temp file.....
     }

     // do some other work

     if(today() == when)
     {
           // close the resource or delete temp file.....
     }
}

เป็นไปได้ในตัวอย่างข้างต้น ว่าifคำสั่งที่สองจะไม่ดำเนินการ แม้ว่าคนแรกทำ ปล่อยให้ทรัพยากรอยู่ในสถานะไม่ดี


1

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

ทุกครั้งที่เราโทรToday()เราจะให้พารามิเตอร์เดียวกัน (ไม่มี) และยังไม่จำเป็นต้องได้รับผลลัพธ์เดียวกัน (จันทร์, อังคาร, ฯลฯ )


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

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