関数型プログラミング言語で分岐限定を実装する方法は?


26

すべての関数f:D-> Rのセットで分岐および境界検索を作成しようとしています。ここで、ドメインサイズは小さく(| D |〜20)、範囲ははるかに大きくなります(| R |〜2 ^ 20 )。最初に、私は次の解決策を思いつきました。

(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ドメイン内の他の要素の画像である可能性があるため、これらの破棄された要素(範囲内の最初の要素など)を格納するリストを維持する必要があります。

このソリューションには2つの問題があります。1つ目は、リストignoredrange関数のrecur-on-firstサイズが非常に大きく、リストを作成appendするのは高価な操作であることです。2番目の問題は、解の再帰の深さが範囲のサイズに依存することです。

そこで、二重リンクリストを使用して範囲内の要素を保存する次のソリューションを思い付きました。関数startnextおよび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))))))

2番目のソリューションのランタイムは、1番目のソリューションのランタイムよりもはるかに優れています。append最初のソリューションの操作は、二重にリンクされたリストの内外のスプライシング要素に置き換えられ(これらの操作は一定時間です)、再帰の深さはドメインのサイズにのみ依存します。しかし、このソリューションの私の問題は、Cスタイルコードを使用することです。私の質問はこれです。

2番目のソリューションと同じくらい効率的ですが、setfsおよび可変データ構造を使用しないソリューションはありますか?言い換えれば、この問題に対する効率的な関数型プログラミングソリューションはありますか?

回答:


1

頭に浮かぶ最初のアイデア:同じ一般的なアプローチを使用しますが、ループを、計算の次の段階のスプライスリストである末尾再帰呼び出しで置き換えますか?接続されたリストを変更する必要はなく、各段階で新しいリストを生成するだけです。確かにこれは一定時間ではありませんが、とにかくスプライスする場所を見つけるためにリストを歩く必要があります。特に、単一リンクリストを使用できる場合は、ほとんどのノードを再利用することもできます。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.