คำนวณถ้าฟังก์ชั่นนั้นบริสุทธิ์


11

ตาม Wikipedia:

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

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

function sum(a,b) {
    return a+b;
}

function say(x){
    console.log(x);
}

isPure(sum) // True
isPure(say) // False

7
นี้อาจจะมีผู้สมัครสำหรับวิทยาการคอมพิวเตอร์ Stack แลกเปลี่ยน สิ่งนี้มีแนวโน้มมากขึ้นสำหรับขอบเขตของทฤษฎี ("เป็นไปได้") มากกว่าการใช้งานจริงและการออกแบบ ... มันค่อนข้างเกิน ken ของฉัน

... และแม้ว่ามันจะเป็น "เป็นไปได้" และ "ทฤษฎี" มีความเป็นไปได้ที่คำตอบจริง ๆ (เหมาะสำหรับคำถามและคำตอบ) ซึ่งอาจเกี่ยวข้องกับการพิสูจน์ ... ซึ่งนำกลับไปสู่โลกวิทยาศาสตร์คอมพิวเตอร์อีกครั้ง .

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

7
ไม่สามารถทำได้หากไม่มีแฮ็กการสะท้อนเฉพาะแพลตฟอร์ม หากฟังก์ชั่นเป็นกล่องดำการทดลองไม่สามารถพิสูจน์ได้ว่ามันบริสุทธิ์ เช่นหากมีสิ่งที่คล้ายif (rand(1000000)<2) return WRONG_ANSWERกันการตรวจสอบฟังก์ชันหลายครั้งสำหรับพฤติกรรมที่สอดคล้องกันจะไม่ช่วย แต่ถ้าคุณมีการเข้าถึงนิยามฟังก์ชันการพิสูจน์นั้นเป็นเรื่องเล็กน้อย
SK-logic

1
@ user470365 จากคำจำกัดความของฟังก์ชั่นแท้ - "การประเมินผลไม่ก่อให้เกิดผลข้างเคียงหรือเอาท์พุทที่สังเกตเห็นได้ทางความหมายเช่นการกลายพันธุ์ของวัตถุที่ไม่แน่นอนหรือเอาท์พุทอุปกรณ์ I / O" - สิ่งใดก็ตามที่เขียนถึง IO นั้นไม่บริสุทธิ์ตามคำจำกัดความ ในตัวอย่างการsayโทรconsole.logที่ไม่บริสุทธิ์จึงsayไม่บริสุทธิ์เช่นกัน

คำตอบ:


17

ใช่เป็นไปได้ขึ้นอยู่กับภาษา

ใน JavaScript คุณสามารถบอกได้ว่าฟังก์ชั่นนั้นบริสุทธิ์ตามเกณฑ์ต่อไปนี้หรือไม่:

  • มันอ่านพารามิเตอร์และคนในท้องถิ่นเท่านั้น

  • มันเขียนเฉพาะคนในท้องถิ่น

  • สำหรับผู้ที่ไม่ใช่คนท้องถิ่นจะเรียกใช้ฟังก์ชันที่บริสุทธิ์เท่านั้น

  • ฟังก์ชั่นทั้งหมดที่เรียกใช้โดยปริยายนั้นบริสุทธิ์เช่น, toString; และ

  • มันเขียนคุณสมบัติของคนในท้องถิ่นเท่านั้นหากพวกเขาไม่ได้ใช้นามแฝงที่ไม่ใช่คนในท้องถิ่น

นามแฝงไม่สามารถระบุได้ใน JavaScript ในกรณีทั่วไปเนื่องจากคุณสามารถค้นหาคุณสมบัติของวัตถุแบบไดนามิก ( object["property"]) หากคุณไม่เคยทำเช่นนั้นและคุณมีแหล่งที่มาของโปรแกรมทั้งหมดแล้วฉันคิดว่าปัญหานั้นง่ายมาก นอกจากนี้คุณยังต้องการข้อมูลเกี่ยวกับฟังก์ชั่นพื้นฐานที่มีผลข้างเคียงเช่นconsole.logหรืออะไรก็ตามที่เกี่ยวข้องกับ DOM

