Özyineleme başlı başına bir özellik mi?


116

... yoksa sadece bir uygulama mı?

Bunu profesörümle yaptığım bir tartışma nedeniyle soruyorum: Sınıfta özyinelemeyi kapsamadığımız için bir işlevi yinelemeli olarak çağırma kredimi kaybettim ve benim argümanım bunu örtük olarak öğrenme returnve yöntemlerle öğrendiğimizdir .

Burada soruyorum çünkü birinin kesin bir cevabı olduğundan şüpheleniyorum.

Örneğin, aşağıdaki iki yöntem arasındaki fark nedir:

public static void a() {
    return a();
    }

public static void b() {
    return a();
    }

" aSonsuza kadar devam eder" dışında (gerçek programda, geçersiz girdi sağlandığında kullanıcıyı tekrar uyarmak için doğru şekilde kullanılır), ave arasında temel bir fark var bmı? Optimize edilmemiş bir derleyici için, bunlar nasıl farklı şekilde ele alınır?

Sonuçta o öğrenerek olsun aşağı gelir return a()dan bbiz de öğrendik bunun o return a()dan a. Yaptık mı?


24
Mükemmel tartışma. Profesörünüze bunu böyle açıkladınız mı merak ediyorum. Yaptıysanız, kayıp kredinizi size vermesi gerektiğini düşünüyorum.
Michael Yaworski

57
Özyineleme, bilgisayar bilimine özel bir kavram bile değildir. Fibonacci işlevi, faktör operatörü ve matematikten (ve muhtemelen diğer alanlardan) daha birçok şey yinelemeli olarak ifade edilir (veya en azından yapılabilir). Profesör bu şeylerden de habersiz olmanızı istiyor mu?
Theodoros Chatzigiannakis

34
Profesör, bunun yerine, bir sorunu çözmek için zarif bir yol bulduğu ya da kutunun dışında düşündüğü için ona fazladan kredi vermelidir.

11
Görev neydi? Bu, bir programlama ödevi gönderdiğinizde, neyin işaretlendiğini, bir problemi çözme yeteneğiniz veya öğrendiklerinizi kullanma beceriniz, sık sık merak ettiğim bir sorundur. Bu ikisi mutlaka aynı olmak zorunda değil.
Leon

35
FWIW, doğru olana kadar girdiyi sormak özyinelemeyi kullanmak için iyi bir yer değildir, yığından taşmak çok kolaydır. Bu özel örnek için, gibi bir şey kullanmak daha iyi olacaktır a() { do { good = prompt(); } while (!good); }.
Kevin

Yanıtlar:


113

Özel sorunuzu cevaplamak için: Hayır, bir dil öğrenme açısından, özyineleme bir özellik değildir. Profesörünüz, henüz öğretmediği bir "özelliği" kullanmak için gerçekten not aldıysa, bu yanlıştı.

Satır aralarını okumak, bir olasılık, özyinelemeyi kullanarak, onun kursu için bir öğrenme sonucu olması gereken bir özelliği kullanmaktan kaçınmanızdır. Örneğin, belki hiç yineleme kullanmadınız veya belki forde forve kullanmak yerine yalnızca döngüler kullandınız while. Bir ödevin belirli şeyleri yapma yeteneğinizi test etmeyi amaçlaması yaygındır ve bunları yapmaktan kaçınırsanız, profesörünüz size bu özellik için ayrılan notları veremez. Bununla birlikte, eğer gerçekten kayıp notlarınızın nedeni buysa, profesör bunu kendi öğrenme deneyimi olarak almalıdır - eğer belirli öğrenme çıktılarını göstermek bir ödev için kriterlerden biriyse, öğrencilere açıkça anlatılmalıdır. .

Bunu söyledikten sonra, yinelemenin burada yinelemeden daha iyi bir seçim olduğu konusundaki diğer yorumların ve yanıtların çoğuna katılıyorum. Bunun birkaç nedeni var ve diğer insanlar onlara bir dereceye kadar değindiyse de, arkasındaki düşünceyi tam olarak açıkladıklarından emin değilim.

