LISP Recursive Triangle
ดูเหมือนว่าการโทรซ้ำของฉันจะไม่ทำงานฉันพยายามสร้างรูปสามเหลี่ยมให้เป็นจำนวนเต็มและด้วยความช่วยเหลือจากพวกคุณในที่สุดฉันก็สามารถพิมพ์สตริงในบรรทัดเดียวกันจำนวนครั้งที่ถูกต้องสำหรับ 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
คำตอบ
ขั้นแรกคุณต้องตัดสินใจให้แน่ชัดว่า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
"