ลบการเรียกซ้ำ - ดูทฤษฎีหลังฉาก


10

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

ดังนั้นฉันรู้ว่าโปรแกรมแบบเรียกซ้ำทุกตัวมีแอนะล็อกซ้ำแล้วซ้ำอีกและฉันเข้าใจคำอธิบายยอดนิยมที่เสนอให้โดยการรักษาบางสิ่งที่คล้ายกับ "ระบบสแต็ก" และผลักดันการตั้งค่าสภาพแวดล้อมเช่นที่อยู่ผู้ส่ง ฯลฯ .

เป็นรูปธรรมมากขึ้นผมอยากจะ (อย่างเป็นทางการ) ดูวิธีการหนึ่งไม่ได้พิสูจน์ว่าคำสั่งนี้ในกรณีที่คุณมีฟังก์ชั่นห่วงโซ่กล่าวอ้าง 0 นอกจากนี้จะเกิดอะไรขึ้นถ้ามีข้อความบางส่วนที่มีเงื่อนไขซึ่งอาจทำให้F ฉันโทรไปหาF j ได้บ้าง? นั่นคือกราฟการเรียกใช้ฟังก์ชันที่มีศักยภาพมีองค์ประกอบที่เชื่อมโยงกันอย่างมากF0F1...FผมFผม+1...FnF0FผมFJ

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

สิ่งที่ฉันถามจริงๆคือคำถาม2

(1) มีหลักฐานที่เป็นทางการ (น่าเชื่อถือมากกว่านี้) จริง ๆ หรือไม่ว่าการเรียกซ้ำสามารถเปลี่ยนเป็นการวนซ้ำได้?

(2) ถ้าทฤษฏีนี้อยู่ข้างนอกจริงๆแล้วทำไมฉันถึงค้นหาตัวอย่างเช่นการวนคำสั่งpreeratizingง่ายขึ้นและ postorder ยากมาก? (นอกเหนือจากสติปัญญาที่ จำกัด ของฉัน)


1
เหมือนคำซ้ำแล้วซ้ำอีก :)
Akash Kumar

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

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

ขอบคุณทุกคนสำหรับความคิดเห็นของคุณ - ฉันคิดว่าฉันได้อ่านมาบ้างแล้ว
Itachi Uchiha

@ ราฟาเอล - หนึ่งความคิดเห็นเกี่ยวกับสาเหตุที่ฉันคิดว่าการโพสต์ซ้ำ iteratizing ยาก (นอกเหนือจากฉันไม่สามารถทำมันได้) ฉันอ่านบทความไม่กี่ข้อเกี่ยวกับการลบการเรียกซ้ำและวิ่งเข้าไปในสิ่งที่เรียกว่าฟังก์ชั่นการเรียกซ้ำแบบหาง ปรากฎว่าพวกมันง่ายกว่าในการคำนวณซ้ำ ฉันยังไม่เข้าใจอย่างเป็นทางการว่าทำไมเรื่องนี้ถึงเป็นจริง แต่มีสิ่งอื่นที่ฉันควรเพิ่ม ฉันได้ยินมาว่าการเรียงลำดับซ้ำ ๆ นั้นต้องมีสองกองและไม่เหลือใคร แต่ไม่ทราบรายละเอียด และตอนนี้ฉันหลงทาง - ทำไมความแตกต่างระหว่างโหมดสำรวจเส้นทางทั้งสองนี้ และทำไมการเรียกซ้ำหางจึงง่ายต่อการจัดการ
Itachi Uchiha

คำตอบ:


6

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

ดังนั้นถือว่าเรามี "ห่วงโซ่เรียกว่า" F หากเราสมมติว่าF 1 , , F nไม่ได้วนซ้ำ (เพราะเราได้แปลงพวกมันไปแล้ว) เราสามารถอินไลน์การเรียกทั้งหมดเหล่านั้นลงในคำจำกัดความของFซึ่งจะกลายเป็นฟังก์ชันแบบเรียกซ้ำโดยตรงที่เราสามารถจัดการได้FF1FnFF1,...,FnF

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

