J'essaie d'écrire une branche et une recherche liée sur l'ensemble de toutes les fonctions f: D -> R, où la taille du domaine est petite (| D | ~ 20) et la plage est beaucoup plus grande (| R | ~ 2 ^ 20 ). Au départ, j'ai trouvé la solution suivante.
(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))))))))
Ici, le paramètre condlist
de la fonction builder
est une liste de conditions qui doivent être satisfaites par une solution. La fonction check
renvoie nil si aucun élément de la liste de conditions n'est violé par le partial-map
. La fonction recur-on-first
affecte le premier élément du domaine au premier élément de la plage et essaie de créer une solution à partir de là. A défaut, cela recur-on-first
invoque pour essayer de construire une solution qui affecte le premier élément du domaine à un élément autre que le premier de la plage. Cependant, il doit conserver une liste ignored
qui stocke ces éléments supprimés (comme le premier élément de la plage) car ils pourraient être des images de certains autres éléments du domaine.
Il y a deux problèmes que je peux voir avec cette solution. Le premier est que les listes ignored
et range
dans la fonction recur-on-first
sont assez grandes et les append
ing est une opération coûteuse. Le deuxième problème est que la profondeur de récursivité de la solution dépend de la taille de la plage.
J'ai donc trouvé la solution suivante qui utilise des listes doublement liées pour stocker les éléments dans la plage. Les fonctions start
, next
et end
fournir des installations sur la liste itérer doublement chaînée.
(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))))))
Le temps d'exécution de la deuxième solution est bien meilleur que celui de la première solution. L' append
opération dans la première solution est remplacée par des éléments d'épissage dans et hors d'une liste doublement liée (ces opérations sont à temps constant) et la profondeur de récursivité ne dépend que de la taille du domaine. Mais mon problème avec cette solution est qu'elle utilise du C
code de style. Voici donc ma question.
Existe-t-il une solution aussi efficace que la deuxième solution mais qui n'utilise pas de setf
structures de données s et mutables? En d'autres termes, existe-t-il une solution de programmation fonctionnelle efficace à ce problème?