Yığın Taşmaları

Daha bariz olanı, yığın taşması hatası alma riskinizdir. Gerçekçi olarak, yazdığınız yöntemin gerçekten bir yönteme yol açması pek olası değildir, çünkü bir kullanıcının bir yığın taşmasını tetiklemek için birçok kez yanlış girdi vermesi gerekir.

Bununla birlikte, akılda tutulması gereken bir şey, yalnızca yöntemin kendisi değil, çağrı zincirinde daha yüksek veya daha düşük diğer yöntemlerin de yığında olacağıdır. Bu nedenle, mevcut yığın alanını gelişigüzel bir şekilde yutmak, herhangi bir yöntem için oldukça kaba bir şeydir. Diğer kodun gereksiz yere çoğunu kullanmış olma riski nedeniyle, hiç kimse kod yazarken sürekli olarak boş yığın alanı konusunda endişelenmek istemez.

Bu, yazılım tasarımında soyutlama adı verilen daha genel bir ilkenin parçasıdır. Esasen, aradığınızda DoThing(), önemsemeniz gereken tek şey İşin bitmiş olmasıdır. Nasıl yapıldığına dair uygulama detayları hakkında endişelenmenize gerek yok . Ancak yığının açgözlü kullanımı bu prensibi bozar, çünkü her kod parçasının, çağrı zincirinin başka bir yerindeki kodla kendisine ne kadar yığın bıraktığını güvenle varsayabileceği konusunda endişelenmesi gerekir.

Okunabilirlik

Diğer sebep okunabilirliktir. Kodun amaçlaması gereken ideal, her satırın basitçe ne yaptığını açıkladığı, insan tarafından okunabilir bir belge olmaktır. Bu iki yaklaşımı ele alın:

private int getInput() {
    int input;
    do {
        input = promptForInput();
    } while (!inputIsValid(input))
    return input;
}

karşı

private int getInput() {
    int input = promptForInput();
    if(inputIsValid(input)) {
        return input;
    }
    return getInput();
}

Evet, ikisi de işe yarıyor ve evet ikisi de anlaşılması oldukça kolay. Ancak bu iki yaklaşım İngilizce olarak nasıl tanımlanabilir? Sanırım şöyle bir şey olurdu:

Giriş geçerli olana kadar giriş isteyeceğim ve sonra geri göndereceğim

karşı

Giriş için bilgi isteyeceğim, sonra giriş geçerliyse geri vereceğim, aksi takdirde girişi alıp bunun yerine sonucunu döndürürüm

Belki ikincisi için biraz daha az karmaşık ifadeler düşünebilirsiniz, ancak bence her zaman ilkinin, aslında yapmaya çalıştığınız şeyin kavramsal olarak daha doğru bir açıklaması olacağını göreceksiniz. Bu, özyinelemenin her zaman daha az okunabilir olduğu anlamına gelmez . Ağaç geçişi gibi parladığı durumlar için, özyineleme ve başka bir yaklaşım arasında aynı türden yan yana analizi yapabilirsiniz ve neredeyse kesinlikle özyinelemenin, kendi kendini tanımlayan, satır satır daha net bir kod veren kod verdiğini göreceksiniz.

Tek başına, bunların ikisi de küçük noktalardır. Bunun gerçekten bir yığın taşmasına yol açması pek olası değildir ve okunabilirlikteki kazanç küçüktür. Ancak herhangi bir program, bu küçük kararların birçoğunun bir derlemesi olacaktır, bu nedenle, tek başlarına çok önemli olmasalar bile, bunları doğru yapmanın arkasındaki ilkeleri öğrenmek önemlidir.


8
Özyinelemenin bir özellik olmadığı yönündeki iddianızı genişletebilir misiniz? Cevabımda öyle olduğunu savundum, çünkü tüm derleyiciler onu desteklemiyor.
Harry Johnston

