ต่อไปนี้เป็นเวอร์ชันสองสามเวอร์ชันที่มีแนวคิดสะอาด (ไม่มีการดำเนินการทำลายล้าง แม้จะโดยปริยายก็ตาม) แต่อาจเข้าใจไม่ได้เล็กน้อย ทั้งสองทำงานได้มากกว่าเวอร์ชันทำลายล้างที่ชัดเจน (โดยปริยาย) ทั้งสองพยายามที่จะใช้ฟังก์ชันใดที่มีความซับซ้อนของเวลาที่ไม่คงที่ นอกเหนือจากฟังก์ชันที่พวกเขากำหนดไว้เอง (เช่น ไม่มีการเรียกไปยัง reverse
หรือ length
) ทั้งสองแสดงการวนซ้ำเป็นการเรียกซ้ำส่วนท้าย
จุดประสงค์ของทั้งสองสิ่งนี้คือการดูว่าคุณสามารถสร้างสิ่งต่าง ๆ โดยใช้การดำเนินการที่ค่อนข้างดั้งเดิมเท่านั้นและไม่ใช้การดำเนินการแบบทำลายล้าง และไม่มีการใช้โครงสร้างการวนซ้ำที่ชัดเจน แต่สิ่งนี้มีแนวโน้มที่จะทำให้คุณเสียค่าใช้จ่ายและเวลาเพิ่มขึ้นอีกเล็กน้อย
สำหรับฉัน จุดประสงค์ก็คือเพื่อแสดงให้เห็นว่าปัญหารูปแบบที่ 'สะอาด' เหล่านี้มักจะเข้าใจได้ยากกว่ารูปแบบที่ 'สกปรก' มาก อย่างไรก็ตาม นี่คือความเห็น
ทั้งสองคืนค่าสองครึ่งหนึ่งของรายการเป็นค่าหลายค่า ทั้งสองอาจไม่เหมาะเป็นคำตอบการบ้าน
เวอร์ชันแรกทำงานโดย:
- เดินไปตามรายการโดยคำนวณความยาวและสร้างสำเนากลับรายการ
- เดินลงไปที่สำเนาที่กลับด้านนั้นสะสมไว้เป็นสำเนาที่กลับรายการอีกสองชุดซึ่งเป็นผลลัพธ์
เวอร์ชันนี้จะดำเนินรายการอย่างมีประสิทธิภาพสองครั้ง และสร้างสำเนาที่กลับรายการโดยสมบูรณ์ จากนั้นจึงสร้างสำเนา นั้น ที่กลับรายการทั้งหมด
(defun halfify (list)
;; Return two values: a copy of the first half of LIST and a copy of
;; the second half of LIST. 'half' is defined as by (round (length
;; list) 2).
;;
;; This works by walking down the list to accumulate a reversed copy
;; of it and its length: half/accum does this. When this is done,
;; rev/split then walks down the reversed copy accumulating a
;; further reversed copy into one of two accumulators.
;;
;; This walks the list twice, and conses essentially two copies of
;; it.
(labels ((half/accum (tail n accum)
(if (null tail)
(rev/split accum (round n 2) '() '())
(half/accum (rest tail) (1+ n) (cons (first tail) accum))))
(rev/split (tail n h1 h2)
(cond ((null tail)
(values h1 h2))
((> n 0)
(rev/split (rest tail) (1- n) (cons (first tail) h1) h2))
(t
(rev/split (rest tail) n h1 (cons (first tail) h2))))))
(half/accum list 0 '())))
เวอร์ชันที่สองทำงานโดย:
- เดินไปตามรายการเพื่อคำนวณความยาวของรายการ
- การแยกรายการออกเป็น halg โดยใช้ความยาวที่คำนวณได้ โดยสะสมการแยก (ส่วนนำของรายการ) ไปข้างหลัง
- การกลับก้อนนำโดยใช้ตัวสะสม
วิธีนี้จะมีประสิทธิภาพมากกว่าเล็กน้อย: โดยจะเดินไปตามรายการสองครั้งอย่างมีประสิทธิภาพ (หนึ่งครั้งเพื่อคำนวณความยาว จากนั้นจึงเดินครึ่งทางสองครั้ง) แต่จะถือว่ามากเท่ากับรายการเท่านั้น เนื่องจากจะถือว่าครึ่งนำสองครั้ง ถอยหลังหนึ่งครั้งแล้วหนึ่งครั้ง เพื่อย้อนกลับ
โปรดทราบว่าส่วนท้ายของรายการที่ส่งคืนโดยฟังก์ชันนี้จะแชร์โครงสร้างร่วมกับส่วนท้ายของรายการเดิม ซึ่งไม่เป็นความจริงสำหรับฟังก์ชันแรก
(defun halfify (list)
;; Return two values: a copy of the first half (rounded) of the
;; list, and the remainder of it.
;;
;; This does essentially two walks down the list (once to compute
;; the length, half to build a reversed of the first half and then
;; half again to reverse it, and conses as much as the whole list
;; (half for the reversed half-copy, half to reverse it). I don't
;; think you can do better than this without code which is
;; implicitly destructive, or not tail-recursive.
(labels ((half (tail n)
(if (null tail)
(split list (round n 2) '())
(half (rest tail) (1+ n))))
(split (tail m results)
(if (zerop m)
(values (rev results '()) tail)
(split (rest tail) (1- m) (cons (first tail) results))))
(rev (tail result)
(if (null tail)
result
(rev (rest tail) (cons (first tail) result)))))
(half list 0)))
ในที่สุด ฉันได้อ่านคำใบ้อันชาญฉลาดของ Kaz แล้ว และนี่คือเวอร์ชันที่ใช้เคล็ดลับนั้น เวอร์ชันนี้จะตัดรายการก่อนจุดกึ่งกลางเสมอหากความยาวของรายการเป็นเลขคี่
(defun halfify (list)
(labels ((half/step (fast slow a)
(if (null fast)
(values (rev a '()) slow)
(let ((fast-tail (rest fast)))
(if (null fast-tail)
(values (rev a '()) slow)
(half/step (rest fast-tail) (rest slow)
(cons (first slow) a))))))
(rev (tail result)
(if (null tail)
result
(rev (rest tail) (cons (first tail) result)))))
(half/step list list '())))
person
Community
schedule
23.10.2019
subseq
เว้นแต่ว่าการมอบหมายที่นี่คือการใช้การเรียกซ้ำและไม่ใช่ฟังก์ชันในตัว... - person verdammelt   schedule 23.10.2019let
คือ(let ((variable value) (other-variable other-value)) form)
หากคุณพูด(let (x ... ))
นั่นจะตั้งค่าx
เป็นnil
- person verdammelt   schedule 23.10.2019