Özyineleme veya döngü sırasında


123

Özellikle bazı röportajlarda yapılan teknik sorular ve testlerle ilgili bazı röportaj uygulamaları okuyordum ve bir kaç defa tökezledi "Tamam problemi bir süre döngüsüyle çözdün, şimdi yapabilir misin? özyineleme ", veya" Herkes döngü sırasında bunu 100 satırla çözebilir, ancak bunu 5 satır özyinelemeli fonksiyonda yapabilir mi? " vb.

Sorum şu, özyineleme genellikle yapılar için / if / while / dan daha iyi midir?

Dürüst olmak gerekirse, her zaman özyinelemenin tercih edilmeyeceğini düşünmüştüm, çünkü yığıntan çok daha küçük olan yığın belleği ile sınırlıdır, ayrıca çok sayıda işlev / yöntem çağrısı yapmak da performans açısından düşüktür, ancak ben yanlış olmak...



73
Özyinelemeyle ilgili olarak, bu oldukça ilginç görünüyor.
dan_waterworth

4
@ dan_waterworth da, bu yardımcı olacaktır: google.fr/… ama ben her zaman özlüyorum heceleyerek görünüyor: P
Shivan Dragon

@ShivanDragon Öyle düşündüm ^ _ ^ Dün gönderdiğim çok uygun :-)
Neal

2
Özyinelemede çalıştığım gömülü ortamlarda en iyi şekilde kaşlarını çattı ve halka açık şekilde en kötü şekilde saplanmasına neden oldu. Sınırlı yığın yığını alanı onu yasa dışı kılar.
Fred Thomsen

Yanıtlar:


192

Özyinelemenin özünde döngüsellerden daha iyi veya daha kötü değildir - her birinin avantajları ve dezavantajları vardır ve bunlar bile programlama diline (ve uygulamaya) bağlıdır.

Teknik olarak, yinelemeli döngüler tipik bilgisayar sistemlerine donanım düzeyinde daha iyi uyum sağlar: makine kodu düzeyinde, bir döngü yalnızca bir test ve koşullu bir atlamadır, oysa yineleme (natal olarak uygulanan) bir yığın çerçevesini itmeyi, zıplamayı, geri dönmeyi ve geri çekmeyi içerir. Yığından OTOH, birçok tekrarlama durumu (özellikle yinelemeli döngülere trivally denk olanlar), yığın push / pop'dan kaçınılabilecek şekilde yazılabilir; özyinelemeli işlev çağrısı geri dönmeden önce işlev gövdesinde gerçekleşen son şey olduğunda ve bu genellikle bir kuyruk çağrısı optimizasyonu (veya kuyruk özyineleme optimizasyonu ) olarak bilinirse mümkündür . Düzgün bir kuyruk çağrısı için optimize edilmiş özyinelemeli işlev, çoğunlukla makine kodu seviyesindeki yinelemeli bir döngüye eşittir.

Bir başka husus, yinelemeli döngülerin, onları saf (yan etkisiz) dil anlambilimiyle uyumsuz kılan yıkıcı durum güncellemeleri gerektirmesidir. Haskell gibi saf dillerin hiç döngü yapısının olmayışının nedeni budur ve diğer birçok işlevsel programlama dili de ya tamamen yoksundur ya da olabildiğince kaçınırlar.

Ancak, bu soruların görüşmelerde bu kadar fazla görünmesinin nedeni, cevaplamak için, birçok hayati programlama kavramını (değişkenler, fonksiyon çağrıları, kapsam ve tabii ki döngüleri ve özyinelemeyi) çok iyi anlamanız gerekir. zihinsel esnekliği, bir soruna iki farklı açıdan, farklı açılardan yaklaşmanıza ve aynı kavramın farklı tezahürleri arasında hareket etmenize olanak tanıyan tabloya getirmek.

Deneyim ve araştırma, değişkenleri, işaretçileri ve özyinelemeyi anlama yeteneğine sahip insanlar ile anlamayanlar arasında bir çizgi olduğunu göstermektedir. Çerçeveler, API'ler, programlama dilleri ve son durumları da dahil olmak üzere programlamadaki hemen hemen her şey, çalışma ve deneyim yoluyla edinilebilir, ancak bu üç temel kavram için bir sezgi geliştiremiyorsanız, programcı olmaya uygun değilsiniz. Basit bir yinelemeli döngüyü özyinelemeli bir sürüme çevirmek, programcı olmayanları filtrelemenin mümkün olan en hızlı yolu hakkındadır - oldukça deneyimsiz bir programcı bile genellikle 15 dakikada bunu yapabilir ve bu çok dilli bir sorun olabilir, böylece aday seçebilir aptal yerine oturmak yerine kendi seçtikleri bir dil.

