パラモーフィズムとは何ですか?


96

この古典的な論文を読んで、私はパラモーフィズムにこだわっています。残念ながら、このセクションはかなり薄く、ウィキペディアのページには何も書かれていません。

私のHaskellの翻訳は:

para :: (a -> [a] -> b -> b) -> b -> [a] -> b
para f base = h
  where
    h []       =   base
    h (x:xs)   =   f x xs (h xs)

しかし、私はそれをしていません-私は型シグネチャや希望する結果について直感を持っていません。

パラモーフィズムとは何ですか?実際に役立つ例は何ですか?


はい、私はこれらの 質問を見ましたが、それらはパラモーフィズムを直接カバーしておらず、参考資料として役立つかもしれないが、学習資料としては役立つかもしれないリソースのみを指しています。


1
para f base xs = foldr (uncurry f) base $ zip xs (tail $tails xs)、メチンク。
ダニエルフィッシャー

このwikiページによると、パラモーフィズムは「帰納的データ型に対するプリミティブな再帰のモデル化」です。それは何か/助けを意味しますか?
huon

4
これらの質問の1つに対するコメントで指摘したジェレミーギボンズの「分裂」論文は、非常に有用な学習資料です。cs.ox.ac.uk/jeremy.gibbons/publications/fission.pdf多数の再帰パターンを通じて非常に明確に機能します。
スティーブンテトリー

1
ダニエルの書き直しはのように簡略化できます para f base xs = foldr g base (init $ tails xs) where g (x:xs) = f x xs。これはCommon Lispをmaplist思い出させます。
Will Ness

回答:


110

はい、そうparaです。カタモルフィズムと比較、またはfoldr

para  :: (a -> [a] -> b -> b) -> b -> [a] -> b
foldr :: (a ->        b -> b) -> b -> [a] -> b

para  c n (x : xs) = c x xs (para c n xs)
foldr c n (x : xs) = c x    (foldr c n xs)
para  c n []       = n
foldr c n []       = n

準同型写像(foldr)が「反復」であることとは対照的に、準同型写像を「原始再帰」と呼ぶ人もいます。

どこfoldrの2つのパラメータは(リストの末尾だここで、)入力データの各再帰サブオブジェクトのために再帰的に計算された値が与えられている、paraのパラメータは、元のサブオブジェクトとそれから再帰的に計算された値の両方を取得します。

上手く表現される関数の例paraは、リストの適切な十分性のコレクションです。

suff :: [x] -> [[x]]
suff = para (\ x xs suffxs -> xs : suffxs) []

そのため

suff "suffix" = ["uffix", "ffix", "fix", "ix", "x", ""]

おそらくもっと簡単です

safeTail :: [x] -> Maybe [x]
safeTail = para (\ _ xs _ -> Just xs) Nothing

「cons」ブランチは、再帰的に計算された引数を無視し、末尾を返すだけです。遅延評価では、再帰的な計算は行われず、テールは一定の時間で抽出されます。

foldrを使用してpara非常に簡単に定義できます。それは定義する少しトリッキーだparaからfoldr、それは確かに可能だし、誰もがどのように行うのを知っている必要があります!

foldr c n =       para  (\ x  xs  t ->           c x    t)       n
para  c n = snd . foldr (\ x (xs, t) -> (x : xs, c x xs t)) ([], n)

定義するトリックparaでは、foldr再構築することであるコピー、我々は元にはアクセスできなかったにも関わらず、各ステップでの尾のコピーへのアクセスを得るようにすることを、元のデータのを。最後sndに、入力のコピーを破棄し、出力値のみを提供します。あまり効率的ではありませんが、純粋な表現力に興味がある場合はにすぎparaませんfoldr。このでfoldrエンコードされたバージョンのを使用するとparasafeTail結局線形の時間がかかり、末尾の要素が要素ごとにコピーされます。

つまり、それだけです。リストの末尾や、リストから計算された値にすぐにアクセスできるpara、より便利なバージョンですfoldr

一般的なケースでは、ファンクターの再帰的な固定点として生成されたデータ型を操作する

data Fix f = In (f (Fix f))

あなたが持っている

cata :: Functor f => (f         t  -> t) -> Fix f -> t
para :: Functor f => (f (Fix f, t) -> t) -> Fix f -> t

cata phi (In ff) = phi (fmap (cata phi) ff)
para psi (In ff) = psi (fmap keepCopy   ff) where
  keepCopy x = (x, para psi x)

繰り返しになりますが、この2つは相互に定義可能で、同じ「コピーを作成する」トリックによってpara定義されcataています。

para psi = snd . cata (\ fxt -> (In (fmap fst fxt), psi fxt))

繰り返しますが、paraは表現力に劣るものではありませんcataが、入力のサブ構造に簡単にアクセスする必要がある場合には便利です。

編集:私は別の良い例を思い出しました。

Fix TreeFwhere によって与えられる二分探索木を考えます

data TreeF sub = Leaf | Node sub Integer sub

バイナリ検索ツリーの挿入を最初にcata、次にとして定義してみてくださいparapara各ノードで1つのサブツリーに挿入する必要がありますが、他のノードをそのまま維持するため、バージョンがはるかに簡単になります。

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