도메인 크기가 작고 (| 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이러한 폐기 된 요소 (예 : 범위의 첫 번째 요소)를 도메인에있는 다른 요소의 이미지로 저장할 수 있는 목록을 유지 해야합니다.
이 솔루션에서 볼 수있는 두 가지 문제가 있습니다. 첫 번째는 목록 ignored과 range기능 recur-on-first이 상당히 커서 append값이 비싸다는 것입니다. 두 번째 문제는 솔루션의 재귀 깊이가 범위의 크기에 달려 있다는 것입니다.
그래서 이중 연결 목록을 사용하여 범위에 요소를 저장하는 다음 솔루션을 생각해 냈습니다. 기능 start, next및 end이중 연결리스트를 반복하는 기능을 제공합니다.
(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 및 가변 데이터 구조를 사용하지 않는 솔루션이 있습니까? 다시 말해,이 문제에 대한 효율적인 기능적 프로그래밍 솔루션이 있습니까?