Bir röportajda böyle bir soru alırsanız, bu iyi bir işarettir: Potansiyel işveren, bir programlama aracının kullanım kılavuzunu ezberleyen kişileri değil, programlayabilenleri arıyor demektir.


3
Cevabınızı en çok seviyorum, çünkü bu sorunun cevabının olabilecek en önemli konularına değiniyor, ana teknik bölümleri açıklıyor ve aynı zamanda bu konunun programlama alanına nasıl uyduğu hakkında iyi bir yakınlaştırma görüntüsü veriyor.
Shivan Dragon

1
Bir senkronizasyon programında, bir .next () yöntemi içeren bir yineleyici üzerinde yinelemeli çağrılar lehine döngülerin önlendiği bir kalıp da fark ettim. Uzun süre çalışan kodun çok fazla CPU açgözlü olmasını engellediğini düşünüyorum.
Evan Plaice

1
Yinelemeli sürüm aynı zamanda değerlerin itilmesi ve patlatılmasını içerir. Bunu yapmak için kodu el ile yazmanız yeterlidir. Özyinelemeli sürüm, yinelemeli bir algoritmayla durumu yığının üzerine itiyor, genellikle durumu bir yapıya iterek bunu manuel olarak simüle etmeniz gerekiyor. Sadece en önemsiz algoritmalar bu duruma ihtiyaç duymaz ve bu durumlarda derleyici kuyruk özyinelemesini tespit eder ve yinelemeli bir çözüm ekler.
Martin York

1
@tdammers Bahsettiğiniz çalışmayı nerede okuyabileceğimi söyleyebilir misiniz "Deneyim ve araştırma insanlar arasında bir çizgi olduğunu gösterir ..." Bu benim için çok ilginç görünüyor.
Yoo Matsuo,

2
Bahsetmeyi unuttuğunuz bir şey, yinelemeli kod, tek bir yürütme işiyle uğraşırken daha iyi performans gösterme eğilimindedir, ancak özyinelemeli algoritmalar kendilerini birden fazla iş parçacığına uygulanma eğilimindedir.
GordonM

37

Değişir.

  • Bazı problemler özyinelemeli çözümlere çok uygundur, örneğin quicksort
  • Bazı diller özyinelemeyi gerçekten desteklemez; örneğin, erken FORTRAN
  • Bazı diller özyinelemeyi döngü için birincil bir araç olarak kabul eder, örneğin Haskell

Ayrıca, kuyruk özyineleme desteğinin, özyinelemeyi özyinelemeli ve yinelemeli döngülere eşdeğer yaptığını, yani özyinelemenin her zaman yığını boşa harcamasını gerektirmediğine dikkat etmek önemlidir.

Ayrıca, özyinelemeli bir algoritma her zaman açık bir yığın kullanarak yinelemeli olarak uygulanabilir .

Son olarak, beş satırlı bir çözümün her zaman 100 satırlık bir çözümden daha iyi olduğunu unutmayın (aslında eşdeğer oldukları varsayılarak).


5
Güzel cevap (+1). "5 satırlık bir çözüm, muhtemelen her zaman 100 satırlık olandan daha iyidir": özlülük, özyinelemenin tek avantajı olmadığını düşünüyorum. Özyinelemeli bir çağrı kullanmak sizi farklı yinelemelerdeki değerler arasındaki fonksiyonel bağımlılıkları belirgin hale getirmeye zorlar.
Giorgio

4
Daha kısa çözümler daha iyi olma eğilimindedir, ancak aşırı veciz olmak gibi bir şey var.
dan_waterworth

5
@dan_waterworth "100 hattına" ile karşılaştırıldığında olması oldukça zordur aşırı veciz
tatarcık

4
@Giorgio, Gereksiz kodu kaldırarak ya da açık şeyler ima ederek programları daha küçük hale getirebilirsiniz. Birincisine bağlı kaldığınız sürece kaliteyi yükseltirsiniz.
dan_waterworth

