Haskell:使用幺半群和可折叠
我正在尝试使用Haskell:使用幺半群和可折叠,haskell,Haskell,我正在尝试使用Monoid和Foldable实现排序。这就是我目前所拥有的。真的很慢。然而,当我在没有Monoid或Foldable的情况下编写相同的函数时,速度相当快。任何关于我在这里做错了什么的指点都将不胜感激 newtype MergeL a = MergeL { getMergeL :: [a] } deriving (Eq, Show) instance Ord a => Monoid (MergeL a) where mempty = MergeL [] m
Monoid
和Foldable
实现排序。这就是我目前所拥有的。真的很慢。然而,当我在没有Monoid
或Foldable
的情况下编写相同的函数时,速度相当快。任何关于我在这里做错了什么的指点都将不胜感激
newtype MergeL a = MergeL { getMergeL :: [a] } deriving (Eq, Show)
instance Ord a => Monoid (MergeL a) where
mempty = MergeL []
mappend l r = MergeL $ merge (getMergeL l) (getMergeL r)
comp :: a -> MergeL a
comp a = MergeL [a]
instance Foldable MergeL where
foldMap f xs =
case divide xs of
(MergeL [], MergeL []) -> mempty
(MergeL l , MergeL []) -> foldMap f l
(MergeL [], MergeL r) -> foldMap f r
(MergeL l , MergeL r) -> foldMap f l <> foldMap f r
divide :: MergeL a -> (MergeL a, MergeL a)
-- now uses leftHalf and rightHalf
divide xs = (MergeL $ leftHalf ls, MergeL $ rightHalf ls)
where
ls = getMergeL xs
foldSort :: (Ord a, Foldable t) => t a -> [a]
foldSort = getMergeL . foldMap comp
mon :: Integer -> IO ()
mon n = (print . last . getMergeL . foldMap comp) $ MergeL [n,n - 1 ..0]
性能上的差异是:
λ> mon 4000
4000
(2.20 secs, 1,328,105,368 bytes)
λ> plain 4000
4000
(0.03 secs, 11,130,816 bytes)
这里的主要问题很容易忽略(事实上,我忽略了它,直到我在
中输入了一个divide
)。您的foldMap
案例之一是:
(MergeL l , MergeL r) -> foldMap f l <> foldMap f r
这提供了可接受的性能——与未经优化的普通实现相比,具有相同的复杂性和数量级的计时,并且在进行优化时,具有大致相同的性能。(1)“但是,当我编写相同的函数时,如果没有
Monoid
或可折叠
,则速度相当快。”--我建议在您的问题中添加此替代实现。(2) 作为初步猜测,divide
看起来可能会引起麻烦:首先,length
后跟take
和drop
可能意味着列表在内存中的保存时间比您希望的要长,因为需要多次运行它们。(3) 请注意,take n
和drop n
的组合可以被splitAt n
所取代。一个简短的提示:看起来您正在从MergeL
中提取内容,但只是为了将其向后推很多次(例如,每次调用divide
)。这是一个很好的迹象,表明您希望为MergeL
定义一个Functor
实例,这样您就可以fmap length(xs::MergeL a)
divide
然后变成fmap(flip div 2.length&&id>>uncurry splitAt)
@AdamSmithfmap
将作用于列表元素,不在列表中。@duplode我在上面添加了mergesort
函数。我还重写了divide
以明确使用非幺半体mergesort
使用的相同leftHalf
和righhalf
函数。还请注意,splitAt
是在源代码中定义的splitAt nxs=(取nxs,删除nxs)
。我知道我在这里调用了两次length xs
,但我这样做只是为了表明这不是这里的主要问题。
λ> mon 4000
4000
(2.20 secs, 1,328,105,368 bytes)
λ> plain 4000
4000
(0.03 secs, 11,130,816 bytes)
(MergeL l , MergeL r) -> foldMap f l <> foldMap f r
instance Foldable MergeL where
foldMap f xs =
case divide xs of
(MergeL [], MergeL []) -> mempty
(ml, MergeL [y]) -> foldMap f ml <> f y
(MergeL [x], mr) -> f x <> foldMap f mr
(ml, MergeL []) -> foldMap f ml
(MergeL [], mr) -> foldMap f mr
(ml, mr) -> foldMap f ml <> foldMap f mr