使用递归方案在Haskell中使用RamdaJS reduceBy()

时间:2017-02-09 23:17:24

标签: haskell recursion-schemes

我使用findUserbyUsername库获得以下代码:

Service

用作when(service.findUserbyUsername(defaultName)).thenReturn(userStub); 。代码模仿http://ramdajs.com/docs/#reduceBy

使用recursion-schemes实施{-# 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 是否有更好的方法? let countBy = reduceBy (\case Nil -> 0 ; Cons a b -> succ b) id in countBy [42,5,5,8,8,8]论证看起来很脆弱,reduceBy真的适合吗?我听说有些事情可以实现为recursion-schemesalter

2 个答案:

答案 0 :(得分:2)

我认为你的做法没有任何问题。 alter的论点不太令人愉快,但是大多数beacues alter使用起来都有些笨拙。由于您不需要从地图中删除元素,因此可以使用fooAlgebra而不是insertWith重写alter ...

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

......您可能会或可能不会找到改进。

至于使用一个变形现象,感觉这是一件很自然的事情,因为你正在摧毁原始结构,以产生元素的分组总结。 (值得注意的是,如果keyFn是一个常数函数,那么reduceBy实际上变成了valueAlgebra所有元素的普通旧折叠。)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

答案 1 :(得分:2)

我自己的尝试基于目前的所有建议:

/usr/local/lib64

攻击的另一个方向是注意./configure可以考虑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 ,因此它变为keyFn。在这种形式中,它恰好是groupAlgebra,虽然有点异国情调:

groupAlgebra' :: ListAlgebra (k, v) (M.Map k [v])

在创建此实例期间,没有任何修复点受到损害。我们的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 “强制转换”构建(一个从reduceBy个实例中获取代数和代数的hylomorphism):

refix

请注意,该方法是完全模块化的:您可以轻松地将功能拆分为独立的组合器,并且还可以使用anorporphisms和其他展开灵活地构建地图,而不仅仅是消费列表。