คำว่า "บริสุทธิ์" อาจใช้คำอธิบายบางอย่าง แม้ในภาษาการเขียนโปรแกรมที่ใช้งานได้อย่างแน่นหนาคงที่ล้วนๆซึ่งทุกฟังก์ชั่นนั้นมีความโปร่งใสในการอ้างอิง แต่ฟังก์ชั่นยังสามารถล้มเหลวได้ ดังนั้นเมื่อเราพูดถึงid :: a -> aสิ่งที่เราพูดจริง ๆ ไม่ใช่:

ได้รับค่าของชนิดบางaฟังก์ชันการผลิตค่าของชนิดida

แต่:

ได้รับค่าของชนิดบางaฟังก์ชั่นidไม่ได้ผลิตค่าซึ่งเป็นไม่ได้aจากประเภท

เพราะการดำเนินงานที่ถูกต้องคือid error "Not implemented!"ดังที่ Peteris ชี้ให้เห็นการไม่สนใจเรื่องนี้เป็นความไม่บริสุทธิ์ Kokaเป็นภาษาโปรแกรมที่ใช้งานได้โดยมีรูปแบบทางไวยากรณ์บน JavaScript ซึ่งสามารถอนุมานถึงผลกระทบที่เป็นไปได้เช่นความแตกต่าง (การไม่สิ้นสุด) การอ้างอิงโปร่งใสการโยนข้อยกเว้นและการกระทำ I / O


+1 - ด้วยคำตอบของ Peteris มี upvotes มากมาย .....
mattnz

ฉันคิดว่าคุณจะต้องเพิ่ม "มันเรียกฟังก์ชั่นบริสุทธิ์เท่านั้น" ในรายการของเกณฑ์
Greg Hewgill

1
@ GregHewgill: จับได้ดี ฉันปรับปรุงคำตอบตามนั้น มันเป็นการดีที่จะเรียกฟังก์ชั่นการกลายพันธุ์ในคนท้องถิ่นตราบใดที่พวกเขาไม่ได้มีผลข้างเคียง “ บริสุทธิ์” เกินคำเกินไป…
Jon Purdy

คุณต้องตรวจสอบว่าtoStringบริสุทธิ์สำหรับวัตถุใด ๆ ที่คุณใช้เป็นสตริง
Oleg V. Volkov

ฉันไม่คิดว่าคำตอบนี้ถูกต้องเพราะมีเพียงบางส่วนของสถานการณ์ที่คุณสามารถทำการตัดสินใจเหล่านี้ได้ ลองพิจารณา: ฟังก์ชั่นนี้บริสุทธิ์หรือไม่ function a (o) { return o.method(); }- เราไม่สามารถตอบคำถามนี้ได้จริงเพราะมันขึ้นอยู่กับพารามิเตอร์ที่oถูกส่งผ่าน นอกจากนี้เราไม่สามารถอธิบายได้ว่าจะเกิดอะไรขึ้นหากฟังก์ชันที่ได้รับการรับรองก่อนหน้านี้เปลี่ยนเป็นการใช้งานที่ไม่บริสุทธิ์ซึ่งเป็นปัญหาที่อาจเกิดขึ้นกับจาวาสคริปต์
จูลส์

11

ไม่คุณสามารถตรวจสอบได้ง่าย ๆ ว่าฟังก์ชั่นนั้นเป็นการทำงานที่ "ปลอดภัยอย่างแท้จริง" เท่านั้นดังที่อธิบายไว้ในคำตอบของ Jon Purdy แต่นั่นไม่เพียงพอที่จะตอบคำถามของ IMO

พิจารณาฟังก์ชั่นนี้:

function possiblyPure(x) {
    if (someCheck(x)) {
        return x+1; // pure code path
    }
    else {
        console.log("I'm so unpure..."); // unpure code path
    }
}

เห็นได้ชัดว่าถ้าsomeCheckไม่บริสุทธิ์ก็เป็นpossiblyPureเช่นนั้น แต่ถ้าsomeCheckเป็นที่บริสุทธิ์และผลตอบแทนtrueสำหรับทุกๆค่าที่เป็นไปได้x, possiblyPureบริสุทธิ์เนื่องจากเส้นทางรหัส unpure ไม่สามารถเข้าถึง!

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

