เมื่อฉันเข้าใจแบบจำลองการแทนที่ (ด้วย referential transparent (RT)) คร่าวๆคุณสามารถยกเลิกการจัดทำฟังก์ชันเป็นส่วนที่ง่ายที่สุดได้ หากการแสดงออกเป็น RT คุณสามารถยกเลิกการแสดงออกและได้รับผลลัพธ์เดียวกัน
ใช่ปรีชาค่อนข้างถูกต้อง ต่อไปนี้เป็นตัวชี้เพื่อให้แม่นยำยิ่งขึ้น:
เช่นเดียวกับที่คุณพูดนิพจน์ RT ควรมีsingle
"ผลลัพธ์" นั่นคือเมื่อได้รับfactorial(5)
นิพจน์ในโปรแกรมก็ควรให้ผลลัพธ์ "ผลลัพธ์" ที่เหมือนกันเสมอ ดังนั้นถ้าบางอย่างfactorial(5)
อยู่ในโปรแกรมและผลตอบแทนถัวเฉลี่ย 120 ก็ควรถัวเฉลี่ย 120 โดยไม่คำนึงถึง "สั่งขั้นตอน" มันมีการขยาย / คำนวณ - ไม่คำนึงถึงเวลา
ตัวอย่าง: factorial
ฟังก์ชั่น
def factorial(n):
if n == 1:
return 1
return n * factorial(n - 1)
มีข้อควรพิจารณาบางประการเกี่ยวกับคำอธิบายนี้
ก่อนอื่นโปรดจำไว้ว่ารูปแบบการประเมินที่แตกต่างกัน (ดูที่การสมัครกับลำดับปกติ) อาจให้ "ผลลัพธ์" ที่แตกต่างกันสำหรับนิพจน์ RT เดียวกัน
def first(y, z):
return y
def second(x):
return second(x)
first(2, second(3)) # result depends on eval. model
ในโค้ดข้างต้นfirst
และsecond
มีความโปร่งใสในการอ้างอิงและยังนิพจน์ที่ส่วนท้ายให้ผลลัพธ์ "ผลลัพธ์" ที่แตกต่างกันหากประเมินภายใต้คำสั่งปกติและคำสั่งการสมัคร (ภายใต้หลังนิพจน์จะไม่หยุด)
.... ซึ่งนำไปสู่การใช้ "ผลลัพธ์" ในเครื่องหมายคำพูด เนื่องจากมันไม่จำเป็นต้องมีการแสดงออกที่จะหยุดมันอาจไม่ก่อให้เกิดมูลค่า ดังนั้นการใช้ "ผลลัพธ์" จึงเป็นพร่ามัว เราสามารถพูดได้ว่านิพจน์ RT ให้ผลเหมือนกันเสมอcomputations
ภายใต้โมเดลการประเมิน
ประการที่สามมันอาจจะต้องเห็นสองfoo(50)
ปรากฏในโปรแกรมในสถานที่ที่แตกต่างกันเป็นนิพจน์ที่แตกต่างกัน - แต่ละคนให้ผลลัพธ์ของตัวเองที่อาจแตกต่างกัน ตัวอย่างเช่นหากภาษาอนุญาตให้ใช้ขอบเขตแบบไดนามิกทั้งสองนิพจน์แม้จะเหมือนกันทุกประการจะแตกต่างกัน ใน Perl:
sub foo {
my $x = shift;
return $x + $y; # y is dynamic scope var
}
sub a {
local $y = 10;
return &foo(50); # expanded to 60
}
sub b {
local $y = 20;
return &foo(50); # expanded to 70
}
แบบไดนามิก misleads ขอบเขตเพราะมันทำให้มันง่ายที่ใครจะคิดว่าx
เป็นเพียง แต่สำหรับการป้อนข้อมูลfoo
เมื่อในความเป็นจริงมันเป็นและx
y
วิธีหนึ่งที่จะเห็นความแตกต่างคือการแปลงโปรแกรมให้เทียบเท่ากันโดยไม่มีขอบเขตแบบไดนามิกนั่นคือผ่านพารามิเตอร์อย่างชัดเจนดังนั้นแทนที่จะกำหนดfoo(x)
เรากำหนดfoo(x, y)
และส่งผ่านy
อย่างชัดเจนในผู้โทร
ประเด็นคือเราอยู่ภายใต้function
ความคิดเสมอ: เมื่อได้รับข้อมูลบางอย่างสำหรับนิพจน์เราจะได้รับ "ผลลัพธ์" ที่สอดคล้องกัน หากเราให้อินพุตเดียวกันเราควรคาดหวัง "ผลลัพธ์" เหมือนเดิมเสมอ
ทีนี้รหัสต่อไปนี้คืออะไร?
def foo():
global y
y = y + 1
return y
y = 10
foo() # yields 11
foo() # yields 12
foo
ขั้นตอนการแบ่ง RT เพราะมี redefinitions นั่นก็คือเรากำหนดไว้y
ในจุดหนึ่งและหลังในนิยามใหม่ที่เดียวกัน y
ในตัวอย่าง perl ด้านบนy
s มีการผูกที่แตกต่างกันแม้ว่าจะใช้ชื่อตัวอักษรเดียวกัน "y" ที่นี่y
ของจริงเหมือนกัน นั่นเป็นเหตุผลที่เราพูดว่า (อีกครั้ง) การกำหนดเป็นการดำเนินการเมตา : คุณกำลังเปลี่ยนคำจำกัดความของโปรแกรมของคุณ
input -> output
ประมาณคนมักจะแสดงให้เห็นถึงความแตกต่างดังนี้ในการตั้งค่าฟรีผลข้างเคียงคุณมีการทำแผนที่จาก ในการตั้งค่า "จำเป็น" คุณมีinput -> ouput
ในบริบทของstate
ที่สามารถเปลี่ยนแปลงได้ตลอดเวลา
ตอนนี้แทนที่จะเป็นเพียงการแทนที่นิพจน์สำหรับค่าที่สอดคล้องกันของพวกเขาหนึ่งยังต้องใช้การแปลงไปstate
ที่แต่ละการดำเนินการที่ต้องใช้มัน (และแน่นอนว่าการแสดงออกอาจพิจารณาที่เดียวกันstate
เพื่อดำเนินการคำนวณ)
ดังนั้นหากในโปรแกรมฟรีที่มีผลข้างเคียงทั้งหมดที่เราจำเป็นต้องรู้ในการคำนวณนิพจน์คืออินพุตของแต่ละบุคคลในโปรแกรมที่จำเป็นเราจำเป็นต้องรู้อินพุตและสถานะทั้งหมดสำหรับแต่ละขั้นตอนการคำนวณ การใช้เหตุผลเป็นคนแรกที่ประสบกับการระเบิดครั้งใหญ่ (ตอนนี้เพื่อแก้ปัญหาขั้นตอนที่เป็นปัญหาคุณต้องมีอินพุตและดัมพ์หลัก) เทคนิคบางอย่างมีการแสดงผลไม่ได้เช่นบันทึกข้อมูล แต่การเห็นพ้องด้วยและขนานก็ยิ่งท้าทายมากขึ้น
RT
ปิดการใช้งานคุณจากการใช้substitution model.
ปัญหาใหญ่ที่ไม่สามารถใช้งานได้substitution model
คือพลังของการใช้มันเพื่อเหตุผลเกี่ยวกับโปรแกรมหรือไม่