LISP Recursive Triangle

Oct 30 2020

ดูเหมือนว่าการโทรซ้ำของฉันจะไม่ทำงานฉันพยายามสร้างรูปสามเหลี่ยมให้เป็นจำนวนเต็มและด้วยความช่วยเหลือจากพวกคุณในที่สุดฉันก็สามารถพิมพ์สตริงในบรรทัดเดียวกันจำนวนครั้งที่ถูกต้องสำหรับ 1 บรรทัด ในตอนท้ายของการพิมพ์บรรทัดฉันมีการเรียกซ้ำที่เรียกสามเหลี่ยมเพื่อสร้างบรรทัดอื่นสั้นกว่าหนึ่งอักขระ ดูเหมือนว่าจะไม่มีการโทรนี้ด้วยเหตุผลบางประการ โปรดค้นหารหัสด้านล่างนี้ขอขอบคุณทุกท่านล่วงหน้าสำหรับความช่วยเหลือใด ๆ ทั้งหมดนี้เป็นที่ชื่นชมอย่างมาก!

หมายเหตุ: ในบันทึกด้านข้างมีวิธีใดในการหยุดฟังก์ชันใน Lisp คล้ายกับคำสั่ง return หรือไม่? ฉันต้องการให้การเรียกซ้ำหยุดที่ k = 1 และไม่ดำเนินการต่อไปที่ 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))

สามเหลี่ยมเอาต์พุตตัวอย่าง (3)

aaa 
aa 
a 

สามเหลี่ยมเอาต์พุตตัวอย่าง (-3)

aaa 
 aa 
  a

คำตอบ

3 adabsurdum Oct 30 2020 at 08:45

ขั้นแรกคุณต้องตัดสินใจให้แน่ชัดว่าnewTriangleกำลังทำอะไรอยู่ โทร(print (newTriangle 3))แสดงให้เห็นว่าnewTriangleควรจะกลับมาเป็นสตริงprintซึ่งจะถูกพิมพ์แล้วโดยเรียกร้องให้ แต่คำจำกัดความ OP ของnewTriangleเป็นทั้งเอาต์พุตการพิมพ์และส่งคืนบรรทัดเดียวของสามเหลี่ยมเป็นสตริง

การเรียกแบบเรียกซ้ำnewTriangleไม่สามารถเข้าถึงได้เนื่องจากกรณีที่เป็นไปได้ทั้งหมดสำหรับค่าของkจะหมดก่อนที่จะถึงบรรทัดนี้ เนื่องจากkสามารถมีค่าน้อยกว่าศูนย์เท่ากับศูนย์หรือมากกว่าศูนย์เท่านั้นและเนื่องจากกรณีเหล่านี้ทั้งหมดได้รับการทดสอบก่อนที่จะถึงการเรียกซ้ำจึงไม่สามารถเข้าถึงได้ โปรดทราบว่ารหัส OP มีไวยากรณ์ที่ไม่ถูกต้องสำหรับส่วนสุดท้ายของcondคำสั่ง นิพจน์แรกในcondสาขาคือการทดสอบและการประชุมจะใช้tที่นี่สำหรับสาขาที่จะได้รับการประเมินเสมอหากถึง แต่หลายกรณีนี้ไม่จำเป็นต้องใช้ที่นี่

สมมติว่าnewTriangleฟังก์ชันไม่ควรส่งคืนสตริง แต่ควรพิมพ์สามเหลี่ยมเป็นเอฟเฟกต์ด้านข้างควรทำอย่างไร? หากหมายเลขอินพุตมากกว่า 0 ควรพิมพ์บรรทัดที่มีจำนวนอักขระเท่ากับหมายเลขอินพุตจากนั้นเรียกตัวเองโดยที่อินพุตลดลงทีละหนึ่ง มิฉะนั้นก็ไม่ควรทำอะไร:

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

คำจำกัดความนี้ได้รับการตั้งชื่อprint-triangleเพื่อเน้นว่าพิมพ์สามเหลี่ยมเป็นผลข้างเคียงและเนื่องจาก kebab-case เป็นสำนวนใน Lisps และไม่ใช่ camelCase โปรดสังเกตว่าทุกครั้งที่print-triangleถูกเรียกด้วยอินพุตที่มากกว่าศูนย์จะมีการพิมพ์สตริงที่มีความยาวที่ถูกต้องจากนั้นจึงพิมพ์ขึ้นบรรทัดใหม่ (ด้วยชื่อที่คลุมเครือterpriซึ่งจะเขียนขึ้นบรรทัดใหม่ไปยังสตรีมเอาต์พุตปัจจุบัน) ก่อนที่จะเรียกprint-triangleอีกครั้ง โดยkลดลง 1

ตัวอย่างการโต้ตอบ REPL:

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

หากเป้าหมายคือการส่งคืนสตริงแทนวิธีการหนึ่งคือการเรียกใช้ฟังก์ชันตัวช่วยที่เก็บผลลัพธ์ไว้ในพารามิเตอร์:

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

ที่นี่new-triangleรับอาร์กิวเมนต์จำนวนเต็มและเรียกbuild-triangleผ่านอาร์กิวเมนต์จำนวนเต็มและสตริงว่างในresultตำแหน่ง build-triangleฟังก์ชั่นดำเนินการมากเช่นเดียวกับprint-triangleก่อน แต่แทนที่จะพิมพ์เส้นที่พวกเขาจะตัดแบ่งกับresultพร้อมกับสตริงที่มีขึ้นบรรทัดใหม่ เมื่อbuild-triangleเสร็จสิ้นสตริงถูกส่งกลับไปresult new-triangleโปรดทราบว่าการเรียกnew-triangleจาก REPL จะพิมพ์สตริงผลลัพธ์เป็นข้อมูล (เช่นพร้อมด้วยเครื่องหมายคำพูด) การเรียกprintใช้ผลลัพธ์ของnew-triangleทั้งสองจะพิมพ์สตริงเป็นข้อมูลและส่งคืนสตริง หากต้องการดูสตริงที่พิมพ์โดยไม่มีเครื่องหมายคำพูดformatสามารถใช้ได้ หรือคุณสามารถใช้princซึ่งจะพิมพ์สตริงโดยไม่มีเครื่องหมายคำพูดและส่งคืนสตริงเอง:

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
"