Y combinator และการปรับหางเรียกให้เหมาะสม


20

คำจำกัดความของ Y combinator ใน F # คือ

let rec y f x = f (y f) x

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

let y f x = f (y f) x = f (f (y f)) x = f (f (f (y f))) x etc...

ปัญหาคือโครงร่างนี้ precludes โดยใช้การปรับ tail call ใด ๆ : แน่นอนว่าอาจมีการดำเนินการบางอย่างที่ค้างอยู่ใน f's ซึ่งในกรณีนี้เราไม่สามารถกลายพันธุ์ local stack frame ที่เกี่ยวข้องกับ f

ดังนั้น:

  • ในด้านหนึ่งการใช้ Y combinator ต้องมีความต่อเนื่องที่แตกต่างกันอย่างชัดเจนกว่าฟังก์ชั่นของตัวเอง
  • บน othe เพื่อใช้ TCO เราต้องการให้ไม่มีการดำเนินการที่ค้างอยู่ใน f และเรียก f เท่านั้น

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


คุณได้เพิ่ม keywork rec ในการนำไปปฏิบัติของคุณหรือไม่ ฉันควรคิดว่ามันจำเป็นต้องใช้จากการอ่านของฉัน ..
จิมมี่ฮอฟฟา

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

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

5
ฉันทำบัญชีและได้รับ 50 คะแนนเพื่อแสดงความคิดเห็นที่นี่ คำถามนี้น่าสนใจจริงๆ fฉันคิดว่ามันขึ้นอยู่กับ เราสามารถเห็นได้ว่าyสามารถตัดfกับอันธพาล(y f)แต่อย่างที่คุณบอกว่าfอาจมีการดำเนินการที่ค้างอยู่ ฉันคิดว่ามันน่าสนใจที่จะทราบว่ามี combinator แยกต่างหากที่เป็นมิตรกับหางมากขึ้น ฉันสงสัยว่าคำถามนี้จะได้รับความสนใจมากขึ้นในไซต์ CS Stackexchange หรือไม่
John Cartwright

คำตอบ:


4

คุณรู้วิธีใดบ้างที่จะสามารถปรับความสมดุลทั้งสองได้

ไม่และด้วยเหตุผลที่ดี IMHO

Y-combinator เป็นสิ่งก่อสร้างเชิงทฤษฎีและจำเป็นต้องทำแลมบ์ดาแคลคูลัสเท่านั้น (โปรดจำไว้ว่าไม่มีลูปในแคลคูลัสแลมบ์ดาหรือแลมบ์ดามีชื่อที่เราสามารถใช้เรียกซ้ำได้)

ด้วยเหตุนี้ combinator Y จึงน่าหลงใหลอย่างแท้จริง

แต่ : ไม่มีใครใช้ Y-combinator สำหรับการสอบถามซ้ำ! (ยกเว้นอาจจะเพื่อความสนุกเพื่อแสดงว่ามันใช้งานได้จริง)

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

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


2
Y-combinator เหมือนปมปมที่ได้รับการผูกหลังจากการใช้งานแต่ละครั้ง ระบบส่วนใหญ่ตัดสั้นและผูกปมที่ระดับเมตาดาต้าซึ่งไม่จำเป็นต้องทำการทดสอบซ้ำ
Dan D.

1
สำหรับย่อหน้าสุดท้ายให้พิจารณา Haskell ซึ่งหัวใจใช้การลดกราฟเพื่อทำการประเมินผลที่ขี้เกียจ แต่สิ่งที่ฉันชอบคือการลดที่ดีที่สุดซึ่งมักใช้เส้นทางในโครงร่างของ Church-Rosser ที่มีการลดลงน้อยที่สุดในรูปแบบปกติเต็มรูปแบบ เช่นที่ปรากฏในAsperti และ Guerrini ของอัตราส่วนการดำเนินงานของฟังก์ชั่นการเขียนโปรแกรมภาษา ยังเห็นBOHM 1.1
Dan D.