แก้ไข: พิสูจน์ว่ามันเป็นไปไม่ได้

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

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

กรณี B) ถ้าฟังก์ชั่น pure ได้รับอนุญาตให้ไม่ยุติในอินพุตบางตัวเราสามารถสร้างบางสิ่งเช่นนั้น: ลองสมมติว่าisPure(f)คำนวณว่าfเป็นสตริงที่กำหนดฟังก์ชันบริสุทธิ์

function halts(f) {
   var fescaped = f.replace(/\"/g, '\\"');
   var upf = 'function() { '+f+'("'+fescaped+'\); console.log("unpure"); }';
   return isPure(upf);
}

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

หากisPureทำงานตามที่คาดไว้ (ส่งคืนผลลัพธ์ที่ถูกต้องและยุติในทุกอินพุต) เราจะแก้ไขปัญหาการหยุดพัก (*)! เนื่องจากสิ่งนี้เป็นที่รู้กันว่าเป็นไปไม่ได้isPureจึงไม่มีอยู่จริง

(*) สำหรับฟังก์ชัน JavaScript บริสุทธิ์ซึ่งเพียงพอที่จะแก้ไขสำหรับเครื่องทัวริงด้วย


3
จริง เป็นไปได้เสมอที่จะทำการวิเคราะห์แบบอนุรักษ์นิยม - เพื่อตรวจสอบว่าฟังก์ชั่นนั้นบริสุทธิ์แน่นอนหรือไม่ แต่ไม่สามารถตรวจสอบได้ว่ามันไม่ได้บริสุทธิ์แน่นอน
SK-logic

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

1

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

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

ดังนั้นเป็นไปไม่ได้ในทางทฤษฎีและเป็นไปได้ในทางปฏิบัติหากคุณปรับแต่งคำถามเล็กน้อยและฉันคิดว่ามันยากแค่ไหนที่จะขึ้นอยู่กับภาษาเป็นอย่างมาก ภาษาที่เรียบง่าย / สะอาดขึ้นโดยมุ่งเน้นที่ความไม่สามารถเปลี่ยนแปลงได้และการสะท้อนที่ดีจะง่ายขึ้น


ฉันคิดว่ามันถูกลดระดับลงเพราะมันผิด bottomเป็นค่าชั้นหนึ่งที่ถูกต้อง แต่ไม่สมควรที่จะถูกเลือกปฏิบัติด้วยวิธีนี้
SK-logic

0

เป็นคำถามที่ดีมาก

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

แต่คุณไม่สามารถทำได้โดยทั่วไป โปรแกรมที่ไม่หยุดชะงักนั้นไม่บริสุทธิ์และเราไม่สามารถตัดสินปัญหาการหยุดทำงานได้


1
-1: การเขียนฟังก์ชั่นที่ผ่านการทดสอบนี้จะไม่สำคัญเกินกว่าจะเป็นเรื่องไร้สาระ
mattnz

3
ด้วยตรรกะนี้voidฟังก์ชั่นใด ๆจะเป็น "บริสุทธิ์" ซึ่งเป็นเท็จอย่างชัดเจน
Greg Hewgill

1
@Greg: ตามนามสกุล void foo (void) ก็ต้องบริสุทธิ์เช่นกัน
mattnz

0

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


5
การทำงานตลอดไปดูเหมือนจะไม่ลดฟังก์ชันจากการปรับเกณฑ์ให้เหมาะกับฟังก์ชันบริสุทธิ์
whatsisname

+1: มีความจำเป็นโดยนัยที่ functiuon ยกเลิกโดย "ฟังก์ชั่นประเมินค่าผลลัพธ์เดียวกันเสมอให้ .... "
mattnz

2
ทำงานอย่างต่อเนื่องอย่างต่อเนื่องโดยไม่ต้องแก้ไขสถานะใด ๆ ว่าเป็น "บริสุทธิ์" อย่างสมบูรณ์แบบ แต่แน่นอนว่ามันเป็นปัญหาศัพท์ที่นี่
SK-logic

@mattnz ฟังก์ชั่นดังกล่าวมักจะประเมินbottomค่า
SK-logic

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