Bu özyinelemeyi ne kadar kesin tanımladığınıza bağlıdır.
Çağrı yığınını (veya program durumunu sürdürmek için hangi mekanizmaları kullanıyorsanız) dahil etmeyi kesinlikle istiyorsak, onu her zaman yerine geçmeyen bir şeyle değiştirebiliriz. Gerçekten de, doğal olarak özyinelemenin yoğun kullanımına neden olan diller, kuyruk çağrısı optimizasyonunu yoğun şekilde kullanan derleyicilere sahip olma eğilimindedir, bu nedenle yazdıklarınız özyinelemelidir ancak çalıştırdığınız şey yinelemelidir.
Ancak, özyinelemeli bir çağrı yaptığımız ve bir özyinelemeli çağrı için özyinelemeli çağrı sonucunu kullandığımız bir durumu düşünelim.
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));
}
İlk özyinelemeli aramayı yinelemeli yapmak kolaydır:
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));
}
Daha sonra temizlik yapabiliriz, velociraptors ve Dijkstra'nın gölgesini goto
önlemek için:
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;
}
Ancak diğer özyinelemeli aramaları kaldırmak için bazı aramaların değerlerini bir yığında depolamak zorunda kalacağız:
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;
}
Şimdi, kaynak kodu göz önüne aldığımızda, özyinelemeli yöntemimizi kesinlikle yinelemeli bir yönteme dönüştürdük.
Bunun neye derlendiğini göz önüne alarak, çağrı kodunu özyinelemeyen koda dönüştürmek için çağrı yığınını kullanan kod haline getirdik (ve bunu yaparken, sadece küçük kodlar için bile bir yığın taşması istisnası atacak kod haline getirdik) geri dönmek için müthiş uzun bir zaman ayırın [bkz. Ackerman işlevimin yığından taşmasını nasıl önleyebilirim? Gerçekten daha birçok giriş için geri dönmesini sağlayan bazı daha fazla optimizasyon için].
Genel olarak nasıl özyinelemenin uygulandığı göz önüne alındığında, çağrı yığınını kullanan kodu bekleyen işlemleri tutmak için farklı bir yığın kullanan koduna dönüştürdük. Bu nedenle, bu düşük seviyede değerlendirildiğinde hala özyinelemeli olduğunu savunabiliriz.
Ve bu seviyede, gerçekten de onun etrafında başka yollar yok. Eğer bu yöntemi özyinelemeli olarak düşünürseniz, gerçekten onsuz yapamayacağımız şeyler var. Genelde böyle bir kodu özyinelemeli olmasak da. Terimi tekrarlama o yaklaşımların belli kümelerini kapsar ve bize onlar hakkında konuşmak için bir yol verir, biz de artık bunlardan biri kullanıyor çünkü yararlıdır.
Elbette, bunların hepsi bir seçeneğiniz olduğunu varsayar. Özyinelemeli çağrıları yasaklayan diller ve yineleme için gerekli olan döngüsel yapıları olmayan diller vardır.