f(0) = a
f(n) = f'(g(n-1))

g(0) = b
g(n) = g'(f(n-1))

ด้วยฟังก์ชั่นf'และg'ไม่ใช่แบบเรียกซ้ำ (หรืออย่างน้อยก็เป็นอิสระจากfและg) จะกลายเป็น

h(0) = (a,b)
h(n) = let (f,g) = h(n-1) in (f'(g), g'(f)) end

f(n) = let (f, _) = h(n) in f end
g(n) = let (_, g) = h(n) in g end

สิ่งนี้จะขยายไปยังฟังก์ชันที่เกี่ยวข้องและฟังก์ชันที่ซับซ้อนมากขึ้นตามธรรมชาติ


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

1
Raphel เคล็ดลับของคุณใช้งานได้เฉพาะเมื่อทั้งฟังก์ชั่นวนซ้ำยอมรับอาร์กิวเมนต์ที่มีประเภทเดียวกัน หากfและgยอมรับประเภทที่แตกต่างกันจำเป็นต้องใช้กลอุบายทั่วไปที่มากขึ้น
Andrej Bauer

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

fgn-1n-2

ดูคำตอบของฉันเกี่ยวกับวิธีการทำ
Andrej Bauer

8

ใช่มีเหตุผลที่น่าเชื่อถือที่จะเชื่อว่าการสอบถามซ้ำสามารถเปลี่ยนเป็นการทำซ้ำได้ นี่คือสิ่งที่คอมไพเลอร์ทุกคนทำเมื่อแปลซอร์สโค้ดเป็นภาษาเครื่อง สำหรับทฤษฎีคุณควรทำตามคำแนะนำของ Dave Clarke หากคุณต้องการดูรหัสจริงที่แปลงการเรียกซ้ำเป็นรหัสที่ไม่ใช่แบบเรียกซ้ำดูที่machine.mlภาษา MiniML ในสวนสัตว์ PLของฉัน(สังเกตว่าloopฟังก์ชั่นที่ด้านล่างซึ่งจริง ๆ แล้วเรียกใช้รหัสนั้นเป็นแบบเรียกซ้ำและมันสามารถ ถูกแปลงเป็นลูปเล็กน้อยจริง ๆ )

อีกหนึ่งสิ่ง. MiniML ไม่รองรับฟังก์ชั่นวนซ้ำ แต่นี่ไม่ใช่ปัญหา หากคุณมีการเรียกซ้ำระหว่างฟังก์ชัน

1:A1B1
2:A2B2
n:AnBn

การเรียกซ้ำสามารถแสดงในรูปของแผนที่แบบเรียกซ้ำ

:A1++AnB1++Bn,

8

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

วิธีการที่เกี่ยวข้องเป็นเครื่อง CEK

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

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


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

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

@ItachiUchiha - ฉันไม่คิดว่าปัญหาของคุณจะไม่สามารถตัดสินใจได้ ดูคำตอบของ Andrej Bauer เขาบันทึกว่าคอมไพเลอร์ทุกตัวจะทำเมื่อแปลซอร์สโค้ดเป็นภาษาเครื่อง นอกจากนี้เขายังเพิ่มคุณสามารถดูรหัสจริงที่แปลงแบบเรียกซ้ำให้เป็นแบบไม่เรียกซ้ำในภาษา MiniM (a) l สิ่งนี้บ่งชี้อย่างชัดเจนว่ามีขั้นตอนการตัดสินใจในการเรียกซ้ำ "iteratize" ฉันไม่แน่ใจเกี่ยวกับความยากลำบาก / ความซับซ้อนในการลบการเรียกซ้ำ ฉันไม่เข้าใจคำถามนี้อย่างชัดเจน แต่มันดูน่าสนใจ บางทีคุณสามารถแก้ไขคำถามของคุณเพื่อให้ได้คำตอบที่ดีกว่า
Akash Kumar

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

