2015-10-20 10 views
7

私は再帰スキームスタイルの構造を持っています。完全構造を含むすべての部分構造のリストを取得したいと思います - Listparaを呼び出すことでこれを実装し、各ステップで元の構造にマッピングしてから別のものを前面に貼り付けることは可能だと思いますが、それは非常に扱いにくいようです。(Haskellが間違っていれば、私は本当にまだBase構造を理解していないようMuの用語で書かれた)`tails`の再帰スキームの一般化

gtails :: Functor f => Mu f -> [Mu f] 
gtails = para (\a -> (fmap fst a) : (foldMap snd a)) 

(すなわち場合f=Primには、これはtailsで、他のfことが一般化だ)

よりよい方法はあります?これはあまり悪くはないが、fmap fst aはその段階で元の構造を回復するのがかなり面倒だと感じている.はparaを使ったときに自分自身がたくさん繰り返していることが分かっている(fold aの場合はcataそれは不要です)。

答えて

10

paraとは何の問題もありません。リストにしてrecursion-schemesせずに専門の明確化のために

import Data.Functor.Foldable 

tails :: [a] -> [[a]] 
tails = para (\case Nil -> [[]]; Cons a (as, res) -> (a : as) : res) 

、::ちょうど頭と各Cons段階で尾をバックCONS

para :: (a -> [a] -> b -> b) -> b -> [a] -> b 
para f z []  = z 
para f z (a:as) = f a as (para f z as) 

tails :: [a] -> [[a]] 
tails = para (\a as res -> (a : as) : res) [[]] 

をしかし、あなたは、より一般的になりたい場合は、それほど素敵para製剤は便利来る:

import qualified Data.Functor.Foldable as Rec (Foldable) 

tails :: (Rec.Foldable t, Base t ~ Prim [a]) => t -> [t] 
tails as = as : para (\case Nil -> []; Cons a (as, res) -> as : res) as 

これはいつものようにリストのために働くが、あなたはまた、type instance Base t = Prim [a]を与えることができます他のt -sとFoldableインスタンスと一緒に使用し、同様に使用します。

代わりに、我々は制約を導入する費用で最初のtailsの定義を維持することができます:

tails' :: (Unfoldable t, Rec.Foldable t, Base t ~ Prim [a]) => t -> [t] 
tails' = para (\case Nil -> [embed Nil]; Cons a (as, res) -> embed (Cons a as) : res) 

projectためにファンクタのfixpointsのための逆embedがあるはずですので、これは、あまりにも悪いことではありませんとにかく、Foldableとインスタンスは自然にペアになります。

+0

私は再帰-制度の面でバージョンが必要です私の実際のケースは 'List'ではなく、再帰スキームのスタイル構造であるからです。 AIUI再帰スキーム 'para'は、あなたの例の' b'パラメータを持たない 'para :: Foldable t =>(Base t(t、a) - > a) - > t - > a'です。あなたが 'tfa = Maybe(a、f)'( 'Base t'が' 't' =' 't'')であれば' 'tails''と呼ばれるこの' 'para''私が間違っていない限り 'List'と同型ですが)他の' t'でも呼び出すことができますか? – lmm

+0

@lmm編集を参照してください。私はこれがあなたが意味するものだと願っています。 –

+0

'Prim'は' List'特有のものですが、私はリスト以外のデータ構造でも動作するバージョンを望んでいますが、必ずしも 'Base t〜Prim [a]' ?私が考えている関数は、追加の制約を必要とすべきではありません。私は、有効と思われる実装で質問を更新しました(しかし、私は標準的な関数になると思います。バージョン)。 – lmm

1

paraが実際にここで使用する機能です。私はself-contained gistにすべてを入れて、それを試してみたいと思ったら例を追加しました。

固定点Muと通常のfoldparaの定義から始めます。

module Tails where 

import Data.Function 

newtype Mu f = In { out :: f (Mu f) } 

fold :: Functor f => (f a -> a) -> Mu f -> a 
fold alg = alg . fmap (fold alg) . out 

para :: Functor f => (f (a, Mu f) -> a) -> Mu f -> a 
para alg = alg . fmap (\m -> (para alg m, m)). out 

私たちは、その後paraと私たちは、リストモノイドに中間結果を収集するためにfoldMapを使用することができ、追加のFoldable制約を使用してtailsを実装することができます

tails :: (Functor f, Foldable f) => Mu f -> [Mu f] 
tails m = m : para (foldMap $ uncurry $ flip (:)) m 
+0

これは 'para'を使うときに好まれるスタイルですか?私はこのように再帰を行うことを考えましたが、私は 'fmap fst'が好きで、' m: 'が好きではありません。どちらかというと間違った抽象のように感じます。 – lmm

関連する問題