ฟังก์ชั่น pure ที่ถูกบันทึกไว้นั้นถือว่าตัวเองบริสุทธิ์หรือไม่?


47

สมมติว่าเป็นฟังก์ชั่นบริสุทธิ์ที่ทำบางสิ่งที่มีราคาแพงเช่นการกลับรายการปัจจัยสำคัญของfn(x)x

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

การพูดอย่างเป็นทางการmemoizedFn(x)ถือว่าบริสุทธิ์หรือไม่

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


24
บางทีมันอาจจะไม่บริสุทธิ์สำหรับคนพิถีพิถัน แต่ "บริสุทธิ์เพียงพอ" สำหรับคนที่ใช้งานได้ ;-)
Doc Brown

2
@DocBrown ฉันเห็นด้วยเพียงแค่สงสัยว่ามีคำที่เป็นทางการมากขึ้นสำหรับ 'pure พอ'
callum

13
การใช้งานฟังก์ชั่นแท้นั้นน่าจะแก้ไขแคชคำสั่งของโปรเซสเซอร์, ตัวทำนายสาขาเป็นต้น แต่นั่นอาจจะ "บริสุทธิ์เพียงพอ" สำหรับนักสอนคนเก่ง - หรือคุณอาจลืมฟังก์ชั่นล้วนๆ
gnasher729

10
@ callum ไม่ไม่มีคำจำกัดความที่เป็นทางการของ "pure พอ" เมื่อถกเถียงเกี่ยวกับความบริสุทธิ์และความหมายเชิงความหมายของการโทร "referential transparent" สองสายคุณจะต้องระบุว่าความหมายที่คุณจะนำไปใช้เสมอ ในระดับต่ำของรายละเอียดการใช้งานมันจะพังลงและมีผลต่อหน่วยความจำหรือการกำหนดเวลาที่แตกต่าง นั่นเป็นเหตุผลที่คุณจะต้องใช้งานได้อย่างจริงจัง: รายละเอียดระดับใดที่มีประโยชน์สำหรับการใช้เหตุผลเกี่ยวกับรหัสของคุณ
Bergi

3
ดังนั้นเพื่อประโยชน์ของลัทธิปฏิบัตินิยมฉันจะบอกว่าความบริสุทธิ์นั้นขึ้นอยู่กับว่าคุณพิจารณาเวลาในการคำนวณเพื่อเป็นส่วนหนึ่งของผลลัพธ์หรือไม่ funcx(){sleep(cached_time--); return 0;}ส่งคืน val เดียวกันทุกครั้ง แต่จะทำงานต่างกัน
ดาวอังคาร

คำตอบ:


41

ใช่. รุ่นที่บันทึกไว้ของฟังก์ชั่นแท้ยังเป็นฟังก์ชั่นที่บริสุทธิ์

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

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


19

Wikipedia กำหนด"Pure Function"เป็นฟังก์ชั่นที่มีคุณสมบัติดังต่อไปนี้:

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

  • การประเมินผลไม่มีผลข้างเคียง (ไม่มีการกลายพันธุ์ของตัวแปรสแตติกท้องถิ่นตัวแปรที่ไม่ใช่ในท้องถิ่นข้อโต้แย้งอ้างอิงที่ไม่แน่นอนหรือสตรีม I / O)

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

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


16
ฉันอาจพลาดบางสิ่ง แต่คุณจะเก็บแคชโดยไม่มีผลข้างเคียงได้อย่างไร
Val

1
โดยเก็บไว้ภายในฟังก์ชั่น
Robert Harvey

4
"ไม่มีการกลายพันธุ์ของตัวแปรคงที่ในท้องถิ่น" ดูเหมือนว่าจะยกเว้นตัวแปรท้องถิ่นที่คงอยู่ระหว่างการโทรเช่นกัน
Val

3
สิ่งนี้ไม่ได้ตอบคำถามจริงๆแม้ว่าคุณจะบอกว่าใช่ แต่ก็บริสุทธิ์
ดาวอังคาร

6
@val คุณถูกต้อง: เงื่อนไขนี้ต้องผ่อนคลายสักหน่อย บันทึกช่วยจำที่ใช้งานได้อย่างหมดจดที่เขาอ้างถึงไม่มีการกลายพันธุ์ที่มองเห็นได้ของข้อมูลคงที่ใด ๆ สิ่งที่เกิดขึ้นคือผลลัพธ์นั้นจะถูกคำนวณและบันทึกในครั้งแรกที่มีการเรียกใช้ฟังก์ชันและส่งกลับค่าเดิมทุกครั้งที่เรียกใช้ หลายภาษามีสำนวนว่า: static constตัวแปรท้องถิ่นใน C ++ (แต่ไม่ใช่ C) หรือโครงสร้างข้อมูลที่ประเมินอย่างขี้เกียจใน Haskell มีอีกหนึ่งเงื่อนไขที่คุณต้องการ: การเริ่มต้นจะต้องปลอดภัยต่อเธรด
Davislor