5
Tüm diller de özyinelemeyi desteklemeyebilir, bu nedenle bu sadece doğru derleyiciyi seçme meselesi değildir - ancak "özellik" in doğası gereği belirsiz bir açıklama olduğunu ve yeterince adil olduğunu söylemekte haklısınız. İkinci fikriniz, ilk önce makine kodu programlaması konusunda herhangi bir geçmişe sahip olmadan (şimdi her zaman olduğu gibi) programlamayı öğrenen birinin bakış açısından da adil. :-)
Harry Johnston

2
'Okunabilirlik' sorununun sözdizimi ile ilgili bir sorun olduğunu unutmayın. Özyineleme hakkında doğası gereği "okunamaz" hiçbir şey yoktur. Aslında tümevarım, döngüler, listeler ve diziler gibi tümevarımlı veri yapılarını ifade etmenin en kolay yoludur. Ve çoğu veri yapısı endüktiftir.
nomen

6
Sanırım ifadelerinizle desteyi üst üste koydunuz. Yinelemeli sürümü işlevsel olarak tanımladınız ve tersi. Sanırım her ikisinin de daha adil bir satır-satır açıklaması “Giriş isteyeceğim. Giriş geçerli değilse, geçerli giriş alana kadar uyarıyı tekrarlamaya devam edeceğim. Sonra onu iade edeceğim. " vs “Giriş isteyeceğim. Giriş geçerliyse, onu iade edeceğim. Aksi takdirde, tekrarlamanın sonucunu geri getireceğim. " (Çocuklarım, okul öncesi dönemlerinde bir tekrarlamanın işlevsel kavramını anladılar, bu yüzden buradaki yinelemeli kavramın meşru bir İngilizce özeti olduğunu düşünüyorum.)
pjs

2
@HarryJohnston Özyineleme desteğinin olmaması, yeni bir özellik eksikliğinden ziyade mevcut özellikler için bir istisna olacaktır . Özellikle, bu soru bağlamında, "yeni bir özellik", "henüz var olduğunu öğretmediğimiz yararlı davranış" anlamına gelir; bu , öğretilen özelliklerin mantıksal bir uzantısı olduğu için özyineleme için doğru değildir (yani, prosedürler şunları içerir: talimatlar ve prosedür çağrıları talimatlardır). Sanki profesör bir öğrenci toplamayı öğretti ve sonra aynı değeri birden fazla kez kattığı için onu azarladı çünkü "çarpmayı ele almadık".
nmclean

48

Meta sorudan ziyade gerçek soruyu yanıtlamak için: özyineleme , tüm derleyicilerin ve / veya dillerin buna zorunlu olarak izin vermediği anlamda bir özelliktir. Pratikte, tüm (sıradan) modern derleyicilerden ve kesinlikle tüm Java derleyicilerinden beklenir! - ama evrensel olarak doğru değil .

Özyinelemenin neden desteklenmediğine dair uydurma bir örnek olarak, bir işlevin dönüş adresini statik bir konumda saklayan bir derleyiciyi düşünün; bu, örneğin yığını olmayan bir mikroişlemci için bir derleyici için geçerli olabilir.

Böyle bir derleyici için, böyle bir işlevi çağırdığınızda

a();

olarak uygulanır

move the address of label 1 to variable return_from_a
jump to label function_a
label 1

ve a () 'nın tanımı,

function a()
{
   var1 = 5;
   return;
}

olarak uygulanır

label function_a
move 5 to variable var1
jump to the address stored in variable return_from_a

Umarım a()böyle bir derleyicide özyinelemeli olarak çağırmaya çalıştığınızda problem açıktır; geri dönüş adresinin üzerine yazıldığı için derleyici artık dış çağrıdan nasıl dönüleceğini bilmiyor.

