Tôi đang cố gắng viết một nhánh và tìm kiếm ràng buộc trên tập hợp tất cả các hàm f: D -> R, trong đó kích thước miền nhỏ (| D | ~ 20) và phạm vi lớn hơn nhiều (| R | ~ 2 ^ 20 ). Ban đầu, tôi đã đưa ra giải pháp sau đây.
(builder (domain range condlist partial-map)
(let ((passed? (check condlist partial-map)))
(cond
((not passed?) nil)
(domain (recur-on-first domain range condlist partial-map '()))
(t partial-map))))
(recur-on-first (domain range condlist partial-map ignored)
(cond
((null range) nil)
(t (let ((first-to-first
(builder (cdr domain)
(append ignored (cdr range))
condlist
(cons (cons (car domain) (car range)) partial-map))))
(or first-to-first
(recur-on-first domain
(cdr range)
condlist
partial-map
(cons (car range) ignored))))))))
Ở đây, tham số condlist
cho hàm builder
là một danh sách các điều kiện cần được thỏa mãn bằng một giải pháp. Hàm check
trả về nil iff bất kỳ phần tử nào trong danh sách các điều kiện bị vi phạm bởi partial-map
. Hàm recur-on-first
gán phần tử đầu tiên trong miền cho phần tử đầu tiên trong phạm vi và cố gắng xây dựng giải pháp từ đó. Không thực hiện được điều này recur-on-first
sẽ tự thử và xây dựng một giải pháp gán phần tử đầu tiên trong miền cho một số phần tử khác với phần tử đầu tiên trong phạm vi. Tuy nhiên, nó phải duy trì một danh sách ignored
lưu trữ các phần tử bị loại bỏ này (như phần tử đầu tiên trong phạm vi) vì chúng có thể là hình ảnh của một số phần tử khác trong miền.
Có hai vấn đề mà tôi có thể thấy với giải pháp này. Đầu tiên là danh sách ignored
và range
trong chức năng recur-on-first
khá lớn và append
ing chúng là một hoạt động đắt tiền. Vấn đề thứ hai là độ sâu đệ quy của giải pháp phụ thuộc vào kích thước của phạm vi.
Vì vậy, tôi đã đưa ra giải pháp sau đây sử dụng danh sách liên kết đôi để lưu trữ các yếu tố trong phạm vi. Các chức năng start
, next
và end
cung cấp các cơ sở để lặp lại danh sách liên kết đôi.
(builder (domain range condlist &optional (partial-map nil))
(block builder
(let ((passed? (check condlist partial-map)))
(cond
((not passed?) nil)
(domain (let* ((cur (start range))
(prev (dbl-node-prev cur)))
(loop
(if (not (end cur))
(progn
(splice-out range cur)
(let ((sol (builder (cdr domain)
range
condlist
(cons (cons (car domain) (data cur)) partial-map))))
(splice-in range prev cur)
(if sol (return-from builder sol)))
(setq prev cur)
(setq cur (next cur)))
(return-from builder nil)))))
(t partial-map))))))
Thời gian chạy của giải pháp thứ hai tốt hơn nhiều so với thời gian chạy của giải pháp thứ nhất. Các append
hoạt động trong các giải pháp đầu tiên được thay thế bởi các yếu tố nối vào và ra khỏi một danh sách gấp đôi liên kết (các hoạt động này là hằng số thời gian) và độ sâu đệ quy chỉ phụ thuộc vào kích thước của tên miền. Nhưng vấn đề của tôi với giải pháp này là nó sử dụng C
mã kiểu. Vì vậy, câu hỏi của tôi là này.
Có giải pháp nào hiệu quả như giải pháp thứ hai nhưng không sử dụng setf
cấu trúc dữ liệu s và có thể thay đổi? Nói cách khác, có một giải pháp lập trình chức năng hiệu quả cho vấn đề này?