まあ、コアカーシブデータを扱うときの根本的な間違いは本当に1つだけです。それは、不注意に再帰を使用していることです。
コアカーションは、ある意味でデータが段階的に生成されていることを意味します。グラフ距離関数は、機能するはずなので、ここで参考になります。増分部分をどこに配置するかを考えてください。開始点は、ノードからそれ自体までの距離0です。それ以外の場合は、ノード間の最小距離より1大きい距離です。すぐ隣人。したがって、各距離値は増分であると予想されます。つまり、それら自体が適切にコアカーシブである必要があります。
問題の再帰は、との組み合わせによって発生し(+)
ますminimum
。最小値を見つけると、何であるかを心配することなく、1
常に未満になります...しかし、全体の値を計算せずにsを比較する方法はありません。1 + n
n
Int
つまり、アルゴリズムは、必要な範囲でのみ(1 +)
適用された回数を比較できることを期待しています。0
つまり、「ゼロ」と「サクセサ」を使用して定義された怠惰な自然数が必要です。
見よ:
data Nat = Z | S Nat
natToInt :: Nat -> Int
natToInt Z = 0
natToInt (S n) = 1 + natToInt n
instance Show Nat where show = show . natToInt
instance Eq Nat where
Z == Z = True
(S n1) == (S n2) = n1 == n2
_ == _ = False
Z /= Z = False
(S n1) /= (S n2) = n1 /= n2
_ /= _ = True
instance Ord Nat where
compare Z Z = EQ
compare Z (S _) = LT
compare (S _) Z = GT
compare (S n1) (S n2) = compare n1 n2
そしてGHCiでは:
> distance 1 $ fromList [(0,[1]),(1,[0,2]),(2,[1,3]),(3,[2])]
fromList [(0,1),(1,0),(2,1),(3,2)]
アルゴリズムが機能することの証明[0] ; それのあなたの実装はちょうど間違っていました。
ここで、わずかなバリエーションとして、アルゴリズムを別のグラフに適用してみましょう。
> distance 1 $ fromList [(0,[1]),(1,[0]),(2,[3]),(3,[2])]
...これで何ができると思いますか?ノード2または3のノード1からの距離はどれくらいですか?
GHCiで実行すると、明らかな結果が得られます。
fromList [(0,1),(1,0),(2,^CInterrupted.
それにもかかわらず、アルゴリズムはこのグラフで正しく機能します。問題がわかりますか?なぜGHCiにぶら下がったのですか?
要約すると、自由に混合できない2つの形式を明確に区別する必要があります。
- コアカーシブに生成された、怠惰な、場合によっては無限のデータ
- 再帰的に消費される有限データ
どちらの形式も、構造にとらわれない方法で変換できます(たとえば、map
有限リストと無限リストの両方で機能します)。Codataは、コアカーシブアルゴリズムによって駆動され、段階的に消費できます。データは再帰的に生成できますが、再帰的なアルゴリズムによって制限されます。
できないことは、codataを再帰的に消費するか(たとえば、無限のリストを折りたたむままにする)、またはデータをコアカーシブに生成することです(Haskellでは、怠惰のためにまれです)。
[0]:実際には、一部の入力(たとえば、一部の切断されたグラフ)で失敗しますが、それは別の問題です。