Làm thế nào để thực hiện một nhánh và ràng buộc trong một ngôn ngữ lập trình chức năng?


26

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ố condlistcho hàm builderlà 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 checktrả 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-firstgá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-firstsẽ 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 ignoredlư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 ignoredrangetrong chức năng recur-on-firstkhá lớn và appending 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, nextendcung 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 appendhoạ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 Cmã 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 setfcấ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?

Câu trả lời:


1

Ý tưởng đầu tiên xuất hiện trong đầu: sử dụng cùng một cách tiếp cận chung, nhưng thay thế vòng lặp bằng một cuộc gọi đệ quy đuôi có tham số là danh sách ghép cho giai đoạn tiếp theo của tính toán? Bạn không bao giờ phải sửa đổi danh sách ghép, chỉ cần tạo một danh sách mới ở mỗi giai đoạn. Điều này thừa nhận không phải là thời gian liên tục, nhưng bạn cần phải đi bộ danh sách để tìm nơi nối. Nó thậm chí có thể sử dụng lại hầu hết các nút, đặc biệt nếu bạn có thể sử dụng danh sách liên kết đơn.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.