Ich versuche, eine verzweigte und gebundene Suche für die Menge aller Funktionen f: D -> R zu schreiben, wobei die Domänengröße klein ist (| D | ~ 20) und der Bereich viel größer ist (| R | ~ 2 ^ 20) ). Anfangs hatte ich die folgende Lösung.
(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))))))))
Hier ist der Parameter condlist
der Funktion builder
eine Liste von Bedingungen, die von einer Lösung erfüllt werden sollten. Die Funktion check
gibt nil zurück, wenn ein Element in der Liste der Bedingungen von der verletzt wird partial-map
. Die Funktion recur-on-first
weist dem ersten Element im Bereich das erste Element in der Domäne zu und versucht, daraus eine Lösung zu erstellen. Gelingt dies recur-on-first
nicht, wird versucht, eine Lösung zu erstellen, die das erste Element in der Domäne einem anderen Element als dem ersten Element im Bereich zuweist. Es muss jedoch eine Liste geführt werden ignored
, in der diese verworfenen Elemente (wie das erste Element im Bereich) gespeichert sind, da es sich möglicherweise um Bilder einiger anderer Elemente in der Domäne handelt.
Es gibt zwei Probleme, die ich mit dieser Lösung sehen kann. Der erste Grund ist, dass die Listen ignored
und range
die Funktion recur-on-first
ziemlich umfangreich sind und dass append
sie eine teure Operation sind. Das zweite Problem ist, dass die Rekursionstiefe der Lösung von der Größe des Bereichs abhängt.
Deshalb habe ich die folgende Lösung gefunden, die doppelt verknüpfte Listen verwendet, um die Elemente im Bereich zu speichern. Die Funktionen start
, next
und end
bieten Einrichtungen iterieren der doppelt verknüpften Liste.
(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))))))
Die Laufzeit der zweiten Lösung ist viel besser als die Laufzeit der ersten Lösung. Die append
Operation in der ersten Lösung wird durch das Verbinden von Elementen in und aus einer doppelt verknüpften Liste ersetzt (diese Operationen sind zeitlich konstant), und die Rekursionstiefe hängt nur von der Größe der Domäne ab. Aber mein Problem mit dieser Lösung ist, dass es C
Style-Code verwendet. Meine Frage lautet also:
Gibt es eine Lösung, die genauso effizient ist wie die zweite, aber keine setf
s und veränderlichen Datenstrukturen verwendet? Mit anderen Worten, gibt es eine effiziente funktionale Programmierlösung für dieses Problem?