私は現在LISP(特にSchemeとClojure)で遊んでおり、関数型プログラミング言語で典型的なデータ構造がどのように処理されるのかと思っています。
たとえば、グラフパスファインディングアルゴリズムを使用して問題を解決したいとします。そのグラフを関数型プログラミング言語で表現するにはどうすればよいでしょうか(主にLISPに適用できる純粋な関数型スタイルに関心があります)。グラフを完全に忘れて、他の方法で問題を解決しますか?
LISPで働いて久しぶりですが、覚えているように、基本的な非原子構造はリストです。他のすべてはそれに基づいています。したがって、各atomはノードであり、その後にノードを他のノードに接続するエッジのリストが続くノードです。アトムのリストを作成できます。これを行う他の方法もあると思います。
多分このようなもの:
(
(a (b c)),
(b (a c)),
(c (a b d)),
(d (c))
)
このようなグラフを与えることができます:
a <-> b <-> c <-> d ^ ^ | | + --------- +
ファンシーにしたい場合は、それにウェイトを追加することもできます。
(
(a (b 1.0 c 2.0)),
(b (a 1.0 c 1.0)),
(c (a 1.3 b 7.2 d 10.5)),
(d (c -10.5))
)
あなたもこれに興味があるかもしれません: CL-Graph (google-searchingフレーズ "LISP graph structure"で見つかります)
関数型言語は、非関数型言語と同じ方法でデータ構造を処理します。インターフェイスを実装から分離し、抽象データ型を作成します。
LISPで抽象データ型を作成できます。たとえば、グラフの場合、いくつかの関数が必要になることがあります。
(define (get-vertices graph) ;; gets all the vertices from a graph
...)
(define (get-edges graph) ;; gets all the edges from a graph
...)
(define (get-weight vertex-from vertex-to) ;; get the weight of a specific vertex
...)
グラフへのインターフェースを作成したら、実際のデータ構造をさまざまな方法で実装し、プログラマーの効率、柔軟性、計算効率などの要素を最適化できます。
重要なのは、グラフを使用するコードonlyがグラフインターフェイスを使用し、基になる実装にアクセスしないようにすることです。これにより、実際の実装から切り離されたクライアントコードがよりシンプルになります。
まあそれはあなたのグラフが有向/無向、加重/無加重であるかどうかに依存しますが、有向、加重グラフを表す1つの方法(これは最も一般的です)は(Clojureの)マップのマップを使うことです
{
:a {:b 3 :c 4}
:b {:a 1}
:c {}
}
ノード:a:bおよび:cのマップを表します。 :aは、重みが3の:bを指し、重みが4の:cを指します。:bは、重みが1の:aを指します。:cは、何も指しません。
Common LISPでは、ツリーを表す必要がある場合は、リストを使用するか(クイックハックの場合のみ)、ツリークラスを定義します(または構造体ですが、クラスは一般的な関数とうまく相互作用するので、なぜですか) 。
(defclass tree ()
((node :accessor node :initarg :node)
(children :accessor children :initarg :children)))
コードにリテラルツリーが必要な場合は、おそらく、必要なツリーのリスト表現を取り、それをツリーオブジェクトのツリーに変換するmake-tree
関数も定義します。