Aslında kullandığım derleyici için (70'lerin sonu veya 80'lerin başında) özyineleme desteği olmadan sorun bundan biraz daha incelikliydi: dönüş adresi tıpkı modern derleyicilerde olduğu gibi yığında saklanacaktı, ancak yerel değişkenler 't. (Teorik olarak bu, statik olmayan yerel değişkenleri olmayan işlevler için özyinelemenin mümkün olduğu anlamına gelmelidir, ancak derleyicinin bunu açıkça destekleyip desteklemediğini hatırlamıyorum. Bazı nedenlerden dolayı örtük yerel değişkenlere ihtiyaç duymuş olabilir.)

İleriye baktığımda, her iş parçacığı için bir yığın sağlamak zorunda olmamanın avantajlı olabileceği ve bu nedenle özyinelemeye yalnızca derleyici onu bir döngü halinde yeniden düzenleyebiliyorsa izin verilen özel senaryolar - muhtemelen büyük ölçüde paralel sistemler - hayal edebiliyorum. (Elbette yukarıda tartıştığım ilkel derleyiciler, kodu yeniden düzenleme gibi karmaşık görevleri yerine getiremiyordu.)


Örneğin, C ön işlemcisi makrolarda özyinelemeyi desteklemez. Makro tanımları işlevlere benzer şekilde davranır, ancak bunları özyinelemeli olarak çağıramazsınız.
STH

7
Sizin "uydurma örneğiniz" o kadar da uydurulmuş değil: Fortran 77 standardı, işlevlerin kendilerini yinelemeli olarak adlandırmalarına izin vermedi - bunun nedeni, hemen hemen sizin tarif ettiğiniz şeydi. (Fonksiyon yapıldığında atlanacak adresin fonksiyon kodunun sonunda veya bu düzenlemeye eşdeğer bir yerde saklandığına inanıyorum.) Bununla ilgili biraz bilgi için buraya bakın .
alexis

5
Gölgelendirici dilleri veya GPGPU dilleri (örneğin, GLSL, Cg, OpenCL C) örnek olarak özyinelemeyi desteklemez. Dolayısıyla, "tüm diller bunu desteklemiyor" argümanı kesinlikle geçerlidir. Yineleme bir yığın eşdeğer (mutlaka bir vermeye ihtiyaç varsayar yığını , ama mağaza dönüş adresleri ve fonksiyon çerçeveleri için bir araç için ihtiyaç vardır şekilde ).
Damon

1970'lerin başında biraz üzerinde çalıştığım bir Fortran derleyicisinin çağrı yığını yoktu. Her alt yordam veya işlev, dönüş adresi, parametreler ve kendi değişkenleri için statik bellek alanlarına sahipti.
Patricia Shanahan

2
Turbo Pascal'ın bazı sürümleri bile özyinelemeyi varsayılan olarak devre dışı bıraktı ve bunu etkinleştirmek için bir derleyici yönergesi belirlemeniz gerekiyordu.
dan04

17

Öğretmen çalışıp çalışmadığınızı bilmek istiyor. Görünüşe göre problemi size öğrettiği şekilde ( iyi yol ; yineleme) çözmediniz ve bu nedenle, çözmediğini düşünüyorsunuz. Ben yaratıcı çözümlerden yanayım ama bu durumda farklı bir nedenle öğretmeninizle aynı fikirde olmak zorundayım:
Kullanıcı çok fazla kez geçersiz girdi sağlarsa (yani, enter tuşuna basılı tutarak), bir yığın taşması istisnanız olacak ve çözüm çökecek. Ek olarak, yinelemeli çözüm daha verimli ve bakımı daha kolaydır. Bence öğretmenin sana vermiş olması gereken sebep bu.


2
Bize bu görevi belirli bir şekilde yapmamız söylenmedi; ve sadece yinelemeyi değil, yöntemleri de öğrendik. Ayrıca, kişisel tercihlere göre hangisinin daha kolay okunacağını bırakırım: Bana iyi görüneni seçtim. SO hatası benim için yenidir, ancak özyinelemenin kendi başına bir özellik olduğu fikri hala temellenmiş görünmemektedir.

3
"Hangisinin daha kolay okunacağını kişisel tercihlere göre bırakırım". Kabul. Özyineleme bir Java özelliği değildir. Bunlar .
mike