1
@jk, bence bu kesin bilgiyi açıklatan başka bir yol. Bir değişkenin ne için kullanıldığına ilişkin bilgiler, açık olduğu adından kaldırılır ve örtülü olan kullanımına itilir.
dan_waterworth

17

Programlama söz konusu olduğunda “daha ​​iyi” tanımı konusunda evrensel olarak kabul görmüş bir şey yoktur, ancak “bakımı / okunması daha kolay” anlamına gelecektir.

Özyineleme, yinelemeli döngü yapılarından daha anlamlı bir güce sahiptir: Bunu bir süre döngüsü bir kuyruk özyinelemeli fonksiyona eşdeğer olduğu için özyinelemeli fonksiyonların kuyruk özyinelemeli olması gerekmediğini söylüyorum. Güçlü yapılar genellikle kötü bir şeydir, çünkü okunması zor olan şeyleri yapmanıza izin verir. Ancak, özyineleme size değişkenliği kullanmadan döngü yazma yeteneği verir ve benim akıl değişkenliğine göre özyinelemeden çok daha güçlüdür.

Dolayısıyla, düşük ifade gücünden yüksek ifade gücüne kadar, döngü yapıları şöyle toplanır:

  • Değişmez veri kullanan kuyruk özyinelemeli fonksiyonlar,
  • Değişmez veri kullanan özyinelemeli fonksiyonlar,
  • Değişken veri kullanan döngüler,
  • Değişken veri kullanan kuyruk özyinelemeli fonksiyonlar,
  • Değişken veri kullanan özyinelemeli fonksiyonlar,

İdeal olarak, yapabileceğiniz en etkileyici yapıları kullanırsınız. Elbette, diliniz kuyruk çağrısı optimizasyonunu desteklemiyorsa, bu da döngüsel yapı seçiminizi etkileyebilir.


1
"bir süre döngüsü bir kuyruk özyinelemeli fonksiyona eşittir ve özyinelemeli fonksiyonların kuyruk özyinelemeli olması gerekmez": +1. Bir süre döngüsü + bir yığın aracılığıyla özyinelemeyi simüle edebilirsiniz.
Giorgio

1
% 100 katılıyorum emin değilim ama bu kesinlikle ilginç bir perspektif bu yüzden +1.
Konrad Rudolph

Harika bir cevap için +1 ve bazı dillerin (veya derleyicilerin) kuyruk çağrısı optimizasyonu yapmadığını belirtmek için.
Shivan Dragon

@ Giorgio, "Bir süre döngü + bir yığın aracılığıyla özyinelemeyi simüle edebilirsiniz", Bu yüzden ifade gücü söyledim. Hesaplamalı olarak, eşit derecede güçlüler.
dan_waterworth

@ dan_waterworth: Aynen ve cevabınızda dediğiniz gibi, tek başına özyineleme bir süre döngüsünden daha etkileyicidir çünkü özyinimi simüle etmek için bir süre döngüsüne bir yığın eklemeniz gerekir.
Giorgio

7

Özyineleme genellikle daha az belirgindir. Daha az belirgin olması bakımı zordur.

Eğer yazarsanız for(i=0;i<ITER_LIMIT;i++){somefunction(i);}ana akışında, sen kendini temize bir döngü yazıyoruz olun. Eğer yazarsan somefunction(ITER_LIMIT);ne olacağını gerçekten belli etmiyorsun. Yalnızca içeriği görmek: bu somefunction(int x)çağrılar somefunction(x-1)aslında yinelemeleri kullanan bir döngü olduğunu söylüyor. Ayrıca, break;yinelemelerin yarısında bir yere kolayca kaçış koşulunu koyamazsınız , ya tamamen geri getirilecek bir koşullu eklemeniz ya da bir istisna atmanız gerekir. (ve istisnalar yine karmaşıklık ekler ...)

Özünde, yineleme ve özyineleme arasında bariz bir seçim ise, sezgisel şeyi yapın. Yineleme işi kolayca yaparsa, 2 satır tasarruf nadiren uzun vadede yaratabileceği baş ağrısına değmez.

Elbette, size 98 satır kazandırıyorsa, bu tamamen farklı bir konu.