@DanD ขอบคุณสำหรับลิงค์ฉันจะลองในภายหลังในเบราว์เซอร์ที่รู้คำลงท้าย แน่นอนว่ามีบางสิ่งที่จะเรียนรู้สำหรับฉัน แต่คุณแน่ใจหรือไม่ว่าการรวบรวมฮาเซลจะทำให้กราฟลดลง? ฉันสงสัยในสิ่งนี้
Ingo

1
ที่จริงแล้วมันใช้การลดกราฟ: "GHC คอมไพล์กับ G-machine แบบไม่มีป้ายกำกับ (STG) แบบไม่มีสเกลนี่คือเครื่องลดกราฟที่มีการประมาณ (เช่นเครื่องเสมือนที่ดำเนินการลดกราฟตามที่อธิบายไว้ข้างต้น)" จาก ...สำหรับข้อมูลเพิ่มเติมเกี่ยวเครื่อง STG ให้ดูที่ไซมอนเพย์ตันโจนส์การใช้ภาษาการทำงานขี้เกียจบนฮาร์ดแวร์หุ้นที่: ใจเสาะ Tagless G-เครื่อง
Dan D.

@DanD ในบทความเดียวกับที่คุณเชื่อมโยงมันจะอ่านเพิ่มเติมว่า GHC "ทำการเพิ่มประสิทธิภาพหลายอย่างในการเป็นตัวแทนนั้นก่อนที่จะรวบรวมในรหัสเครื่องจริง (อาจใช้ C ผ่าน GCC)
Ingo

0

ฉันไม่แน่ใจเกี่ยวกับคำตอบนี้อย่างสมบูรณ์ แต่มันเป็นสิ่งที่ดีที่สุดที่ฉันจะได้รับ

y combinator ขี้เกียจโดยเนื้อแท้ในภาษาที่เข้มงวดต้องเพิ่มความขี้เกียจด้วยตนเองโดยใช้ lambdas เสริม

let rec y f x = f (y f) x

ลักษณะความหมายของคุณเหมือนว่ามันจะต้องมีความเกียจคร้านในการสั่งซื้อที่จะยุติหรือ(y f)ข้อโต้แย้งที่ไม่เคยจะเสร็จสิ้นการประเมินและจะต้องมีการประเมินหรือไม่ว่าfใช้มัน TOC ในบริบทที่ขี้เกียจมีความซับซ้อนมากขึ้นและยิ่งไปกว่านั้นผลลัพธ์ของ(y f)การจัดองค์ประกอบฟังก์ชั่นซ้ำโดยไม่ต้องใช้แอพพลิเคชั่xน ฉันไม่แน่ใจว่าสิ่งนี้จำเป็นต้องใช้หน่วยความจำ O (n) โดยที่ n คือความลึกของการเรียกซ้ำ แต่ฉันสงสัยว่าคุณสามารถบรรลุ TOC ประเภทเดียวกับที่เป็นไปได้กับบางสิ่งเช่น (เปลี่ยนเป็น Haskell เพราะไม่รู้จริงๆ F #)

length acc []    = acc
length acc (a:b) = length (acc+1) b 

หากคุณไม่ทราบมาก่อนความแตกต่างระหว่างfoldlและfoldl'ในแฮสเค็ลล์อาจทำให้สถานการณ์มีความชัดเจน foldlเขียนตามที่ควรจะเป็นภาษากระตือรือร้น แต่แทนที่จะเป็น TOC ที่จริงแล้วมันแย่กว่าfoldrเพราะตัวเก็บเสียงนั้นมีขนาดใหญ่ที่ไม่สามารถประเมินได้บางส่วน (สิ่งนี้เกี่ยวข้องกับสาเหตุที่ทั้ง foldl และ foldl 'ไม่ทำงานในรายการอนันต์) ดังนั้นใน Haskell รุ่นล่าสุดที่foldl'เพิ่มเข้ามาซึ่งบังคับให้มีการประเมินการสะสมในแต่ละครั้งที่ฟังก์ชั่นเกิดขึ้นเพื่อให้แน่ใจว่าจะไม่มีการสร้างอันมหาศาล ฉันแน่ใจว่าhttp://www.haskell.org/haskellwiki/Foldr_Foldl_Foldl%27สามารถอธิบายสิ่งนี้ได้ดีกว่า I.

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