함수형 프로그래밍 언어로 분기 및 바인딩을 구현하는 방법은 무엇입니까?


26

도메인 크기가 작고 (| D | ~ 20) 범위가 훨씬 더 큰 (| R | ~ 2 ^ 20) 모든 기능 집합 f : D-> R에 대해 분기 및 바운드 검색을 작성하려고합니다. ). 처음에는 다음 솔루션을 생각해 냈습니다.

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

여기서 condlist함수 의 매개 변수 builder는 솔루션이 충족해야하는 조건 목록입니다. check조건 목록의 모든 요소가에 의해 위반되면 함수 는 nil을 반환합니다 partial-map. 이 함수 recur-on-first는 도메인의 첫 번째 요소를 범위의 첫 번째 요소에 할당하고 거기에서 솔루션을 작성하려고합니다. 이를 실패 recur-on-first하면 도메인의 첫 번째 요소를 범위의 첫 번째 요소가 아닌 다른 요소에 지정하는 솔루션을 시도하고 구성 할 수 있습니다. 그러나 ignored이러한 폐기 된 요소 (예 : 범위의 첫 번째 요소)를 도메인에있는 다른 요소의 이미지로 저장할 수 있는 목록을 유지 해야합니다.

이 솔루션에서 볼 수있는 두 가지 문제가 있습니다. 첫 번째는 목록 ignoredrange기능 recur-on-first이 상당히 커서 append값이 비싸다는 것입니다. 두 번째 문제는 솔루션의 재귀 깊이가 범위의 크기에 달려 있다는 것입니다.

그래서 이중 연결 목록을 사용하여 범위에 요소를 저장하는 다음 솔루션을 생각해 냈습니다. 기능 start, nextend이중 연결리스트를 반복하는 기능을 제공합니다.

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

두 번째 솔루션의 런타임은 첫 번째 솔루션의 런타임보다 훨씬 낫습니다. append첫 번째 솔루션 의 작업은 이중 연결 목록 안팎의 스 플라이 싱 요소로 대체되며 (이 작업은 일정한 시간 임) 재귀 깊이는 도메인의 크기에만 의존합니다. 그러나이 솔루션의 내 문제는 C스타일 코드를 사용한다는 것 입니다. 제 질문은 이것입니다.

두 번째 솔루션만큼 효율적이지만 setfs 및 가변 데이터 구조를 사용하지 않는 솔루션이 있습니까? 다시 말해,이 문제에 대한 효율적인 기능적 프로그래밍 솔루션이 있습니까?

답변:


1

염두에 두어야 할 첫 번째 아이디어 : 동일한 일반적인 접근 방식을 사용하지만 다음 계산 단계의 매개 변수가 매개 변수 인 꼬리 재귀 호출로 루프를 대체합니까? 스 플라이 싱 된 목록을 수정할 필요가 없으며 각 단계에서 새 목록을 생성하면됩니다. 이것은 항상 일정한 시간은 아니지만 어쨌든 접합 할 위치를 찾으려면 목록을 걸어야합니다. 특히 단일 연결 목록을 사용할 수있는 경우 대부분의 노드를 재사용 할 수도 있습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.