Özyinelemenin basitçe mükemmel bir şekilde uyduğu durumlar vardır ve bunlar nadir değildir. Ağaç yapılarının, çoğul bağlantılı ağların, kendi tipini içerebilen yapıların, çok boyutlu tırtıklı dizilerin, esasen basit bir vektör veya sabit bir boyut dizisi olmayan herhangi bir şeyin çaprazlanması. Bilinen, düz bir yolu geçerseniz, yineleyin. Eğer bilinmeyene dalırsanız, tekrarlayın.

Temel olarak, eğer somefunction(x-1)seviye başına bir kereden fazla kendi içinden çağrılacaksa, yinelemeleri unut.

... Yinelemeli işlevleri, özyinelemeyle en iyi şekilde yapılan işler için mümkündür; Nerede kullanırsanız kullanın int, buna benzer bir şey gerekir stack<int>. Bunu bir kez yaptım, pratik amaçlardan çok bir egzersiz olarak yaptım. Böyle bir görevle karşılaştığınızda sizi ifade ettiğiniz gibi şüpheleriniz olmayacağına dair sizi temin ederim.


10
Açık olan ve daha az açık olan şey, kısmen alışık olduğunuza bağlıdır. Programlama dillerinde yineleme daha sık kullanılmıştır, çünkü CPU'nun çalışma biçimine daha yakındır (yani daha az bellek kullanır ve daha hızlı çalışır). Ancak, endüktif olarak düşünmeye alışkınsanız, özyineleme sezgisel olabilir.
Giorgio

5
"Bir döngü anahtar sözcüğü görürlerse, bunun bir döngü olduğunu bilirler. Ancak yinelenen bir anahtar kelime yoktur, yalnızca f (x) 'in içinde f (x-1)' i görerek tanıyabilirler.": Yaptığınız özyinelemeli bir işlevi çağırdığınızda özyinelemeli olduğunu bilmek istemiyorum. Benzer şekilde, bir döngü içeren bir işlev çağırdığınızda, bunun bir döngü içerdiğini bilmek istemezsiniz.
Giorgio

3
@SF: Evet, ancak bunu yalnızca fonksiyonun gövdesine bakarsanız görebilirsiniz. Bir döngü durumunda, döngü tekrarlanırsa, özyineleme durumunda, fonksiyonun kendisini çağırdığını görürsünüz.
Giorgio

5
@SF: Bana biraz dairesel akıl yürütme gibi geliyor: "Sezgilerime göre bir döngü ise, o zaman bir döngü." tekrarlımap bir işlev olarak tanımlanabilir (bkz. örneğin haskell.org/tutorial/functions.html ), bir listeyi geçtiğini ve listenin her üyesine bir işlev uyguladığını sezgisel olarak netleştirse bile.
Giorgio

5
@SF, mapbir anahtar kelime değil, normal bir işlev, ancak bu biraz alakasız. İşlevsel programcılar özyinelemeyi kullandıklarında, genellikle bir dizi eylem yapmak istedikleri için değil, çözülen sorunun bir işlev ve bir argüman listesi olarak ifade edilebildiğindendir. Sorun daha sonra başka bir işleve ve başka bir argüman listesine indirgenebilir. Sonunda, önemsiz bir şekilde çözülebilecek bir probleminiz var.
dan_waterworth

6

