Segitiga Rekursif LISP

Oct 30 2020

Panggilan rekursif saya sepertinya tidak berfungsi, saya mencoba membuat segitiga yang diberi bilangan bulat, dan dengan bantuan kalian saya akhirnya dapat membuat string dicetak pada baris yang sama, jumlah kali yang benar untuk 1 baris. Di akhir pencetakan baris, saya memiliki panggilan rekursif yang memanggil segitiga untuk membuat baris lain, satu karakter lebih pendek. Panggilan ini sepertinya tidak pernah dihubungi karena suatu alasan. Silakan temukan kode di bawah ini, terima kasih sebelumnya untuk semua dan semua bantuan, itu semua sangat dihargai!

Catatan: Di samping catatan, apakah ada cara untuk menghentikan fungsi di Lisp yang mirip dengan pernyataan return? Saya ingin rekursi berhenti di k = 1, dan tidak terus ke k = 0.

(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))

sampel keluaran segitiga (3)

aaa 
aa 
a 

sampel keluaran segitiga (-3)

aaa 
 aa 
  a

Jawaban

3 adabsurdum Oct 30 2020 at 08:45

Pertama, Anda perlu memutuskan dengan tepat apa yang newTrianglesedang dilakukannya. Panggilan tersebut (print (newTriangle 3))menyarankan agar newTrianglemengembalikan string , yang kemudian dicetak oleh panggilan ke print. Tapi, definisi OP newTriangleadalah hasil pencetakan, dan mengembalikan satu baris segitiga sebagai string.

Panggilan rekursif ke newTriangletidak pernah tercapai karena semua kemungkinan kasus untuk nilai khabis sebelum garis ini tercapai. Karena khanya bisa kurang dari nol, sama dengan nol, atau lebih besar dari nol, dan karena semua kasus ini diuji sebelum mencapai panggilan rekursif, itu tidak pernah tercapai. Perhatikan juga bahwa kode OP memiliki sintaks yang salah untuk bagian akhir condpernyataan. Ekspresi pertama dalam condcabang adalah tes, dan konvensi digunakan di tsini untuk cabang yang akan selalu dievaluasi jika tercapai. Tapi, banyak kasus ini tidak diperlukan di sini.

Dengan asumsi newTrianglefungsi seharusnya tidak mengembalikan string, tetapi harus mencetak segitiga sebagai efek samping , apa yang harus dilakukannya? Jika nomor masukan lebih besar dari 0, itu harus mencetak baris dengan jumlah karakter yang sama dengan nomor masukan dan kemudian memanggil dirinya sendiri dengan masukan dikurangi satu; jika tidak maka tidak akan melakukan apa-apa:

(defun print-triangle (k)
  (when (> k 0)
    (princ (make-string k :initial-element #\a))
    (terpri)
    (print-triangle (- k 1))))

Definisi ini dinamai print-triangleuntuk menekankan bahwa ia mencetak segitiga sebagai efek samping, dan karena kasus kebab adalah idiomatik di Lisps, dan camelCase tidak. Perhatikan bahwa setiap kali yang print-triangledipanggil dengan input lebih besar dari nol, string dengan panjang yang benar dicetak , dan kemudian baris baru dicetak (dengan nama yang tidak jelas terpri, yang hanya menulis baris baru ke arus keluaran saat ini), sebelum memanggil print-trianglelagi dengan kdikurangi 1.

Contoh interaksi REPL:

CL-USER> (print-triangle 3)
aaa
aa
a
NIL

Jika tujuannya adalah mengembalikan string, salah satu pendekatannya adalah memanggil fungsi pembantu yang menyimpan hasilnya dalam parameter:

(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))

Di sini, new-trianglemengambil argumen integer, dan memanggil build-triangle, meneruskan argumen integer dan string kosong di resultposisinya. The build-triangleFungsi beroperasi sama seperti print-trianglesebelumnya, tapi bukannya mencetak garis, mereka concatenated dengan result, bersama dengan string yang berisi baris baru. Setelah build-triangleselesai, resultstring dikembalikan ke new-triangle. Perhatikan bahwa hanya memanggil new-triangledari REPL akan mencetak string yang dihasilkan sebagai data (yaitu, dengan tanda kutip); memanggil printhasil dari new-trianglekeduanya akan mencetak string sebagai data, dan mengembalikan string tersebut. Untuk melihat string tercetak tanpa tanda petik, formatbisa digunakan; atau Anda bisa menggunakan princyang akan mencetak string tanpa tanda kutip, dan mengembalikan string itu sendiri:

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
"