7

ใช่ฟังก์ชั่น pure ที่มีการบันทึกความจำนั้นมักเรียกกันว่า pure นี่เป็นเรื่องปกติโดยเฉพาะอย่างยิ่งในภาษาเช่น Haskell ซึ่งมีการบันทึกผลการเปลี่ยนแปลงที่ไม่จำเจและขี้เกียจซึ่งเป็นคุณลักษณะในตัว

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

ตัวอย่างหนึ่งของนักวิทยาศาสตร์คอมพิวเตอร์ที่ใช้คำว่า "ทำงานได้อย่างหมดจด" ด้วยวิธีนี้คือโพสต์บล็อกนี้โดย Conal Elliottเกี่ยวกับการบันทึกอัตโนมัติ:

บางทีอาจจะน่าแปลกใจที่การบันทึกช่วยจำสามารถนำไปใช้งานได้อย่างเรียบง่ายและมีประสิทธิภาพในภาษาที่ใช้งานได้ดี

มีตัวอย่างมากมายในวรรณกรรมที่ผ่านการตรวจสอบโดยเพื่อนและมีมานานหลายทศวรรษ ตัวอย่างเช่นกระดาษนี้จากปี 1995 “ การใช้การบันทึกอัตโนมัติเป็นเครื่องมือวิศวกรรมซอฟต์แวร์ในระบบโลกแห่งความจริง AI”ใช้ภาษาที่คล้ายกันมากในส่วนที่ 5.2 เพื่ออธิบายสิ่งที่เราเรียกในวันนี้ว่าฟังก์ชั่นบริสุทธิ์:

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

ภาษาที่จำเป็นบางอย่างมีสำนวนที่คล้ายกัน ตัวอย่างเช่นstatic constตัวแปรใน C ++ จะเริ่มต้นได้เพียงครั้งเดียวก่อนที่จะใช้ค่าของมันและจะไม่กลายพันธุ์


3

ขึ้นอยู่กับว่าคุณทำมันอย่างไร

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

อย่างไรก็ตามคุณสามารถบันทึกโดยไม่ต้องเปลี่ยนหน่วยความจำที่ไม่บริสุทธิ์ ตัวอย่างหนึ่งอยู่ในคำตอบนี้โดยที่ฉันติดตามค่าที่ถูกจดจำภายนอกโดยวิธีการlengthsโต้แย้ง

ในลิงค์ที่มีให้ Robert Harveyการประเมินแบบขี้เกียจใช้เพื่อหลีกเลี่ยงผลข้างเคียง

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

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

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


ฉันไม่คิดว่าวิธีการแก้ปัญหาใด ๆ ที่บริสุทธิ์กว่าแก้ปัญหาข้างต้น: ในขณะที่คุณสูญเสียความกังวลที่เกิดขึ้นพร้อมกันคุณจะสูญเสียโอกาสใด ๆ สำหรับการโทรสองครั้งที่เริ่มพร้อมกันcollatz(100)และcollatz(200)ร่วมมือกัน และ IIUIC ปัญหาของแคชที่เพิ่มขึ้นยังคงมีขนาดใหญ่เกินไป (แม้ว่า Haskell อาจมีลูกเล่นที่ดีสำหรับเรื่องนี้?)
maaartinus

หมายเหตุ: IOบริสุทธิ์ วิธีการที่ไม่บริสุทธิ์ทั้งหมดบนและแมวที่มีชื่อIO บริสุทธิ์เช่นกันดังนั้นเราจึงไม่จำเป็นต้องชำระ "บริสุทธิ์พอ" :)unsafeAsync.memoize
ซามูเอล

2

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

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


0

คุณสามารถดำเนินการได้โดยไม่ต้อง memoization ผลข้างเคียงโดยใช้monad รัฐ

[รัฐ monad] เป็นพื้นฟังก์ชั่น S => (S, A) ที่ S เป็นประเภทที่แสดงถึงสถานะของคุณและเป็นผลที่ได้ฟังก์ชั่นผลิต - แมวรัฐ

ในกรณีของคุณรัฐจะเป็นค่าที่บันทึกไว้หรือไม่มีอะไร (เช่น Haskell Maybeหรือ Scala Option[A]) หากมีการบันทึกค่าที่มีการบันทึกไว้จะมีการส่งคืนเป็นAมิฉะนั้นAจะถูกคำนวณและส่งคืนเนื่องจากสถานะที่ถูกเปลี่ยนแปลงและผลลัพธ์

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