Her zamanki gibi, bu genel olarak kabul edilemez çünkü pratikte vakalar arasında eşit olmayan ve bir kullanım durumunda birbiriyle eşit olmayan ek faktörler vardır. İşte bazı baskılar.

  • Kısa, zarif kod genel olarak uzun, karmaşık koddan daha üstündür.
  • Ancak, geliştirici tabanınız yinelemeye aşina değilse ve öğrenemiyorsa / öğrenemiyorsa, son nokta bir şekilde geçersizdir. Olumlu değil, hafif bir olumsuz bile olabilir .
  • Özyineleme verimlilik için kötü olabilir eğer sen pratikte Çok fazla iç içe aramaları gerekir ve siz kuyruk özyinelemeye kullanamaz (ya da çevre kuyruk özyinelemeye optimize olamaz).
  • Ara sonuçları doğru şekilde önbelleğe alamıyorsanız, özyineleme de çoğu zaman kötüdür. Örneğin, Fibonacci sayılarını hesaplamak için ağaç özyinelemesini kullanmanın genel örneği, önbellek yapmamanız durumunda çok kötü performans gösterir . Önbellek yaparsanız, basit, hızlı, zarif ve tamamen harika.
  • Özyineleme bazı durumlarda geçerli değildir, diğerlerinde yineleme kadar iyidir ve kesinlikle diğerlerinde de gereklidir. Uzun iş kuralları zincirleriyle yağmalamak, genellikle özyinelemede yardımcı olmaz. Veri akışlarını yineleme, özyineleme ile faydalı bir şekilde yapılabilir. Çok boyutlu dinamik veri yapılarını yinelemek (örneğin labirentler, nesne ağaçları ...), tekrarlama, açık veya kapalı olmadan hemen hemen imkansızdır. Bu durumlarda, açık yinelemenin örtük olmaktan çok daha iyi olduğunu - hiçbir şeyin birisinin kendi korkutucu R-sözcüğünden kaçınmak için dilde kendi geçici, eksik, buggy yığınını uyguladığı kodu okumaktan daha acı verici olmadığını unutmayın.

Özyineleme ile ilgili olarak önbellekleme ile ne demek istiyorsunuz?
Giorgio

@Giorgio muhtemelen memoization
jk.

Feh. Ortamınız kuyruk çağrılarını optimize etmiyorsa, daha iyi bir ortam bulmalı ve geliştiricileriniz özyinelemeyi bilmiyorsa, daha iyi geliştiriciler bulmalısınız. Bazı standartlar var millet!
CA McCann

1

Özyineleme, fonksiyon çağrısının tekrarlanması ile ilgilidir, döngü bellekte yer almak için atlamanın tekrarlanması ile ilgilidir.

Yığın taşması ile ilgili olarak da belirtilmesi gerekir - http://en.wikipedia.org/wiki/Stack_overflow


1
Özyineleme, tanımı kendisini çağırmayı gerektiren bir işlev anlamına gelir.
hardmath

1
Basit tanımlamanız tam olarak% 100 doğru olmasa da, yığın taşmasından söz eden tek kişi sizsiniz.
Qix

1

Bu gerçekten kolaylık veya ihtiyacına bağlıdır:

Python programlama dilini alırsanız özyinelemeyi destekler, ancak varsayılan olarak özyineleme derinliği için bir sınır vardır (1000). Sınırı aşarsa, bir hata veya istisna elde ederiz. Bu sınır değiştirilebilir, ancak bunu yaparsak dilde anormal durumlar yaşayabiliriz.

Şu anda (özyineleme derinliğinden daha fazla çağrı), döngü yapılarını tercih etmemiz gerekiyor. Yığın boyutu yeterli değilse, döngü yapılarını tercih etmemiz gerekir.


3
İşte Guido van Rossum'un neden Python'da kuyruk özyineleme optimizasyonu istemediğini belirten bir blog (farklı dillerin farklı taktiksel yaklaşımlar kullandığı fikrini desteklemektedir).
Hardmath

-1

Strateji tasarım desenini kullanın.

  • Özyineleme temiz
  • Döngüler (tartışmalı) verimlidir

Yükünüze bağlı olarak (ve / veya diğer koşullara bağlı olarak) bir tane seçin.


5
Bir dakika ne? Strateji kalıbı buraya nasıl sığar? Ve ikinci cümle boş bir cümle gibi geliyor.
Konrad Rudolph

@KonradRudolph Özyineleme için giderdim. Çok büyük veri kümeleri için döngülere geçeceğim. Demek istediğim şey o. Açık değilse özür dilerim.
Pravin Sonawane

3
Ah. Hala bunun sabit bir anlamı olan ve "mecazi olarak kullandığınız" strateji tasarım deseni "olarak adlandırılabileceğinden hala emin değilim. Ama şimdi en azından nereye gittiğini görüyorum.
Konrad Rudolph

@KonradRudolph önemli bir ders öğrendi. Ne söylemek istediğinizi derinden anlatın .. Teşekkürler .. bu yardımcı oldu .. :)
Pravin Sonawane

2
@Pravin Sonawane: Kuyruk özyineleme optimizasyonunu kullanabilirseniz özyinelemeyi devasa veri kümelerinde de kullanabilirsiniz.
Giorgio
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.