2
@Vality: Kuyruk araması eleme? Bazı JVM'ler bunu yapabilir, ancak istisnalar için bir yığın izleme tutması gerektiğini de unutmayın. Kuyruk çağrısının ortadan kaldırılmasına izin verirse, saf bir şekilde oluşturulan yığın izleme geçersiz hale gelebilir, bu nedenle bazı JVM'ler bu nedenle TCE yapmaz.
icktoofay

5
Her iki durumda da, bozuk kodunuzun daha az kırılması için optimizasyona güvenmek oldukça kötü bir biçimdir.
cHao

7
+1, Ubuntu'da son zamanlarda kullanıcı Enter düğmesine sürekli bastığında giriş ekranının kırıldığını görün, XBox
Sebastian

13

Puanları düşürmek, çünkü "sınıfta özyinelemeyi ele almadık" korkunç. Arayan kişiye geri dönen A'ya geri dönen C işlevini çağıran B işlevini çağıran işlev A işlevini nasıl çağıracağınızı öğrendiyseniz ve öğretmen size bunların farklı işlevler olması gerektiğini açıkça söylemediyse eski FORTRAN sürümlerinde durum böyle olacaktır, örneğin), A, B ve C'nin hepsinin aynı işlev olmaması için hiçbir neden yoktur.

Öte yandan, sizin özel durumunuzda özyinelemenin gerçekten yapılacak doğru şey olup olmadığına karar vermek için gerçek kodu görmemiz gerekir. Çok fazla detay yok ama kulağa yanlış geliyor.


10

Sorduğunuz belirli soruyla ilgili bakmanız gereken birçok bakış açısı var, ancak söyleyebileceğim şey, bir dil öğrenme açısından özyinelemenin kendi başına bir özellik olmadığıdır. Eğer profesörünüz, henüz öğretmediği bir "özelliği" kullanmak için sizi gerçekten notlandırdıysa, bu yanlıştı ama dediğim gibi, burada dikkate alınması gereken başka bakış açıları da var, bu da profesörü puanlar çıkarırken haklı kılıyor.

Sorunuzdan çıkarabildiğim kadarıyla, her özyinelemeli işlevin çağrısı yığına itildiğinden, girdi hatası durumunda girdi istemek için özyinelemeli bir işlev kullanmak iyi bir uygulama değildir. Bu özyineleme, kullanıcı girdisi tarafından yönlendirildiğinden, sonsuz özyinelemeli bir işleve sahip olmak ve dolayısıyla bir StackOverflow ile sonuçlanmak mümkündür.

Sorunuzda bahsettiğiniz bu 2 örnek arasında ne yaptıkları açısından bir fark yoktur (ancak başka şekillerde farklılık gösterir) - Her iki durumda da, bir dönüş adresi ve tüm yöntem bilgileri yığına yüklenir. Bir özyineleme durumunda, dönüş adresi, yöntem çağırmadan hemen sonraki satırdır (elbette tam olarak kodun kendisinde değil, derleyicinin oluşturduğu kodda gördüğünüz tam olarak budur). Java, C ve Python'da, özyineleme, yinelemeye (genel olarak) kıyasla oldukça pahalıdır, çünkü yeni bir yığın çerçevesinin tahsis edilmesini gerektirir. Girdi çok fazla kez geçerli değilse bir yığın taşması istisnası alabileceğinizi de belirtmiyorum.

Profesörün, özyineleme kendi başına bir konu olarak kabul edildiğinden ve programlama deneyimi olmayan birinin özyinelemeyi düşünmesi olası olmadığından, puanları çıkardığına inanıyorum. (Elbette olmayacakları anlamına gelmez, ancak olası değildir).

IMHO, sanırım profesör size puanları düşerek haklı. Doğrulama bölümünü kolaylıkla farklı bir yönteme alıp şu şekilde kullanabilirsiniz:

