การวนซ้ำสามารถแทนที่การเรียกซ้ำได้หรือไม่


41

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

มีแม้แต่กระทู้ upvoted สูงพร้อมคำตอบ upvoted อย่างมากที่กล่าวว่าใช่เป็นไป

ตอนนี้ฉันไม่ได้พูดว่าพวกเขาผิด เป็นเพียงคำตอบที่ตอบโต้ความรู้น้อยของฉันและความเข้าใจเกี่ยวกับการคำนวณ

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

ฉันยังสงสัยว่าการปฏิบัติการหลายมิติสามารถแสดงออกซ้ำ ๆ ได้

ในคำตอบของเขา(ซึ่งฉันไม่เข้าใจโดยวิธี) กับคำถามของฉัน@YuvalFIlmus กล่าวว่ามันเป็นไปไม่ได้ที่จะแปลงลำดับของการดำเนินการทางคณิตศาสตร์ใด ๆ ให้เป็นลำดับของการเพิ่ม

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

คำถามของฉันคือ:

การเรียกซ้ำทุกครั้งสามารถเปลี่ยนเป็นการทำซ้ำได้หรือไม่และทำไม

โปรดให้คำตอบกับนักเรียนมัธยมปลายที่สดใสหรือปริญญาตรีปีแรกจะเข้าใจ ขอขอบคุณ.

ป.ล. ฉันไม่ทราบว่าการเรียกซ้ำแบบดั้งเดิมคืออะไร (ฉันรู้เกี่ยวกับฟังก์ชัน Ackermann และนั่นไม่ใช่การเรียกซ้ำแบบดั้งเดิม แต่ยังคงคำนวณได้ ALL ความรู้ของฉันเกี่ยวกับมันมาจากหน้า Wikipedia ในฟังก์ชัน Ackermann)

PPS: ถ้าคำตอบคือใช่ยกตัวอย่างเช่นคุณสามารถเขียนฟังก์ชั่นที่ไม่ใช่แบบดั้งเดิม - แบบเรียกซ้ำ เช่น Ackermann ในคำตอบ มันจะช่วยให้ฉันเข้าใจ


21
ทุกสิ่งที่คุณเรียกใช้บน CPU นั้นเป็นสิ่งที่ซ้ำซ้อน คุณอาจจะเขียนมันซ้ำในภาษาลำดับที่สูงขึ้น แต่มันจะถูกรวบรวมเป็นคำสั่งแอสเซมเบลอร์ซ้ำซ้ำ ๆ ดังนั้นจริงๆแล้วคอมไพเลอร์ทำสิ่งที่คุณขออย่างแน่นอนมันแปลงการสอบถามซ้ำทั้งหมดของคุณเป็นคำแนะนำซ้ำ ๆ สำหรับซีพียู
Davor

6
ในระดับต่ำเราส่วนใหญ่รู้ว่าการเรียกซ้ำเท่ากับการทำซ้ำบวกกับสแต็ก การเรียกใช้แกรมม่าแบบไม่มีบริบทโดยไม่มีการวนซ้ำในขณะที่การกดออโตมาตาเป็น "โปรแกรม" ซ้ำพร้อมด้วยหน่วยความจำแบบสแต็ก
Hendrik Jan

2
เพียงชี้ให้เห็นว่าโดยทั่วไปแล้วควรรออย่างน้อย 24 ชั่วโมงเพื่อดูว่ามีคำตอบอื่น ๆ ปรากฏขึ้นหรือไม่ คำถามที่ได้รับการยอมรับดูเหมือนจะค่อนข้างยาวเกินไปและโน้มน้าวใจฉันอย่างตรงไปตรงมาและดูเหมือนจะทำให้เรื่องซับซ้อนเกินความจำเป็นเกินความจำเป็น คำตอบพื้นฐานสำหรับคำถามของคุณคือ "สแต็กที่ใช้สำหรับการเรียกซ้ำสามารถดำเนินการได้อย่างชัดเจนในแบบวนซ้ำ" และไม่จำเป็นต้องซับซ้อนกว่านั้นอีกมาก ข้อควรพิจารณาเกี่ยวกับหน่วยความจำที่ถูก จำกัด หรือไม่เข้ามาเล่นเนื่องจากคุณลักษณะนั้นเป็นสิ่งจำเป็นสำหรับการกองซ้อนซ้ำเช่นกันอย่างไรก็ตาม
AnoE

