ขึ้นอยู่กับว่าคุณกำหนด "การเรียกซ้ำ" อย่างเข้มงวดเพียงใด
หากเราต้องการให้มันเกี่ยวข้องกับ call-stack (หรือกลไกใด ๆ ในการรักษาสถานะของโปรแกรม) เราก็สามารถแทนที่มันด้วยสิ่งที่ไม่ได้ แท้จริงแล้วภาษาที่นำไปสู่การใช้การเรียกซ้ำอย่างหนักตามธรรมชาติมักจะมีคอมไพเลอร์ที่ใช้การปรับให้เหมาะสมแบบหางเรียกอย่างหนักดังนั้นสิ่งที่คุณเขียนจะเรียกซ้ำ แต่สิ่งที่คุณเรียกใช้ซ้ำ
แต่ให้พิจารณากรณีที่เราโทรซ้ำและใช้ผลการโทรซ้ำสำหรับการโทรซ้ำนั้น
public static BigInteger Ackermann(BigInteger m, BigInteger n)
{
if (m == 0)
return n+1;
if (n == 0)
return Ackermann(m - 1, 1);
else
return Ackermann(m - 1, Ackermann(m, n - 1));
}
การโทรซ้ำแบบเรียกซ้ำครั้งแรกทำได้ง่าย:
public static BigInteger Ackermann(BigInteger m, BigInteger n)
{
restart:
if (m == 0)
return n+1;
if (n == 0)
{
m--;
n = 1;
goto restart;
}
else
return Ackermann(m - 1, Ackermann(m, n - 1));
}
จากนั้นเราสามารถทำความสะอาดลบgoto
เพื่อกำจัดvelociraptorsและร่มเงาของ Dijkstra:
public static BigInteger Ackermann(BigInteger m, BigInteger n)
{
while(m != 0)
{
if (n == 0)
{
m--;
n = 1;
}
else
return Ackermann(m - 1, Ackermann(m, n - 1));
}
return n+1;
}
แต่หากต้องการลบการโทรแบบเรียกซ้ำอื่น ๆ เราจะต้องเก็บค่าการโทรบางส่วนไว้ในสแต็ก:
public static BigInteger Ackermann(BigInteger m, BigInteger n)
{
Stack<BigInteger> stack = new Stack<BigInteger>();
stack.Push(m);
while(stack.Count != 0)
{
m = stack.Pop();
if(m == 0)
n = n + 1;
else if(n == 0)
{
stack.Push(m - 1);
n = 1;
}
else
{
stack.Push(m - 1);
stack.Push(m);
--n;
}
}
return n;
}
ตอนนี้เมื่อเราพิจารณาซอร์สโค้ดเราได้เปลี่ยนวิธีการเรียกซ้ำของเราให้เป็นซ้ำอย่างแน่นอน
เมื่อพิจารณาถึงสิ่งที่ถูกคอมไพล์เราได้เปลี่ยนรหัสที่ใช้ call stack เพื่อนำ recursion มาใช้เป็นโค้ดที่ไม่ (และในการทำโค้ดหันที่จะโยนข้อยกเว้น stack-overflow สำหรับค่าที่ค่อนข้างเล็กลงในโค้ดที่จะ ใช้เวลานานอย่างเลือดตาแทบกระเด็นเพื่อส่งคืน [ดูฉันจะป้องกันไม่ให้ฟังก์ชั่น Ackerman ของฉันล้นสแต็คได้อย่างไรสำหรับการเพิ่มประสิทธิภาพเพิ่มเติมที่ทำให้มันกลับมาจริงสำหรับอินพุตที่เป็นไปได้มากขึ้น])
เมื่อพิจารณาถึงวิธีการเรียกใช้ซ้ำโดยทั่วไปเราได้เปลี่ยนรหัสที่ใช้ call-stack เป็นรหัสที่ใช้ stack ที่แตกต่างกันเพื่อระงับการดำเนินการที่ค้างอยู่ เราสามารถยืนยันว่ามันยังคงเรียกซ้ำเมื่อพิจารณาในระดับต่ำนั้น
และในระดับนั้นไม่มีทางอื่นรอบตัว ดังนั้นหากคุณพิจารณาวิธีนั้นซ้ำแล้วซ้ำอีกมีบางสิ่งที่เราไม่สามารถทำได้หากไม่มี โดยทั่วไปแม้ว่าเราจะไม่ติดป้ายโค้ดซ้ำซ้ำ การเรียกใช้คำซ้ำนั้นมีประโยชน์เพราะครอบคลุมวิธีการบางอย่างและให้วิธีการพูดคุยกับเราและเราไม่ได้ใช้วิธีใดวิธีหนึ่งต่อไปนี้
แน่นอนทั้งหมดนี้ถือว่าคุณมีทางเลือก มีทั้งภาษาที่ห้ามการโทรซ้ำและภาษาที่ไม่มีโครงสร้างการวนซ้ำที่จำเป็นสำหรับการวนซ้ำ