public bool foo() 
{
  validInput = GetInput();
  while(!validInput)
  {
    MessageBox.Show("Wrong Input, please try again!");
    validInput = GetInput();
  }
  return hasWon(x, y, piece);
}

Eğer yaptığınız şey gerçekten bu şekilde çözülebilirse, o zaman yaptığınız şey kötü bir uygulamadır ve bundan kaçınılmalıdır.


Yöntemin kendisinin amacı girdiyi doğrulamak, ardından başka bir yöntemin sonucunu çağırmak ve döndürmektir (bu yüzden kendisini döndürür). Spesifik olmak gerekirse, Tic-Tac-Toe oyununda hareketin geçerli hasWon(x, y, piece)olup olmadığını kontrol eder , ardından geri döner (yalnızca etkilenen satır ve sütunu kontrol etmek için).

YALNIZCA doğrulama bölümünü kolayca alıp örneğin "GetInput" adlı başka bir yönteme koyabilir ve ardından cevabımda yazdığım gibi kullanabilirsiniz. Cevabımı nasıl görünmesi gerektiği ile düzenledim. Elbette, GetInput'un ihtiyacınız olan bilgileri içeren bir tür döndürmesini sağlayabilirsiniz.
Yonatan Nir

1
Yonatan Nir: Özyineleme ne zaman kötü bir pratikti? Belki JVM, bayt kodu güvenliği nedeniyle Hotspot VM'nin optimize edemediği için patlayacaktır ve bu şeyler güzel bir argüman olacaktır. Kodunuz, farklı bir yaklaşım kullanması dışında nasıl farklıdır?

1
Özyineleme her zaman kötü bir uygulama değildir, ancak önlenebilir ve kodu temiz ve bakımı kolay tutabilirse, bundan kaçınılmalıdır. Java, C ve Python'da, özyineleme, yinelemeye (genel olarak) kıyasla oldukça pahalıdır, çünkü yeni bir yığın çerçevesinin tahsis edilmesini gerektirir. Bazı C derleyicilerinde, bu ek yükü ortadan kaldırmak için bir derleyici bayrağı kullanılabilir; bu, belirli özyineleme türlerini (aslında, belirli tür kuyruk çağrılarını) işlev çağrıları yerine atlamalara dönüştürür.
Yonatan Nir

1
Açık değil, ancak bir döngüyü sonsuz sayıda yineleme ile özyineleme ile değiştirdiyseniz, bu kötüdür. Java, kuyruk arama optimizasyonunu garanti etmez, bu nedenle yığın alanınız kolayca tükenebilir. Java'da, sınırlı miktarda yinelemeniz (genellikle toplam veri boyutuna kıyasla logaritmik) garanti edilmedikçe özyineleme kullanmayın.
hyde

6

Belki profesörünüz henüz öğretmemiştir, ama görünüşe göre yinelemenin avantajlarını ve dezavantajlarını öğrenmeye hazırsınız.

Özyinelemenin temel avantajı, özyinelemeli algoritmaların genellikle çok daha kolay ve daha hızlı yazılmasıdır.

Özyinelemenin ana dezavantajı, özyinelemeli algoritmaların yığın taşmalarına neden olabilmesidir, çünkü her özyineleme düzeyi, yığına ek bir yığın çerçevesi eklenmesini gerektirir.

Ölçeklendirmenin, programcının birim testlerine göre üretimde çok daha fazla özyineleme düzeyiyle sonuçlanabildiği üretim kodu için, dezavantaj genellikle avantaja ağır basar ve pratik olduğunda yinelemeli koddan genellikle kaçınılır.


1
Potansiyel olarak riskli özyinelemeli algoritmalar, açık bir yığın kullanmak için her zaman önemsiz bir şekilde yeniden yazılabilir - sonuçta çağrı yığını sadece bir yığıntır. Bu durumda, çözümü bir yığın kullanmak için yeniden yazarsanız, saçma görünecektir - özyinelemeli yanıtın pek de iyi olmadığına dair daha fazla kanıt.
Aaronaught