เป็นไปได้ที่จะใช้เครื่องจำลองแบบทัวริงด้วยการวนซ้ำเท่านั้น
Sarge Borsch

ในชั้นเรียนเปรียบเทียบภาษาที่ฉันใช้เวลานานแล้วเราต้องเขียนหน้าที่ของแอคเคอร์แมนในการประชุมในฟอร์แทรน (ไม่ใช่ฟอร์แทรนสมัยใหม่) และในภาษาที่เลือกซ้ำ ใช่มันเป็นไปได้ในทางปฏิบัติ และแน่นอนว่าเป็นไปได้ในทางทฤษฎี ดูตัวอย่างคำถามพิสูจน์ให้เห็นว่าเครื่องจักรทัวริงและแคลคูลัสแลมบ์ดานั้นเทียบเท่ากัน
David Hammen

คำตอบ:


51

มันเป็นไปได้ที่จะเปลี่ยนการเรียกซ้ำโดยย้ำบวกหน่วยความจำมากมาย

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

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

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

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

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

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

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

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

¹ ลูปในภาษา C-like สามารถทำซ้ำได้ไม่ จำกัด เช่นเดียวกับการประชุมเพื่อ จำกัด การทำซ้ำ เมื่อผู้คนพูดถึง“ สำหรับลูป” ในทฤษฎีการคำนวณนั่นหมายถึงเฉพาะลูปที่นับจาก 1 ถึง (หรือเทียบเท่า) forwhilen


ยอมรับคำอธิบายรายละเอียดเก็บไว้ในระดับที่ร้องขอ
Tobi Alafin

12
ฉันคิดว่ามันควรค่ากว่าในคอมพิวเตอร์จริงการเรียกซ้ำยังใช้หน่วยความจำมากขึ้น ดังนั้นในการใช้งานจริงไม่มีความต้องการของหน่วยความจำที่ไม่มีขอบเขต (เพราะทุกอย่างถูก จำกัด ขอบเขตโดยเท่ากัน) และการเรียกซ้ำบ่อยครั้งต้องใช้หน่วยความจำมากกว่าการทำซ้ำ
Agent_L

@Agent_L ใช่การเรียกซ้ำต้องการหน่วยความจำที่ไม่ได้ จำกัด เพื่อจัดเก็บสแต็กเฟรมทั้งหมด ด้วยวิธีการเรียกซ้ำมีความต้องการหน่วยความจำที่ไม่ได้ จำกัด แต่มันไม่ได้แยกจากสัญชาตญาณของการดำเนินการเช่นเดียวกับ iterations
Gilles 'หยุดความชั่วร้าย'

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

1
@BlackVegetable หากเมธอดแบบเรียกซ้ำไม่มีตัวแปรภายในและหน่วยความจำเพียงอย่างเดียวที่ใช้คือ call call (สิ่งที่สามารถปรับให้เหมาะสมโดย TCO) นั่นหมายความว่าเวอร์ชันที่ซ้ำกันจะไม่มีตัวแปรภายในและใช้หน่วยความจำคงที่เท่านั้น ตัวแปร) แชร์ในการวนซ้ำทั้งหมดเช่นตัวนับ
Agent_L

33

