TL; DR ในขณะที่คนอื่น ๆ ชี้: สัญกรณ์แลมบ์ดาเป็นเพียงวิธีการกำหนดฟังก์ชั่นโดยไม่ต้องถูกบังคับให้ตั้งชื่อพวกเขา
รุ่นยาว
ฉันต้องการอธิบายรายละเอียดเล็กน้อยในหัวข้อนี้เพราะฉันคิดว่ามันน่าสนใจมาก คำเตือน: ฉันได้เรียนหลักสูตรแลมบ์ดาแคลคูลัสมานานแล้ว หากผู้ที่มีความรู้ดีกว่าพบความไม่ถูกต้องในคำตอบของฉันโปรดช่วยฉันปรับปรุงมัน
ขอเริ่มต้นด้วยการแสดงออกเช่นและ1 + 2
x + 2
ตัวอักษรเช่น1
และ2
เรียกว่าค่าคงที่เพราะมันถูกผูกไว้กับค่าคงที่ที่เฉพาะเจาะจง
ตัวระบุเช่นx
เรียกว่าตัวแปรและเพื่อประเมินคุณต้องผูกมันกับค่าบางอย่างก่อน ดังนั้นโดยทั่วไปคุณไม่สามารถประเมินได้x + 1
ตราบใดที่คุณไม่รู้ว่าx
เป็นอะไร
สัญกรณ์แลมบ์ดาให้สคีมาสำหรับการผูกค่าอินพุตเฉพาะกับตัวแปร แสดงออกแลมบ์ดาสามารถเกิดขึ้นโดยการเพิ่มในหน้าของการแสดงออกที่มีอยู่เช่นλx .
λx . x + 1
ตัวแปรx
กล่าวจะฟรีในx + 1
และผูกพันในλx . x + 1
สิ่งนี้ช่วยในการประเมินนิพจน์ได้อย่างไร หากคุณป้อนค่าให้กับนิพจน์แลมบ์ดาเช่นนั้น
(λx . x + 1) 2
จากนั้นคุณสามารถประเมินนิพจน์ทั้งหมดโดยการแทนที่ (การผูก) การเกิดขึ้นทั้งหมดของตัวแปรx
ด้วยค่า 2:
(λx . x + 1) 2
2 + 1
3
ดังนั้นเครื่องหมายแลมบ์ดาจึงเป็นกลไกทั่วไปสำหรับการเชื่อมโยงสิ่งต่าง ๆ กับตัวแปรที่ปรากฏในนิพจน์ / โปรแกรมบล็อก สิ่งนี้จะสร้างแนวคิดที่แตกต่างในภาษาการเขียนโปรแกรมขึ้นอยู่กับบริบท:
- ในภาษาที่ใช้งานได้อย่างหมดจดเช่น Haskell นิพจน์แลมบ์ดาเป็นตัวแทนของฟังก์ชันในแง่คณิตศาสตร์: ค่าอินพุตจะถูกส่งเข้าไปในร่างกายของแลมบ์ดาและสร้างค่าเอาต์พุต
- ในหลายภาษา (เช่น JavaScript, Python, Scheme) การประเมินเนื้อความของแลมบ์ดานิพจน์สามารถมีผลข้างเคียง ในกรณีนี้เราสามารถใช้คำว่าโพรซีเดอร์เพื่อทำเครื่องหมายความแตกต่างของฟังก์ชั่น wrt pure
นอกเหนือจากความแตกต่างสัญกรณ์แลมบ์ดานั้นเกี่ยวกับการกำหนดพารามิเตอร์ที่เป็นทางการและผูกเข้ากับพารามิเตอร์ที่แท้จริง
ขั้นตอนต่อไปคือการตั้งชื่อฟังก์ชั่น / ขั้นตอน ในหลายภาษาฟังก์ชันมีค่าเหมือนกันดังนั้นคุณจึงสามารถตั้งชื่อฟังก์ชั่นได้ดังนี้
(define f (lambda (x) (+ x 1))) ;; Scheme
f = \x -> x + 1 -- Haskell
val f: (Int => Int) = x => x + 1 // Scala
var f = function(x) { return x + 1 } // JavaScript
f = lambda x: x + 1 # Python
ดังที่ Eli Barzilay ชี้ให้เห็นคำจำกัดความเหล่านี้เพียงผูกชื่อf
กับค่าซึ่งเกิดขึ้นเป็นฟังก์ชัน ดังนั้นในแง่นี้ฟังก์ชั่นตัวเลขสตริงอักขระเป็นค่าทั้งหมดที่สามารถผูกกับชื่อในวิธีเดียวกัน:
(define n 42) ;; Scheme
n = 42 -- Haskell
val n: Int = 42 // Scala
var n = 42 // JavaScript
n = 42 # Python
ในภาษาเหล่านี้คุณสามารถผูกฟังก์ชันกับชื่อโดยใช้สัญลักษณ์ที่คุ้นเคย (แต่เทียบเท่า):
(define (f x) (+ x 1)) ;; Scheme
f x = x + 1 -- Haskell
def f(x: Int): Int = x + 1 // Scala
function f(x) { return x + 1 } // JavaScript
def f(x): return x + 1 # Python
บางภาษาเช่น C สนับสนุนเฉพาะเครื่องหมายหลังเพื่อกำหนด (ชื่อ) ฟังก์ชั่น
ปิด
บางข้อสังเกตขั้นสุดท้ายเกี่ยวกับการปิด x + y
พิจารณาการแสดงออก สิ่งนี้มีตัวแปรอิสระสองตัว หากคุณผูกx
โดยใช้เครื่องหมายแลมบ์ดาคุณจะได้รับ:
\x -> x + y
นี้ไม่ได้ (ยัง) y
ฟังก์ชั่นเนื่องจากยังคงมีตัวแปรฟรี คุณสามารถสร้างฟังก์ชั่นจากฟังก์ชั่นนี้โดยการผูกy
เช่นกัน:
\x -> \y -> x + y
หรือ
\x y -> x + y
ซึ่งเป็นเช่นเดียวกับ+
ฟังก์ชั่น
แต่คุณสามารถผูกพูดy
ด้วยวิธีอื่น (*):
incrementBy y = \x -> x + y
ผลลัพธ์ของการใช้ฟังก์ชัน incrementBy กับตัวเลขคือการปิดเช่นฟังก์ชัน / โพรซีเดอร์ที่เนื้อความมีตัวแปรอิสระ (เช่นy
) ที่ถูกผูกไว้กับค่าจากสภาพแวดล้อมที่กำหนดการปิด
ดังนั้นincrementBy 5
เป็นฟังก์ชั่น (ปิด) ที่เพิ่มขึ้นจำนวน 5
บันทึก (*)
ฉันกำลังโกงที่นี่:
incrementBy y = \x -> x + y
เทียบเท่ากับ
incrementBy = \y -> \x -> x + y
ดังนั้นกลไกการผูกจึงเหมือนกัน ฉันคิดว่าการปิดเป็นตัวแทนของการแสดงออกแลมบ์ดาที่ซับซ้อนยิ่งขึ้น เมื่อการแสดงนี้ถูกสร้างขึ้นการเชื่อมโยงบางส่วนของนิพจน์แม่ได้ถูกตั้งค่าไว้แล้วและการปิดใช้ในภายหลังเมื่อได้รับการประเมิน / เรียกใช้