6

คำถาม : "มีหลักฐานที่เป็นทางการ (น่าเชื่อถือมากกว่านี้) หรือไม่ที่สามารถแปลงการเรียกซ้ำเป็นการวนซ้ำได้หรือไม่"

ตอบ : ทัวริงสมบูรณ์ของเครื่องทัวริง :-)

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

ผมคิดว่าคุณสามารถหาเอกสารจำนวนมาก / บทความที่ " recursive เพื่อการแปลงซ้ำ " (ดูคำตอบของเดฟหรือเพียงแค่ Google คำหลัก) แต่บางทีอาจจะเป็นที่รู้จักกันน้อย (และการปฏิบัติ ) วิธีการคือการวิจัยล่าสุดเกี่ยวกับการใช้ฮาร์ดแวร์ของขั้นตอนวิธี recursive ( ใช้ภาษา VHDLที่ "เรียบเรียง" ลงในฮาร์ดแวร์โดยตรง) ตัวอย่างเช่นดูกระดาษของ V.Sklyarov "การนำFPGA ที่ใช้อัลกอริธึมวนซ้ำมาใช้ " ( บทความแนะนำวิธีการใหม่สำหรับการใช้อัลกอริธึมแบบวนซ้ำในฮาร์ดแวร์ ... ในรายละเอียด .... )


1

หากคุณคุ้นเคยกับภาษาที่รองรับลูกแกะแล้วอเวนิวแห่งหนึ่งคือดูการเปลี่ยนแปลง CPS การลบการใช้ call stack (และเรียกซ้ำโดยเฉพาะ) เป็นสิ่งที่การแปลง CPS ทำได้ มันแปลงโปรแกรมที่มีการเรียกโพรซีเดอร์เป็นโปรแกรมที่มีเฉพาะการเรียกแบบ tail เท่านั้น (คุณสามารถนึกได้ว่านี่เป็น gotos

การแปลง CPS มีความสัมพันธ์อย่างใกล้ชิดกับการเก็บ call stack ในสแต็กตามอาร์เรย์แบบดั้งเดิมอย่างชัดเจน แต่แทนที่จะอยู่ในอาร์เรย์ call stack จะแสดงด้วยการปิดที่เชื่อมโยง


0

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

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

มาก BKD เกี่ยวกับเรื่องนี้อยู่ในวิกิพีเดียหน้านี้คริสตจักรทัวริงวิทยานิพนธ์ ฉันไม่แน่ใจเกี่ยวกับรายละเอียดที่แน่นอน แต่บทความวิกิพีเดียดูเหมือนจะระบุว่ามันเป็น Rosser (1939) ที่ 1 พิสูจน์ความเท่าเทียมกันระหว่างแคลคูลัสแลมบ์ดาและทัวริง อาจจะ / สันนิษฐานว่ากระดาษของเขามีกลไกแบบกองซ้อนสำหรับการแปลงแลมบ์ดา (เรียกซ้ำ) ไปสู่การสร้าง tm?

Rosser, JB (1939) "นิทรรศการอย่างไม่เป็นทางการของบทพิสูจน์ทฤษฎีบทของ Godel และทฤษฎีของโบสถ์" วารสารสัญลักษณ์เชิงสัญลักษณ์ (วารสารสัญลักษณ์เชิงตรรก, เล่ม 4, หมายเลข 2) 4 (2): 53–60 ดอย: 10.2307 / 2,269,059 JSTOR 2269059

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


1
การพิสูจน์ความเท่าเทียมกันของทัวริง / แลมบ์ดาอยู่ในภาคผนวกของเอกสารนี้: www.cs.virginia.edu/~robins/Turing_Paper_1936.pdf
Radu GRIGore
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.