Come implementare una diramazione in un linguaggio di programmazione funzionale?


26

Sto cercando di scrivere un ramo e la ricerca associata sull'insieme di tutte le funzioni f: D -> R, dove la dimensione del dominio è piccola (| D | ~ 20) e l'intervallo è molto più grande (| R | ~ 2 ^ 20 ). Inizialmente, ho trovato la seguente soluzione.

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

Qui il parametro condlistper la funzione builderè un elenco di condizioni che dovrebbero essere soddisfatte da una soluzione. La funzione checkrestituisce zero se qualsiasi elemento nell'elenco delle condizioni viene violato da partial-map. La funzione recur-on-firstassegna il primo elemento nel dominio al primo elemento nell'intervallo e tenta di creare una soluzione da lì. In caso recur-on-firstcontrario, si invoca a tentare di costruire una soluzione che assegni il primo elemento nel dominio a un elemento diverso dal primo nell'intervallo. Tuttavia, deve mantenere un elenco ignoredche memorizza questi elementi scartati (come il primo elemento nell'intervallo) in quanto potrebbero essere immagini di alcuni altri elementi nel dominio.

Ci sono due problemi che posso vedere con questa soluzione. Il primo è che gli elenchi ignorede rangenella funzione recur-on-firstsono piuttosto grandi e appendil loro utilizzo è un'operazione costosa. Il secondo problema è che la profondità di ricorsione della soluzione dipende dalle dimensioni dell'intervallo.

Quindi ho ideato la seguente soluzione che utilizza elenchi doppiamente collegati per memorizzare gli elementi nell'intervallo. Le funzioni start, nexte endoffrono servizi per scorrere la lista doppiamente collegata.

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

Il runtime della seconda soluzione è molto migliore del runtime della prima soluzione. L' appendoperazione nella prima soluzione è sostituita da elementi di giunzione all'interno e all'esterno di un elenco doppiamente collegato (queste operazioni sono a tempo costante) e la profondità di ricorsione dipende solo dalla dimensione del dominio. Ma il mio problema con questa soluzione è che utilizza il Ccodice di stile. Quindi la mia domanda è questa.

Esiste una soluzione efficiente quanto la seconda soluzione ma che non utilizza setfstrutture di dati mutabili? In altre parole, esiste un'efficace soluzione di programmazione funzionale a questo problema?

Risposte:


1

Prima idea che mi viene in mente: utilizzare lo stesso approccio generale, ma sostituire il ciclo con una chiamata ricorsiva di coda il cui parametro è l'elenco di giunzioni per la fase successiva del calcolo? Non è mai necessario modificare l'elenco di giunzioni, è sufficiente generare un nuovo elenco in ogni fase. Questo non è certo un tempo costante, ma è necessario percorrere l'elenco per trovare comunque un punto di giunzione. Potrebbe anche essere in grado di riutilizzare la maggior parte dei nodi, soprattutto se è possibile utilizzare un elenco collegato singolarmente.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.