1
Yığın taşması bir sorunsa, .NET 4.0 veya herhangi bir işlevsel programlama dili gibi kuyruk arama optimizasyonunu destekleyen bir dil / çalışma zamanı kullanmalısınız
Sebastian

Tüm özyinelemeler kuyruk çağrıları değildir.
Warren Dew

6

Spesifik soru ile ilgili olarak, özyineleme bir özelliktir, evet deme eğilimindeyim, ancak soruyu yeniden yorumladıktan sonra. Özyinelemeyi mümkün kılan dillerin ve derleyicilerin ortak tasarım seçenekleri vardır ve özyinelemeye hiç izin vermeyen Turing-complete dilleri mevcuttur . Başka bir deyişle, özyineleme, dil / derleyici tasarımındaki belirli seçimlerle etkinleştirilen bir beceridir.

  • Birinci sınıf işlevlerin desteklenmesi , özyinelemeyi çok minimum varsayımlar altında mümkün kılar; bkz Unlambda döngüler yazarken bir örnek için, ya bu geniş Python ifadesi hiçbir kendinden referanslar, döngüler veya atamaları içeren:

    >>> map((lambda x: lambda f: x(lambda g: f(lambda v: g(g)(v))))(
    ...   lambda c: c(c))(lambda R: lambda n: 1 if n < 2 else n * R(n - 1)),
    ...   xrange(10))
    [1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880]
    
  • Geç bağlama kullanan veya ileri bildirimleri tanımlayan diller / derleyiciler özyinelemeyi mümkün kılar. Örneğin, Python aşağıdaki koda izin verirken, bu bir Turing-complete sistemi için bir gereklilik değil, bir tasarım seçimidir (geç bağlama) . Karşılıklı özyinelemeli işlevler genellikle ileriye dönük bildirimler için desteğe bağlıdır.

    factorial = lambda n: 1 if n < 2 else n * factorial(n-1)
    
  • Yinelemeli olarak tanımlanmış türlere izin veren statik olarak yazılmış diller özyinelemenin etkinleştirilmesine katkıda bulunur. Go'da Y Combinator'ın bu uygulamasına bakın . Özyinelemeli olarak tanımlanan türler olmadan, Go'da özyinelemeyi kullanmak yine de mümkün olabilir, ancak Y birleştiricisinin özellikle imkansız olacağına inanıyorum.


1
Bu kafamı patlattı, özellikle Unlambda +1
John Powell

Sabit noktalı birleştiriciler zordur. Fonksiyonel programlamayı öğrenmeye karar verdiğimde, kendimi anlayana kadar Y birleştiricisini incelemeye ve sonra onu başka faydalı fonksiyonlar yazmak için uygulamaya zorladım. Biraz zaman aldı ama buna değdi.
wberry

5

Sorunuzdan çıkarabildiğim kadarıyla, girdi hatası durumunda girdi istemek için özyinelemeli bir işlev kullanmak iyi bir uygulama değildir. Neden?

Çünkü her özyinelemeli fonksiyon çağrısı yığına itilir. Bu özyineleme, kullanıcı girdisi tarafından yönlendirildiğinden, sonsuz özyinelemeli bir işleve sahip olmak ve dolayısıyla bir StackOverflow ile sonuçlanması mümkündür :-p

Bunu yapmak için yinelemeli olmayan bir döngüye sahip olmak, gidilecek yoldur.


Söz konusu yöntemin büyük kısmı ve yöntemin kendisinin amacı, çeşitli kontroller yoluyla girdiyi doğrulamaktır. Giriş doğru olana kadar (talimat verildiği gibi) giriş geçersizse işlem baştan başlar.

4
@fay Ancak giriş çok fazla geçersizse, bir StackOverflowError hatası alırsınız. Özyineleme daha zariftir, ancak benim gözümde, genellikle normal bir döngüden (yığınlardan dolayı) daha fazla bir problemdir.
Michael Yaworski