การเรียกซ้ำทั้งหมดสามารถแปลงเป็นการวนซ้ำตามที่เห็นโดย CPU ของคุณซึ่งดำเนินการโปรแกรมตามอำเภอใจโดยใช้การทำซ้ำแบบไม่ จำกัด การเรียกใช้ นี่คือรูปแบบของทฤษฎีบทBöhm-Jacopini นอกจากนี้หลายรุ่นทัวริงสมบูรณ์ของการคำนวณจะไม่มีการเรียกซ้ำเช่นเครื่องจักรทัวริงและเครื่องเคาน์เตอร์

ฟังก์ชั่นดั้งเดิม recursive สอดคล้องกับโปรแกรมที่ใช้boundedย้ำ, ที่อยู่, คุณจะต้องระบุจำนวนซ้ำที่วงจะถูกดำเนินการล่วงหน้า การวนซ้ำไม่สามารถจำลองการสอบถามซ้ำโดยทั่วไปได้เนื่องจากฟังก์ชัน Ackermann ไม่ใช่การเรียกซ้ำแบบดั้งเดิม แต่การวนซ้ำที่ไม่ จำกัด สามารถจำลองฟังก์ชันที่คำนวณได้บางส่วน


25

เป็นตัวอย่างของคำตอบจาก Gilles นี่คืออัลกอริทึม "ซ้ำ" สำหรับฟังก์ชัน Ackermann (ใช้รุ่นAckermann-Péterทั่วไปที่กล่าวถึงโดย Wikipedia )a(n,m)

เราจำเป็นต้องมีสแต็คของจำนวนเต็มs

สแต็กดังกล่าวมีสองการดำเนินการแก้ไข (ซึ่งทำให้องค์ประกอบใหม่บนสแต็ก) และซึ่งดึง (และลบ) องค์ประกอบด้านบนและหนึ่งการดำเนินการสืบค้นซึ่งจะคืนค่าจริงหากไม่มีองค์ประกอบเหลืออยู่ในสแต็ก (false หากมีมากกว่า) .x x pop ( s ) ที่ว่างเปล่า( s )push(s,x)xxpop(s)empty(s)

Ackermann(n0,m0):

  • s= (เตรียมใช้งานสแต็กเปล่า)
  • push(s,n0)
  • push(s,m0)
  • while(true):
    • mpop(s)
    • if(empty(s)):
      • return m (ซึ่งจะจบลูป)
    • npop(s)
    • if(n=0):
      • push(s,m+1)
    • else if(m=0):
      • push(s,n1)
      • push(s,1)
    • else:
      • push(s,n1)
      • push(s,n)
      • push(s,m1)

ฉันนำสิ่งนี้ไปใช้ใน Ceylon คุณสามารถเรียกใช้มันได้ใน WebIDEหากคุณต้องการ (มันจะสแต็กเอาท์พุทที่จุดเริ่มต้นของการวนซ้ำแต่ละครั้งของลูป)

แน่นอนว่าสิ่งนี้เพิ่งย้ายสแต็กการเรียกแบบสแต็กจากการเรียกซ้ำไปเป็นสแต็กที่ชัดเจนพร้อมพารามิเตอร์และค่าส่งคืน


15
ฉันคิดว่านี่เป็นจุดสำคัญ คุณได้แสดงให้เห็นอย่างชัดเจนว่าการเรียกซ้ำไม่มีอะไรพิเศษและสามารถลบออกได้เพียงแค่ติดตามการโทรสแต็กด้วยตัวเองแทนที่จะปล่อยให้คอมไพเลอร์ทำ การเรียกซ้ำเป็นเพียงคุณสมบัติคอมไพเลอร์ที่ทำให้การเขียนโปรแกรมประเภทนี้ง่ายขึ้น
David Richerby

4
ขอบคุณที่สละความพยายามในการเขียนโปรแกรมที่ร้องขอ
Tobi Alafin

15

มีคำตอบที่ดีอยู่แล้ว (ซึ่งฉันไม่สามารถแม้แต่จะหวังที่จะแข่งขันด้วย) แต่ฉันต้องการที่จะอธิบายอย่างง่าย ๆ นี้

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

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


2

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

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