Bir süre döngü özünde bir özyineleme olup olmadığını merak ettim?
Sanırım, bir süre döngüsü sonunda kendisini çağıran bir işlev olarak görülebilir. Özyineleme değilse, fark nedir?
Bir süre döngü özünde bir özyineleme olup olmadığını merak ettim?
Sanırım, bir süre döngüsü sonunda kendisini çağıran bir işlev olarak görülebilir. Özyineleme değilse, fark nedir?
Yanıtlar:
Döngüler çok fazla özyineleme değildir . Aslında, bunlar zıt mekanizmanın ana örneğidir : yineleme .
Yineleme noktası işlem bir eleman olmasıdır aramaları başka bir örneği. Döngü kontrol makineleri sadece atlar , başladığı noktaya geri.
Kodda dolaşmak ve başka bir kod bloğunu çağırmak farklı işlemlerdir. Örneğin, döngünün başlangıcına atladığınızda, döngü kontrol değişkeni hala atlamadan önceki değere sahiptir. Ancak, içinde bulunduğunuz rutinin başka bir örneğini çağırırsanız, yeni örneğin tüm değişkenlerinin yeni, ilgisiz kopyaları vardır. Etkili olarak, bir değişken birinci işlem seviyesinde bir değere ve daha düşük bir seviyede bir değere sahip olabilir.
Bu özellik, birçok özyinelemeli algoritmaların çalışması için çok önemlidir ve bu nedenle, tüm bu değerleri izleyen bir çerçeve dizisini yönetmeden yinelemeyle özyinelemeyi taklit edemezsiniz.
X'in özünde Y olduğunu söylemek, yalnızca X'i ifade ettiğinizi aklınızda bulunduran (biçimsel) bir sisteminiz varsa mantıklı gelir. while
Lambda hesabı açısından anlamını tanımlarsanız, özyinelemeden * bahsedebilirsiniz *; Eğer bunu bir kayıt makinesi olarak tanımlarsanız, muhtemelen kullanmayacaksınız.
Her iki durumda da, sadece bir süre döngüsü içerdiğinden özyinelemeli bir işlev çağırırsanız, insanlar muhtemelen sizi anlamaz.
* Belki de yalnızca dolaylı olarak, örneğin bunu tanımlarsanız fold
.
while
yapı özyinelemeli dillerde genellikle işlevlerin bir özelliği olduğundan, bu bağlamda "özyinelemeli" olarak tanımlayabilecek başka bir şey düşünemiyorum.
Bu senin bakış açına bağlı.
Hesaplanabilirlik teorisine bakarsanız, yineleme ve özyineleme aynı derecede ifade edicidir . Bunun anlamı, bir şeyi hesaplayan bir fonksiyon yazabilmenizdir ve özyinelemeli veya yinelemeli olarak yapıp yapmamanız önemli değildir, her iki yaklaşımı da seçebileceksiniz. Yinelemeli olarak hesaplayamadığınız, yinelemeli olarak hesaplayamadığınız ve tersi şekilde hesaplayabileceğiniz hiçbir şey yoktur (her ne kadar programın dahili çalışmaları farklı olabilir).
Pek çok programlama dili, özyinelemeyi ve yinelemeyi aynı şekilde ve iyi bir sebepten dolayı işlemez. Genellikle , özyineleme, dilin / derleyicinin çağrı yığınını yönettiği ve yinelemenin kendiniz için yığın işleme yapmanız gerekebileceği anlamına gelir.
Ancak, dil vardır , özellikle fonksiyonel diller - - döngüler gibi şeyler (için, süre) olduğu aslında sadece sözdizimsel yineleme için şeker ve sahne arkasında hayata bu şekilde. Bu genellikle işlevsel dillerde istenir, çünkü genellikle döngü kavramı yoktur ve ekleri pratik değil, hesaplamaları daha karmaşık hale getirir.
Yani hayır, onlar aslında aynı değildir . Bunlar eşit derecede ifade edicidir , yani yinelemeli olarak bir şeyi hesaplayamazsınız, özyinelemeli olarak hesaplayamazsınız ve bunun tersi de geçerlidir, ancak bu genel davada (Kilise-Turing tezi uyarınca) bununla ilgilidir.
Burada tekrarlı programlar hakkında konuştuğumuzu unutmayın . Veri yapılarında (örneğin, ağaçlar) başka özyinelemeler de vardır.
Bir uygulama bakış açısıyla bakarsanız, özyineleme ve yineleme aynı değildir. Özyineleme, her çağrı için yeni bir yığın çerçeve oluşturur. Özyinelemenin her adımı kendi kendine yeten, hesaplama için argümandan (kendiliğinden) argümanlar alıyor.
Diğer taraftan döngüler çağrı çerçeveleri oluşturmaz. Onlar için bağlam her adımda korunmaz. Döngü için program, döngü koşulu başarısız olana kadar sadece döngünün başına geri atlar.
Bunu bilmek oldukça önemlidir, çünkü gerçek dünyada oldukça radikal farklılıklar yaratabilir. Özyineleme için, tüm içerik her çağrıya kaydedilmelidir. Yinelemede, hangi değişkenlerin bellekte olduğunu ve nereye kaydedildiğini kesin olarak kontrol edebilirsiniz.
Bu şekilde bakarsanız, çoğu dilde yinelemenin ve özyinelemenin temelde farklı olduğunu ve farklı özelliklere sahip olduğunu hızlıca görürsünüz. Duruma bağlı olarak, özelliklerden bazıları diğerlerinden daha çok arzu edilir.
Özyineleme, programların test edilmesini ve kanıtlanmasını daha basit ve kolay hale getirebilir . Bir özyinelemenin yinelemeye dönüştürülmesi genellikle kodu daha karmaşık hale getirir ve başarısızlık olasılığını artırır. Öte yandan, yinelemeye dönüştürmek ve çağrı yığını çerçevelerinin miktarını azaltmak, çok fazla ihtiyaç duyulan hafızayı kaydedebilir.
Aradaki fark örtük istif ve semantiktir.
"Sonunda kendisini çağıran" bir döngüde, bittiğinde geri taranacak bir yığın yoktur. Son yineleme, biteceği haliyle ne olacağını belirler.
Bununla birlikte, özyineleme, daha önce yapılan işin durumunu hatırlayan bu örtülü yığın olmadan yapılamaz.
Açıkça bir yığına erişim izni verirseniz yineleme ile ilgili herhangi bir özyineleme problemini çözebileceğiniz doğrudur. Ancak bu şekilde yapmak aynı değildir.
Anlamsal fark, özyinelemeli koda bakmanın yinelemeli koddan tamamen farklı bir şekilde bir fikri ilettiği gerçeğiyle ilgilidir. Yinelemeli kod işleri bir anda bir adım yapar. Önceden gelen herhangi bir durumu kabul eder ve yalnızca bir sonraki durumu oluşturmak için çalışır.
Özyinelemeli kod bir sorunu fraktallara böler. Bu küçük parça o büyük parçaya benziyor, böylece sadece bu kısmını ve bu kısmını da aynı şekilde yapabiliriz. Sorunları düşünmenin farklı bir yolu. Çok güçlü ve alışmak gerekiyor. Birkaç satırda çok şey söylenebilir. Bir yığına erişimi olsa bile, bunu bir süre döngüsünden çıkaramazsınız.
Hepsi, terimi kendi kullanımınıza bağlıyor . Programlama dili düzeyinde, sözdizimsel ve anlamsal olarak farklılar ve oldukça farklı performans ve bellek kullanımlarına sahipler. Ancak teoriye yeterince derin bir şekilde kazıyorsanız, birbirleriyle tanımlanabilirler ve bu nedenle bazı teorik anlamda "aynıdır".
Asıl soru şudur: İterasyon (döngüler) ile özyineleme arasında ayrım yapmak ne zaman anlamlıdır ve ne zaman aynı şeyleri düşünmek yararlı olur? Bunun cevabı aslında programlamada (matematiksel ispatlar yazarken) yinelemeyle yineleme arasında ayrım yapmanın önemli olduğudur.
Özyineleme, yeni bir yığın çerçeve, yani her çağrı için yeni bir yerel değişken kümesi oluşturur. Bunun ek yükü vardır ve yığın üzerinde yer kaplar, bu da yeterince derin bir özyinelemenin programın çökmesine neden olan yığının üzerine taşabileceği anlamına gelir. Diğer yandan, yineleme yalnızca var olan değişkenleri değiştirir, bu nedenle genellikle daha hızlıdır ve yalnızca sabit miktarda bellek alır. Yani bu bir geliştirici için çok önemli bir ayrımdır!
Kuyruk çağırma özyinelemeli dillerde (tipik olarak işlevsel diller), derleyici özyinelemeli çağrıları yalnızca sabit miktarda bellek alabilecek şekilde optimize edebilir. Bu dillerde, önemli ayrım yineleme ve özyineleme değil, kuyruk çağrısı-özyineleme sürümü kuyruk çağrısı-özyineleme ve yinelemedir.
Alt satır: Farkı söyleyebilmeniz gerekir, aksi takdirde programınız çökecektir.
while
döngüler bir özyineleme şeklidir, bakınız örneğin bu soruya verilen cevaplar . Hesaplanabilirlik teorisindeki operator-operatörüne karşılık gelirler (bakınız, örneğin buraya ).
for
Bir dizi sayı üzerinde yinelenen tüm döngü çeşitleri, sonlu bir koleksiyon, bir dizi ve benzeri, ilkel özyinelemeye karşılık gelir, bakınız örneğin burada ve burada . O Not for
C, C döngüler ++, Java ve benzeri, aslında bir sözdizimsel şeker while
döngü ve bu nedenle tekabül ilkel özyinelemeye değildir. Pascal for
döngüsü, ilkel özyinelemenin bir örneğidir.
Önemli bir fark, ilkel özyinelemenin daima sona ermesidir; oysa genel özyinelemenin ( while
döngüler) sona ermeyebilir.
DÜZENLE
Yorumlar ve diğer cevaplarla ilgili bazı açıklamalar. “Bir şey kendisi veya türüyle tanımlandığında özyineleme oluşur.” (bkz. wikipedia ). Yani,
Bir süre döngüsü özünde bir özyineleme midir?
Çünkü bir while
döngüyü kendisi açısından tanımlayabilirsiniz .
while p do c := if p then (c; while p do c))
o zaman, evet , bir while
döngü özyinelemenin bir şeklidir. Özyinelemeli işlevler, özyinelemenin bir başka biçimidir (özyinelemeli tanımlamanın başka bir örneği). Listeler ve ağaçlar diğer özyinelemelerdir.
Örtüşen birçok cevap ve yorum tarafından kabul edilen bir başka soru ise;
Döngüler ve özyinelemeli işlevler eşdeğer midir?
Bu sorunun cevabı hayır : Bir while
döngü, özyinelemeli özyinelemeye karşılık gelir, ancak döngü tarafından erişilen değişkenler örtük özyinelemeli fonksiyonun argümanlarına karşılık gelir; while
Fazladan bir yığın kullanmadan bir döngü ile modellenemez .
Bu nedenle, "bir while
döngü özyinelemenin bir şeklidir" gerçeği "bazı özyinelemeli işlevlerin bir while
döngü tarafından ifade edilemeyeceği" gerçeğiyle çelişmez .
FOR
döngüye sahip bir dil, tüm ilkel özyinelemeli fonksiyonları tam olarak hesaplayabilir ve sadece bir WHILE
döngüye sahip bir dil tam olarak tüm-özyinelemeli fonksiyonları hesaplayabilir (ve--özyinelemeli fonksiyonların tam olarak bu fonksiyonlar olduğu ortaya çıkar) bir Turing Makinesi hesaplayabilir). Veya kısaltmak için: ilkel özyineleme ve µ özyineleme matematik / hesaplanabilirlik teorisinin teknik terimleridir.
Bir kuyruk çağrısı (veya özyinelemeli özyinelemeli çağrı) tam olarak bir "argümanlarla gitme " ( çağrı yığına herhangi bir ek çağrı çerçevesine basmadan) olarak uygulanır ve bazı işlevsel dillerde (özellikle Ocaml) genel döngü yöntemidir.
Bu yüzden bir süre döngüsü (onlara sahip olan dillerde) bir kuyruk çağrısı ile bitmiş olarak görülebilir (veya kafa testi).
Aynı şekilde, sıradan (kuyruk çağrısı) özyinelemeli çağrılar döngülerle simüle edilebilir (bir miktar yığın kullanarak).
Devamlılıklar ve devam eden stil hakkında da okuyun .
Yani "özyineleme" ve "yineleme" derinden eşdeğerdir.
Hem yinelemenin hem de sınırsız while-döngülerinin hesaplamalı ifade edilebilirlik açısından eşdeğer olduğu doğrudur. Yani, yinelenerek yazılmış herhangi bir program, bunun yerine döngüler kullanılarak eşdeğer bir programa yeniden yazılabilir ve bunun tersi de geçerlidir. Her iki yaklaşım turing-complete , yani herhangi bir hesaplanabilir fonksiyonu hesaplamak için kullanılabilir.
Programlama açısından temel fark, özyinelemenin çağrı yığında depolanan verileri kullanmanıza izin vermesidir. Bunu göstermek için, bir döngü veya özyineleme kullanarak tek bir bağlantılı listenin öğelerini yazdırmak istediğinizi varsayın. Örnek kod için C kullanacağım:
typedef struct List List;
struct List
{
List* next;
int element;
};
void print_list_loop(List* l)
{
List* it = l;
while(it != NULL)
{
printf("Element: %d\n", it->element);
it = it->next;
}
}
void print_list_rec(List* l)
{
if(l == NULL) return;
printf("Element: %d\n", l->element);
print_list_rec(l->next);
}
Basit değil mi? Şimdi küçük bir değişiklik yapalım: Listeyi ters sırada yazdırın.
Özyinelemeli değişken için bu, orijinal işlev için neredeyse önemsiz bir değişikliktir:
void print_list_reverse_rec(List* l)
{
if (l == NULL) return;
print_list_reverse_rec(l->next);
printf("Element: %d\n", l->element);
}
Yine de döngü işlevi için bir sorunumuz var. Listemiz tek tek bağlantılıdır ve bu nedenle yalnızca ileriye doğru kaydırılabilir. Ancak geriye doğru yazdırdığımız için son elemanı yazdırmaya başlamalıyız. Son elemente ulaştıktan sonra, artık ikinci-son elemente geri dönemeyiz.
Bu yüzden ya bir sürü yeniden gezinme yapmak zorundayız ya da ziyaret edilen unsurları izleyen ve daha sonra verimli bir şekilde yazdırabileceğimiz bir yardımcı veri yapısı oluşturmalıyız.
Neden bu özyineleme ile ilgili bir problemimiz yok? Çünkü özyinelemede zaten yardımcı bir veri yapımız var: İşlev çağrısı yığını.
Özyinelemeli özyinelemeli çağrının önceki çağrısına geri dönmemize izin verdiğinden, tüm yerel değişkenler ve bu çağrının durumu hala bozulmamışken, yinelemeli durumda modellemek için can sıkıcı bir esneklik kazanıyoruz.
Döngüler, belirli bir görevi başarmak için özel bir özyineleme biçimidir (çoğunlukla yineleme). Biri birçok dilde aynı performansla [1] tekrarlanan stilde bir döngü uygulayabilir. ve SICP'de [2], döngülerin "sentastik şeker" olarak tanımlandığını görebilirsiniz. Zorunlu programlama dillerinin çoğunda, bloklar için ve blokları ana fonksiyonlarıyla aynı kapsamı kullanıyor. Bununla birlikte, işlevsel programlama dillerinin çoğunda ne döngüler vardır ne de döngü yoktur çünkü bunlara ihtiyaç yoktur.
Zorunlu dillerin döngüler için / süre olmasının nedeni, durumları mutasyona uğratarak idare etmeleridir. Ama aslında, eğer farklı bir perspektiften bakarsanız, bir süre bloğunu bir fonksiyonun kendisi olarak düşünürseniz, parametre alarak, onu işleme koyar ve yeni bir durum döndürürseniz - bu aynı fonksiyonun farklı parametrelerle çağrılması olabilir - siz Döngüyü özyineleme olarak düşünebilir.
Dünya aynı zamanda değişken veya değişken olarak tanımlanabilir. dünyayı bir kurallar kümesi olarak tanımlarsak, tüm kuralları ve mevcut durumu parametre olarak alan nihai bir işlev çağırırsak ve yeni durumu aynı işlevselliğe sahip bu parametrelere göre döndürürsek (aynı durumda bir sonraki durumu oluşturur) yol), bunun bir özyineleme ve bir döngü olduğunu söyleyebiliriz.
Aşağıdaki örnekte, hayat, fonksiyonun "kurallar" ve "durum" olmak üzere iki parametre almasıdır ve bir dahaki sefere yeni durum oluşturulacaktır.
life rules state = life rules new_state
where new_state = construct_state_in_time rules state
[1]: kuyruk çağrısı optimizasyonu, işlevsel programlama dillerinde mevcut fonksiyon yığınını yeni bir tanesini oluşturmak yerine özyinelemeli çağrılarda kullanmak için ortak bir optimizasyondur.
[2]: Bilgisayar Programlarının Yapısı ve Yorumlanması, MIT. https://mitpress.mit.edu/books/structure-and-interpretation-computer-programs
Bir süre döngü özyinelemeden farklı.
Bir işlev çağrıldığında, aşağıdakiler gerçekleşir:
Bir yığın çerçeve yığına eklenir.
Kod işaretçisi, fonksiyonun başlangıcına hareket eder.
Bir süre döngüsü sonunda olduğunda aşağıdakiler gerçekleşir:
Bir durum bir şeyin doğru olup olmadığını sorar.
Eğer öyleyse, kod bir noktaya atlar.
Genel olarak while döngüsü aşağıdaki sözde koda benzer:
if (x)
{
Jump_to(y);
}
Hepsinden önemlisi, özyineleme ve döngüler farklı montaj kodu gösterimlerine ve makine kodu gösterimlerine sahiptir. Bu, aynı olmadığı anlamına gelir. Aynı sonuçlara sahip olabilirler, ancak farklı makine kodu aynı şeyin% 100 olmadığını kanıtlıyor.
Sadece yineleme, özyinelemeye genellikle eşdeğer olmak için yeterli değildir, ancak yığınla yineleme genellikle aynıdır. Herhangi bir özyinelemeli işlev, bir yığınla yinelemeli bir döngü olarak yeniden programlanabilir ve bunun tersi de geçerlidir. Bu, bunun pratik olduğu anlamına gelmez ve herhangi bir durumda, bir veya diğer formun, diğer versiyona göre belirgin faydaları olabileceği anlamına gelmez.
Bunun neden tartışmalı olduğundan emin değilim. Bir yığınla özyineleme ve yineleme aynı işlemsel süreçtir. Tabii ki, aynı "fenomen" dir.
Aklıma gelen tek şey, bunlara "programlama araçları" olarak bakarken, onları aynı şey olarak düşünmemeniz gerektiği konusunda hemfikir olacağım. Bunlar "matematiksel" veya "hesaplamalı" eşdeğerdir (yine bir yığınla yineleme, genel olarak yineleme değil), ancak bu, ikisinin de yapacağı düşüncesiyle sorunlara yaklaşmanız gerektiği anlamına gelmez. Bir uygulama / problem çözme bakış açısına göre, bazı problemler bir şekilde veya diğerinden daha iyi çalışabilir ve bir programcı olarak sizin hangisinin daha uygun olduğuna karar vermektir.
Açıklığa kavuşturmak için, sorunun cevabını bir süre döngü özünde bir özyineleme midir? kesin bir hayır veya en azından "yığınınız yoksa."