예, para
. catamorphism 또는 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
은 "반복"인 catamorphisms ( ) 와 대조적으로 paramorphism을 "primitive recursion"이라고 부릅니다 .
여기서 foldr
의 두 파라미터 (리스트의 꼬리 여기서,)는 입력 데이터의 각 재귀 하위 객체에 대한 재귀 적 연산 값을 주어, para
'S 파라미터 원래 하위 객체 그것으로부터 재귀 계산 값 모두를 얻을.
잘 표현 된 함수의 예 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
with 를 정의하는 비결 은 원본 데이터에 대한 액세스 권한이 없더라도 각 단계에서 꼬리의 복사본에 액세스 할 수 있도록 원본 데이터 foldr
의 복사본 을 재구성하는 것 입니다. 마지막에 snd
입력 사본을 버리고 출력 값만 제공합니다. 그것은 매우 효율적인 아니지만, 당신이 깎아 지른듯한 표현력에 관심이 있다면, para
더 이상 당신에게 제공하지 않습니다 foldr
. 당신이 사용하는 경우 foldr
의 인코딩 된 버전을 para
다음 safeTail
요소에 의해 꼬리 요소를 복사, 결국 선형 시간이 소요됩니다.
그래서, 그게 다입니다 : 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)
다시 말하지만,이 둘은 동일한 "복사본 만들기"트릭 으로 para
정의 되어 상호 정의 가능합니다.cata
para psi = snd . cata (\ fxt -> (In (fmap fst fxt), psi fxt))
다시 말하지만는 para
보다 표현력이 cata
좋지 않지만 입력의 하위 구조에 쉽게 액세스해야하는 경우 더 편리합니다.
편집 : 또 다른 좋은 예를 기억했습니다.
Fix TreeF
여기서 주어진 이진 검색 트리를 고려하십시오 .
data TreeF sub = Leaf | Node sub Integer sub
이진 검색 트리에 대한 삽입을 먼저 정의한 cata
다음 para
. para
각 노드에서 하나의 하위 트리에 삽입하고 다른 하나는 그대로 유지해야 하므로 버전을 훨씬 쉽게 찾을 수 있습니다.
para f base xs = foldr (uncurry f) base $ zip xs (tail $tails xs)
, methinks.