1
O halde bu ilginç ve güzel bir nokta. Bu hatayı düşünmemiştim. Yine de, while(true)aynı yöntemi çağırarak aynı etki elde edilebilir mi? Eğer öyleyse, bunun özyineleme arasındaki herhangi bir farkı desteklediğini söylemem, olduğu gibi bilmek güzel.

1
@fay while(true)sonsuz bir döngüdür. Bir ifadeniz yoksa, breaklol programınızı çökertmeye çalışmadığınız sürece, içindeki noktayı görmüyorum. Demek istediğim, aynı yöntemi çağırırsanız (bu özyineleme), bazen size bir StackOverflowError verir , ancak bir whileveya fordöngü kullanırsanız, vermez. Sorun basitçe normal bir döngüde mevcut değil. Belki seni yanlış anladım ama cevabım hayır.
Michael Yaworski

4
Dürüst olmak gerekirse bu bana, muhtemelen profesörün not almasının gerçek nedeni gibi görünüyor =) Çok iyi açıklamamış olabilir, ancak onu çok kötü bir tarz olarak kabul edilecek bir şekilde kullandığınızı söylemek geçerli bir şikayettir. daha ciddi kodda tamamen kusurlu.
Komutan Kişniş Salamander

3

Özyineleme , bir programlama kavramı , bir özellik (yineleme gibi) ve bir uygulamadır . Bağlantıdan da görebileceğiniz gibi, konuya adanmış geniş bir araştırma alanı var. Belki de bu noktaları anlamak için konunun derinliklerine inmemize gerek yok.

Özellik olarak yineleme

Açıkça ifade etmek gerekirse, Java bunu örtük olarak destekler, çünkü bir yöntemin (temelde özel bir işlevdir) kendisi ve ait olduğu sınıfı oluşturan diğer yöntemler hakkında "bilgi" sahibi olmasına izin verir. Durumun böyle olmadığı bir dil düşünün: bu yöntemin gövdesini yazabilirsiniz a, ancak onun içine bir çağrı ekleyemezsiniz a. Tek çözüm, aynı sonucu elde etmek için yinelemeyi kullanmak olacaktır. Böyle bir dilde, kendi varlıklarının farkında olan işlevler (belirli bir sözdizimi belirteci kullanarak) ile olmayanlar arasında bir ayrım yapmanız gerekir! Aslında, bütün bir dil grubu bu ayrımı yapmaktadır ( örneğin Lisp ve ML ailelerine bakınız ). İlginç bir şekilde, Perl anonim işlevlere bile izin veriyor (lambdas ) kendilerini özyinelemeli olarak çağırmak için (yine, özel bir sözdizimi ile).

özyineleme yok mu?

Yineleme olasılığını bile desteklemeyen diller için, Sabit nokta birleştirici biçiminde genellikle başka bir çözüm vardır , ancak yine de birinci sınıf nesneler olarak adlandırılan işlevleri desteklemek için dil gerektirir (yani nesneler dilin kendi içinde manipüle edilmiş).

Pratik olarak özyineleme

Bu özelliğin bir dilde mevcut olması, bunun deyimsel olduğu anlamına gelmez. Java 8'de lambda ifadeleri eklenmiştir, bu nedenle programlamaya işlevsel bir yaklaşım benimsemek daha kolay hale gelebilir. Bununla birlikte, pratik hususlar vardır:

  • sözdizimi hala çok özyineleme dostu değil
  • derleyiciler bu uygulamayı algılayıp optimize edemeyebilir

Alt çizgi

Neyse ki (veya daha doğrusu, kullanım kolaylığı için) Java, yöntemlerin varsayılan olarak kendilerinin farkında olmasına izin veriyor ve böylece özyinelemeyi destekliyor, bu nedenle bu gerçekten pratik bir sorun değil, ancak yine de teorik bir sorun olarak kalıyor ve sanırım öğretmeniniz özellikle bu konuyu ele almak istedi. Ayrıca dilin son dönemdeki evriminin ışığında gelecekte önemli bir şeye dönüşebilir.

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.