2017-02-09 7 views
4

I recursion-schemes 라이브러리를 사용하여 다음과 같은 코드가 있습니다 let countBy = reduceBy (\case Nil -> 0 ; Cons a b -> succ b) id in countBy [42,5,5,8,8,8]RamdaJS reduceBy()

{-# LANGUAGE LambdaCase #-} 
{-# LANGUAGE TypeFamilies #-} 
import Data.Functor.Foldable 
import Data.Maybe 

import qualified Data.Map as M 

reduceBy valueAlgebra keyFn = cata $ fooAlgebra valueAlgebra keyFn 

fooAlgebra 
    :: Ord k => 
    (ListF t a -> a) -> (t -> k) -> ListF t (M.Map k a) -> M.Map k a 
fooAlgebra valueAlgebra keyFn = \case 
    Nil -> M.empty 
    Cons elt acc -> M.alter 
     (Just . (valueAlgebra . Cons elt) . fromMaybe (valueAlgebra Nil)) 
     (keyFn elt) 
     acc 

사용. 코드 흉내가 http://ramdajs.com/docs/#reduceBy

더 좋은 방법은 reduceBy을 사용하여 recursion-schemes을 사용하고 있습니까? alter 인수가 약해 보입니다. cata 정말 거기에 있습니까? 어떤 것들은 anacata으로 구현 가능하다고 들었습니다.

+0

당신은 목록의지도를 얻기 위해 catamorphism을 사용할 수있는 것처럼 보입니다. 그런 다음 각 그룹에 대해 catamorphism (실제로는 fold)을'fmap'합니다. – danidiaz

+0

'valueAlgebra'를 적용하는 좀 더 모듈화 된 방법? 좋은 생각 같아. 이제 대수학을 교회에 부호화 된 버전을 받아들이는'alter'에 전달합니다. 그리고 디코딩은 고통 스럽습니다. – nponeccop

답변

2

나는 당신의 접근 방식에 이상이없는 것으로 보입니다. alter에 대한 논의는보기에 ​​너무 기분이 좋지 않습니다. 그러나 그것은 대부분은 두드러기입니다. alter은 사용하기에 조금 어색합니다. 맵에서 요소를 제거 할 필요가 없기 때문에, 당신이 또는 개선을 찾을 수 없습니다 수있는 ... ... alter보다는 insertWith를 사용

fooAlgebra 
    :: Ord k => 
    (ListF t a -> a) -> (t -> k) -> ListF t (M.Map k a) -> M.Map k a 
fooAlgebra valueAlgebra keyFn = \case 
    Nil -> M.empty 
    Cons elt acc -> M.insertWith 
     (\_ grpAcc -> valueAlgebra (Cons elt grpAcc)) 
     (keyFn elt) 
     (valueAlgebra (Cons elt (valueAlgebra Nil))) 
     acc 

fooAlgebra를 다시 작성하는 것이 가능하다.

변형 률을 사용하는 경우 원래 구조를 파괴하여 요소의 그룹 별 요약을 생성하므로 자연스러운 일처럼 느껴집니다. (keyFn이 상수 함수 인 경우 valueAlgebra을 가진 모든 요소의 평범한 구배가 reduceBy이됩니다.) danidiaz가 제안한 리팩토링 (즉, valueAlgebra 대립성을 그룹화와 분리하면)은 분명히 더 분명 :

reduceBy valueAlgebra keyFn = 
    fmap (cata valueAlgebra) . cata (groupAlgebra keyFn) 

groupAlgebra 
    :: Ord k => (t -> k) -> ListF t (M.Map k [t]) -> M.Map k [t] 
groupAlgebra keyFn = \case 
    Nil -> M.empty 
    Cons elt acc -> M.alter 
     (Just . (elt :) . fromMaybe []) 
     (keyFn elt) 
     acc 
2

지금까지 모든 조언에 따라 내 자신의 시도 :

type ListAlgebra a b = ListF a b -> b 

reduceBy :: Ord k => ListAlgebra t b -> (t -> k) -> [t] -> M.Map k b 
reduceBy valueAlgebra keyFn x = cata valueAlgebra <$> cata groupAlgebra x where 
    groupAlgebra = \case 
     Nil -> M.empty 
     Cons elt acc -> M.alter (Just . maybe [elt] (elt:)) (keyFn elt) acc 
공격의 또 다른 방향 keyFngroupAlgebra에서 고려 될 수 있음을 알 것입니다

, 그래서된다 210. 이 형태는 다소 이국적인이기는하지만, 정확히 embed입니다 :

newtype XMap k v = XMap { unXMap :: M.Map k [v] } 
type instance Base (XMap k v) = ListF (k, v) 
instance Ord k => Corecursive (XMap k v) where 
    embed = \case 
     Nil -> XMap M.empty 
     Cons (key,elt) acc -> XMap $ M.alter (Just . maybe [elt] (elt:)) key $ unXMap acc 

없음 fixpoints이 인스턴스의 작성 중에 피해를하지 않았다. 접근 방식이 완전히 모듈 식이다

reduceBy :: Ord k => ListAlgebra t b -> (t -> k) -> [t] -> M.Map k b 
reduceBy valueAlgebra keyFn = 
    fmap (cata valueAlgebra) . unXMap . refix . map (keyFn &&& id) 

주 : 우리는 reduceBy 지금 refix "캐스트"(A (Co)recursive 인스턴스로부터 대수 및 coalgebra를 얻을 수 hylomorphism)로 구성 할 수 있습니다 쉽게 독립적으로 분리하는 기능을 찢어 수 있습니다 결합자를 사용하고 목록을 소비하는 대신 아나모 피즘 및 기타 전개를 사용하여 유연하게지도를 구성 할 수 있습니다.