我有以下使用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]。代码模仿http://ramdajs.com/docs/#reduceBy
有没有更好的使用recursion-schemes实现reduceBy的方法?alter的论点看起来很脆弱,那么cata真的适合这里吗?我听说有些东西既可以用ana实现,也可以用cata实现。
发布于 2017-02-11 02:50:17
我看不出你的方法有什么问题。alter的参数看起来不太令人愉快,但这主要是信标alter使用起来有点笨拙。由于您不需要从映射中删除元素,因此可以使用insertWith而不是alter重写fooAlgebra……
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发布于 2017-02-11 08:21:40
基于到目前为止所有的建议,我自己的尝试:
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现在可以用refix“强制转换”(一种从(Co)recursive实例中获得其代数和余代数的亚纯)来构造:
reduceBy :: Ord k => ListAlgebra t b -> (t -> k) -> [t] -> M.Map k b
reduceBy valueAlgebra keyFn =
fmap (cata valueAlgebra) . unXMap . refix . map (keyFn &&& id)请注意,这种方法是完全模块化的:您可以很容易地将函数拆分成独立的组合子,也可以使用变形和其他展开灵活地构造映射,而不仅仅是使用列表。
https://stackoverflow.com/questions/42148749
复制相似问题