Her özyineleme yinelemeye dönüştürülebilir mi?


181

Bir reddit iş parçacığı görünüşte ilginç bir soru getirdi:

Kuyruk özyinelemeli işlevler önemsiz bir şekilde yinelemeli işlevlere dönüştürülebilir. Diğerleri, açık bir yığın kullanılarak dönüştürülebilir. Can her yineleme yineleme dönüştürülebilir?

Yayındaki (counter?) Örneği çifttir:

(define (num-ways x y)
  (case ((= x 0) 1)
        ((= y 0) 1)
        (num-ways2 x y) ))

(define (num-ways2 x y)
  (+ (num-ways (- x 1) y)
     (num-ways x (- y 1))

3
Bunun nasıl bir karşı örnek olduğunu anlamıyorum. Yığın tekniği çalışacaktır. Güzel olmayacak ve ben yazmayacağım, ama yapılabilir. Görünüşe göre akdas bağlantınızda bunu kabul ediyor.
Matthew Flaschen

(Num-way xy) sadece (x + y) choosex = (x + y)! / (X! Y!) Şeklindedir ve özyineleme gerektirmez.
ShreevatsaR


Özyineleme sadece kolaylık olduğunu söyleyebilirim.
e2-e4

Yanıtlar:


182

Her zaman yinelemeli bir işlevi yinelemeli bir işleve dönüştürebilir misiniz? Evet, kesinlikle, ve Church-Turing tezi, hafızanın işe yarayıp yaramadığını kanıtlıyor. Meslekten olmayan terimlerle, özyinelemeli işlevlerle hesaplanabilen şeyin yinelemeli bir modelle (Turing makinesi gibi) hesaplanabileceğini ve tersini belirtir. Tez size tam olarak dönüşümün nasıl yapılacağını söylemiyor, ancak bunun kesinlikle mümkün olduğunu söylüyor.

Çoğu durumda, özyinelemeli bir işlevi dönüştürmek kolaydır. Knuth "Bilgisayar Programlama Sanatı" nda çeşitli teknikler sunmaktadır. Ve sıklıkla, özyinelemeli olarak hesaplanan bir şey, daha az zaman ve mekanda tamamen farklı bir yaklaşımla hesaplanabilir. Bunun klasik örneği Fibonacci sayıları veya bunların dizileridir. Bu problemi kesinlikle derece planınızda karşıladınız.

Bu madalyonun kapak tarafında, bir formülün özyinelemeli bir tanımını, önceki sonuçları hatırlamak için bir davet olarak ele alacak kadar gelişmiş bir programlama sistemi hayal edebiliriz, böylece bilgisayara tam olarak hangi adımları atma zorluğu olmadan hız avantajı sunarız özyinelemeli bir tanım ile bir formülün hesaplanmasını takip edin. Dijkstra neredeyse böyle bir sistem hayal etti. Uygulamayı bir programlama dilinin anlambiliminden ayırmaya çalışmak için uzun zaman harcadı. Daha sonra, deterministik olmayan ve çok işlemcili programlama dilleri, pratik profesyonel programcının üstünde bir ligde.

Son analizde, birçok fonksiyonun özyinelemeli biçimde anlaşılması, okunması ve yazılması daha kolaydır. Zorlayıcı bir neden olmadıkça, muhtemelen bu işlevleri açık bir şekilde yinelemeli algoritmaya dönüştürmemelisiniz. Bilgisayarınız bu işi doğru bir şekilde gerçekleştirecektir.

Zorlayıcı bir neden görüyorum. [ Asbest iç çamaşırı giymek ] Şeması, Lisp, Haskell, OCaml, Perl veya Pascal gibi süper yüksek bir dilde bir prototip sisteminiz olduğunu varsayalım . Koşulların C veya Java'da bir uygulamaya ihtiyacınız olduğunu varsayalım. (Belki de siyasettir.) O zaman tekrar tekrar yazılan ancak kelimenin tam anlamıyla tercüme edilen, çalışma zamanı sisteminizi patlatacak bazı işlevleriniz olabilir. Örneğin, Şema'da sonsuz kuyruk yinelemesi mümkündür, ancak aynı deyim mevcut C ortamları için bir soruna neden olur. Başka bir örnek, Pascal'ın desteklediği, ancak C'nin desteklemediği, sözcüksel olarak iç içe geçmiş işlevlerin ve statik kapsamın kullanılmasıdır.

Bu durumlarda, orijinal dile karşı politik direnişin üstesinden gelmeye çalışabilirsiniz. Greenspun'un (yanaktaki dil) onuncu yasasında olduğu gibi, Lisp'i kötü bir şekilde yeniden uygularken bulabilirsiniz. Veya çözüm için tamamen farklı bir yaklaşım bulabilirsiniz. Ancak her durumda, kesinlikle bir yol var.


10
Church-Turing henüz kanıtlanmış değil mi?
Liran Orevi

15
@eyelidlessness: A'yı B'ye uygulayabiliyorsanız, B'nin en az A kadar gücü olduğu anlamına gelir. A'nın B-uygulamasında A'nın bazı ifadelerini yürütemezseniz, bu bir uygulama değildir. B'de A ve B'de A uygulanabilirse, güç (A)> = güç (B) ve güç (B)> = güç (A) uygulanabilir. Tek çözüm güç (A) == güç (B).
Tordek

6
re: 1. paragraf: Church-Turing tezinden değil, hesaplama modellerinin denkliği hakkında konuşuyorsunuz. Eşdeğerlik, Kilise ve / veya Turing tarafından kanıtlanmış AFAIR idi, ancak tez değil. Tez, sezgisel olarak hesaplanabilir her şeyin sıkı matematiksel anlamda hesaplanabileceği deneysel bir gerçektir (Turing makineleri / özyinelemeli fonksiyonlar vb.). Fizik yasalarını kullanarak, Turing makinelerinin yapamayacağı bir şeyi hesaplayan klasik olmayan bazı bilgisayarlar oluşturabilirsek (örneğin durma sorunu) kanıtlanamayabilir. Oysa denklik matematiksel bir teoremdir ve kanıtlanmayacaktır.
sdcvvc

7
Bu cevap nasıl olumlu oy aldı? Önce Turing bütünlüğünü Church-Turing tezi ile karıştırır, daha sonra "gelişmiş" sistemlerden bahsederek ve tembel sonsuz kuyruk özyinelemesini düşürerek bir grup yanlış el işi yapar (çünkü C veya herhangi bir Turing tam dilinde yapabilirsiniz .. uh. Turing'in tamamlanmasının ne anlama geldiğini bilen var mı?). O zaman umut verici bir el tutma sonucu, bu Oprah ile ilgili bir soruydu ve tek ihtiyacınız olan pozitif ve canlandırıcı olmak mı? Korkunç cevap!
ex0du5

8
Ve anlambilimle ilgili bs ??? Gerçekten mi? Bu sözdizimsel dönüşümler ile ilgili bir sorudur ve bir şekilde damla Dijkstra'yı adlandırmanın ve pi-matematik hakkında bir şeyler bildiğiniz anlamına gelmek için harika bir yol haline geldi? Bunu açıklığa kavuşturalım: Bir kişinin dilin anlamsal anlamlarına veya başka bir modele bakıp bakmadığı, bu sorunun cevabı üzerinde hiçbir etkisi olmayacaktır. Dilin meclis ya da üretken bir etki alanı modelleme dili olması hiçbir şey ifade etmez. Sadece Turing tamlığı ve "yığın değişkenleri" nin "değişkenler yığını" na dönüştürülmesi ile ilgilidir.
ex0du5

43

Her özyinelemeli işlev için özyinelemesiz bir form yazmak her zaman mümkün mü?

Evet. Basit bir resmi kanıt, hem µ özyineleme hem de GOTO gibi özyinelemesiz bir hesabın Turing tamamlandığını göstermektir. Tüm Turing tam hesaplamaları ifade güçlerinde kesinlikle eşdeğer olduğundan, tüm özyinelemeli işlevler, özyinelemeyen Turing-tam hesap ile uygulanabilir.

Ne yazık ki, GOTO çevrimiçi iyi, resmi bir tanım bulamıyorum bu yüzden burada:

Bir GOTO program komutları bir dizi olduğunda , P bir yürütüldüğünde yazmaç makinesi öyle ki p aşağıdakilerden biridir:

  • HALTyürütmeyi durduran
  • r = r + 1rherhangi bir kayıt nerede
  • r = r – 1rherhangi bir kayıt nerede
  • GOTO xxetiket nerede
  • IF r ≠ 0 GOTO xrherhangi bir kayıt nerede ve xbir etiket
  • Yukarıdaki komutlardan herhangi birini izleyen bir etiket.

Bununla birlikte, özyinelemeli ve özyinelemesiz işlevler arasındaki dönüşümler her zaman önemsiz değildir (çağrı yığınının dikkatsiz manuel olarak yeniden uygulanması dışında).

Daha fazla bilgi için bu cevaba bakınız .


Mükemmel cevap! Ancak uygulamada, özyinelemeli algosun yinelemeli haline getirilmesinde büyük zorluk çekiyorum. Örneğin, burada sunulan monomorfik yazıcıyı topluluk.topcoder.com/… ' ı yinelemeli bir algoritmaya çeviremedim
Nils

31

Özyineleme, gerçek yorumlayıcılarda veya derleyicilerde yığınlar veya benzer yapılar olarak uygulanır. Böylece, özyinelemeli bir işlevi yinelemeli bir karşılığa dönüştürebilirsiniz, çünkü her zaman böyle yapılır (otomatik olarak) . Derleyicinin çalışmasını geçici ve muhtemelen çok çirkin ve verimsiz bir şekilde çoğaltacaksınız.


13

Temel olarak evet, esasen yapmanız gereken, yöntem çağrılarını (durumu örtük olarak yığına iten) açık yığın haline getirmek, 'önceki çağrı'nın nereden geldiğini hatırlamak ve daha sonra' çağrılan yöntemi 'yürütmektir. yerine.

Bir döngü, bir yığın ve bir durum-makine kombinasyonunun temelde yöntem çağrılarını simüle ederek tüm senaryolar için kullanılabileceğini hayal ediyorum. Bunun 'daha iyi' olup olmayacağı (daha hızlı ya da bir anlamda daha verimli) genel olarak söylemek mümkün değildir.


9
  • Yinelemeli işlev yürütme akışı bir ağaç olarak gösterilebilir.

  • Aynı mantık, o ağacı taramak için bir veri yapısı kullanan bir döngü ile yapılabilir.

  • Derinlik ilk geçiş bir yığın kullanılarak yapılabilir, ilk genişlik ilk geçiş bir kuyruk kullanılarak yapılabilir.

Yani cevabı evet. Neden: https://stackoverflow.com/a/531721/2128327 .

Tek bir döngüde herhangi bir özyineleme yapılabilir mi? Evet çünkü

Turing makinesi tek bir döngü uygulayarak yaptığı her şeyi yapar:

  1. bir talimat getir,
  2. değerlendir,
  3. git 1.

7

Evet, açıkça bir yığın kullanmak (ancak özyineleme okumak çok daha hoş, IMHO).


17
Okumak her zaman daha keyifli diyemem. Hem yineleme hem de özyineleme yerlerine sahiptir.
Matthew Flaschen

6

Evet, yinelemesiz bir sürüm yazmak her zaman mümkündür. Önemsiz çözüm, bir yığın veri yapısı kullanmak ve özyinelemeli yürütmeyi simüle etmektir.


Yığın veri yapınız yığına tahsis edilirse amacı ortadan kaldırır ya da öbek üzerinde tahsis edilirse daha uzun sürer, hayır? Kulağa önemsiz geliyor ama bana verimsiz geliyor.
conradkleinespel

1
@conradk Bazı durumlarda, çağrı yığınını boşaltmak için yeterince büyük bir sorun üzerinde ağaç özyinelemeli bir işlem yapmanız gerekiyorsa yapılması gereken pratik bir şeydir; yığın belleği genellikle çok daha fazladır.
jamesdlin

4

Prensip olarak, hem veri yapıları hem de çağrı yığını için sonsuz duruma sahip bir dilde özyinelemeyi kaldırmak ve yinelemeyle değiştirmek her zaman mümkündür. Bu, Kilise Turing tezinin temel bir sonucudur.

Gerçek bir programlama dili göz önüne alındığında, cevap o kadar açık değildir. Sorun, programda ayrılabilecek bellek miktarının sınırlı olduğu, ancak kullanılabilecek çağrı yığını miktarının sınırsız olduğu bir dilin bulunması mümkündür (yığın değişkenlerinin adresinin bulunduğu 32 bit C ulaşılabilir değil). Bu durumda, özyineleme daha güçlüdür çünkü kullanabileceği daha fazla belleğe sahiptir; çağrı yığınını taklit etmek için yeterince ayrılan bellek yok. Bununla ilgili ayrıntılı bir tartışma için bu tartışmaya bakın .


2

Tüm hesaplanabilir fonksiyonlar Turing Machines tarafından hesaplanabilir ve dolayısıyla özyinelemeli sistemler ve Turing makineleri (yinelemeli sistemler) eşdeğerdir.


1

Bazen özyinelemeyi değiştirmek bundan daha kolaydır. Özyineleme eskiden CS'lerde 1990'larda öğretilen modaya uygun bir şeydi ve o zamandan beri birçok ortalama geliştirici, özyineleme ile bir şey çözdüyseniz, daha iyi bir çözümdü. Bu nedenle, düzeni tersine çevirmek için geriye doğru döngü yerine özyineleme ya da bunun gibi aptalca şeyler kullanırlardı. Yani bazen özyinelemeyi kaldırmak basit bir "duh, bu bariz" bir egzersiz türüdür.

Moda diğer teknolojilere doğru kaydığı için bu artık daha az problem.



0

Açık yığından Appart, özyinelemeyi yinelemeye dönüştürmek için başka bir desen bir trambolin kullanmaktır.

Burada, işlevler ya nihai sonucu döndürür ya da aksi takdirde gerçekleştireceği işlev çağrısının kapanmasını sağlar. Daha sonra, başlatma (trambolin) fonksiyonu, nihai sonuca ulaşılana kadar döndürülen kapakları çağırmaya devam eder.

Bu yaklaşım karşılıklı olarak yinelenen işlevler için çalışır, ancak korkarım sadece kuyruk çağrıları için çalışır.

http://en.wikipedia.org/wiki/Trampoline_(computers)


0

Evet diyebilirim - bir işlev çağrısı bir goto ve bir yığın işleminden başka bir şey değildir (kabaca konuşuyor). Tek yapmanız gereken, işlevleri çağırırken oluşturulan yığını taklit etmek ve bir goto gibi bir şey yapmaktır (bu anahtar kelimeyi açıkça içermeyen dillerle goto'ları taklit edebilirsiniz).


1
Bence OP bir kanıt ya da başka bir şey arıyor
Tim



-1

tazzego, özyineleme, bir işlevin ister beğensin ister beğenmesin kendisini çağırması anlamına gelir. İnsanlar bir şeyin özyineleme olmadan yapılıp yapılamayacağı hakkında konuşurken, bunun anlamı ve geçerli bir ifade olarak "hayır, bu doğru değil, çünkü özyineleme tanımına katılmıyorum" diyemezsiniz.

Bunu akılda tutarak, söylediğiniz hemen hemen her şey saçmalıktır. Bu saçmalık olmadığını söylediğin tek şey, çağrı istasyonu olmadan programlamayı hayal edemeyeceğiniz fikri. Bu, calltack kullanmaya başlayana kadar onlarca yıldır yapılan bir şeydi. FORTRAN'ın eski sürümlerinde bir çağrı damgası yoktu ve gayet iyi çalıştılar.

Bu arada, sadece bir döngü aracı olarak özyineleme (ör. SML) uygulayan Turing-tamamlanmış diller vardır. Ayrıca, yinelemeyi sadece bir döngü aracı olarak uygulayan Turing-tamamlanmış diller de vardır (örn. FORTRAN IV). Kilise Turing tezi, sadece özyineleme dillerinde mümkün olan her şeyin özyinelemesiz bir dilde yapılabileceğini ve tersi de, her ikisinin de turing-tamlık özelliğine sahip olmasıyla kanıtlanabilir.


-3

İşte yinelemeli bir algoritma:

def howmany(x,y)
  a = {}
  for n in (0..x+y)
    for m in (0..n)
      a[[m,n-m]] = if m==0 or n-m==0 then 1 else a[[m-1,n-m]] + a[[m,n-m-1]] end
    end
  end
  return a[[x,y]]
end
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.