LISP Yinelemeli Üçgen
Yinelemeli çağrım çalışmıyor gibi görünüyor, bir tamsayı verilen bir üçgen oluşturmaya çalışıyorum ve sizlerin yardımıyla nihayet aynı satıra dizgiyi 1 satır için doğru sayıda yazdırabildim. Satırın yazdırılmasının sonunda, üçgeni bir karakter kısaltarak başka bir satırı yapmak için çağıran özyinelemeli bir çağrı var. Bu çağrıya nedense asla ulaşılamadı. Lütfen aşağıdaki kodu bulun, her türlü yardım için şimdiden teşekkür ederim, çok minnettarız!
Not: Bir yan not olarak, Lisp'te return deyimine benzer bir işlevi durdurmanın herhangi bir yolu var mı? Özyinelemenin k = 1'de durmasını ve k = 0'a devam etmemesini istiyorum.
(defun newTriangle (k)
(cond ((<= k 0) (princ '(Nope)))
((or (= k 1) (= k -1)) (princ 'a))
((> k 0) (make-string k :initial-element #\a))
(newTriangle (- k 1))))
(print (newTriangle 3))
örnek çıktı üçgeni (3)
aaa
aa
a
örnek çıktı üçgeni (-3)
aaa
aa
a
Yanıtlar
İlk önce, ne newTriangleyaptığına tam olarak karar vermelisin . Çağrı , daha sonra çağrı tarafından yazdırılan bir dize döndürmesi gerektiğini (print (newTriangle 3))önerir . Ancak, OP tanımı hem çıktıyı yazdırmak hem de üçgenin tek bir satırını dizge olarak döndürmektir.newTriangleprintnewTriangle
İçin özyinelemeli çağrıya newTriangleasla ulaşılmaz, çünkü değeri için tüm olası durumlar kbu satıra ulaşılmadan önce tükenir. Yana ksadece sıfırdan küçük olabilir, sıfıra eşit veya sıfırdan büyük ve bu örneklerin tümünde özyinelemeli çağrı ulaşmadan önce test olduğundan, o ulaştı asla. Ayrıca, OP kodunun, condifadenin son kısmı için yanlış sözdizimi içerdiğine dikkat edin . Bir conddaldaki ilk ifade bir testtir ve kural, tulaşıldığında her zaman değerlendirilecek olan bir dal için burada kullanmaktır . Ancak burada bu kadar çok vakaya ihtiyaç yoktur.
newTriangleİşlevin bir dizge döndürmemesi gerektiğini, ancak yan etki olarak bir üçgen yazdırması gerektiğini varsayarsak , ne yapmalı? Giriş numarası 0'dan büyükse, giriş numarasına eşit sayıda karakter içeren bir satır yazdırmalı ve ardından girişi bir azaltılarak kendisini aramalıdır; aksi takdirde hiçbir şey yapmamalı:
(defun print-triangle (k)
(when (> k 0)
(princ (make-string k :initial-element #\a))
(terpri)
(print-triangle (- k 1))))
Bu tanım, print-triangleyan etki olarak bir üçgen yazdırdığını vurgulamak için adlandırılmıştır ve çünkü kebap-case Lisps'te deyimseldir ve camelCase değildir. print-triangleSıfırdan büyük girdi ile çağrılan her seferinde , doğru uzunlukta bir dizge yazdırıldığına ve ardından tekrar terpriçağırmadan önce bir satırsonu yazdırıldığına dikkat edin (belirsiz bir şekilde adlandırılmış , sadece mevcut çıkış akışına bir satırsonu yazıyor) print-triangleile k1 azaltılır.
Örnek REPL etkileşimi:
CL-USER> (print-triangle 3)
aaa
aa
a
NIL
Hedef bunun yerine bir dize döndürmekse , bir yaklaşım, sonucu bir parametrede tutan bir yardımcı işlevi çağırmak olacaktır:
(defun new-triangle (k)
(build-triangle k ""))
(defun build-triangle (k result)
(if (> k 0)
(build-triangle (- k 1)
(concatenate 'string
result
(make-string k :initial-element #\a)
(string #\newline)))
result))
Burada, new-trianglebir tamsayı argümanı alır ve build-trianglehem tamsayı argümanını hem de resultpozisyondaki boş bir dizeyi ileterek çağırır . build-triangleFonksiyon kadar aynı çalışır print-triangleönce, bunun yerine hatları baskı, bunlar ile birleştirilmiş resultbir yeni satır içeren bir dize ile birlikte. Ne zaman build-trianglebitti, resultdize döndürülür new-triangle. new-triangleREPL'den basitçe çağırmanın , sonuçtaki dizeyi veri olarak (yani tırnak işaretleriyle) yazdıracağını unutmayın ; printsonucunun çağrılması new-trianglehem dizeyi veri olarak yazdırır hem de dizeyi döndürür. Tırnak işareti olmadan yazdırılan dizeyi görmek için formatkullanılabilir; veya princdizeyi tırnak işaretleri olmadan yazdıracak ve dizenin kendisini döndürecek olanı kullanabilirsiniz :
CL-USER> (new-triangle 3)
"aaa
aa
a
"
CL-USER> (print (new-triangle 3))
"aaa
aa
a
"
"aaa
aa
a
"
CL-USER> (format t "~A" (new-triangle 3))
aaa
aa
a
NIL
CL-USER> (princ (new-triangle 3))
aaa
aa
a
"aaa
aa
a
"