Paramorphism은 무엇입니까?


96

이 고전적인 논문을 읽으면서 나는 패러 모피 즘에 갇혀 있습니다. 불행히도 섹션은 매우 얇고 Wikipedia 페이지에는 아무 말도 없습니다.

내 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), methinks.
Daniel Fischer

이 위키 페이지 에 따르면 , 패러 모피 즘은 "유도 데이터 유형에 대한 기본 재귀를 모델링합니다". 그게 도움이 될까요?
huon

4
이러한 질문 중 하나에 대한 의견에서 지적한 Jeremy Gibbons의 "Fission"논문은 매우 유용한 학습 자료입니다. cs.ox.ac.uk/jeremy.gibbons/publications/fission.pdf 수많은 재귀 패턴을 통해 매우 명확하게 작동합니다.
스티븐 테 틀리

1
Daniel의 재 작성 para f base xs = foldr g base (init $ tails xs) where g (x:xs) = f x xs . 이것은 Common Lisp의maplist .
윌 네스

답변:


110

예, 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)

parawith 를 정의하는 비결 은 원본 데이터에 대한 액세스 권한이 없더라도 각 단계에서 꼬리의 복사본에 액세스 할 수 있도록 원본 데이터 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각 노드에서 하나의 하위 트리에 삽입하고 다른 하나는 그대로 유지해야 하므로 버전을 훨씬 쉽게 찾을 수 있습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.