Haskell的见解

fs.*_*fs. 5 haskell

我发现自己非常需要你的见解.

这是我感兴趣的对象:

class Mergable m where
    merge :: m -> m -> Maybe m
    mergeList :: [m] -> [m]

    mergeList [] = []
    mergeList [x] = [x]
    mergeList (x:y:t) = r1 ++ mergeList (r2 ++ t)
        where
            (r1,r2) = case (x `merge` y) of
                Just m  -> ([ ], [m])
                Nothing -> ([x], [y])
Run Code Online (Sandbox Code Playgroud)

但是我稍后会回来.现在我准备了一些例子:

data AffineTransform = Identity
                     | Translation Float Float
                     | Rotation Float
                     | Scaling Float Float
                     | Affine Matrix3x3

instance Monoid AffineTransform where
    mempty = Identity

    Identity `mappend` x = x
    x `mappend` Identity = x
    (Translation dx1 dy1) `mappend` (Translation dx2 dy2) = Translation (dx1+dx2) (dy1+dy2)
    (Rotation theta1) `mappend` (Rotation theta2) = Rotation (theta1+theta2)
    (Scaling sx1 sy1) `mappend` (Scaling sx2 sy2) = Scaling (sx1*sx2) (sy1*sy2)

    -- last resort: compose transforms from different subgroups 
    -- using an "expensive" matrix multiplication
    x `mappend` y = Affine (toMatrix x `mult3x3` toMatrix y) 
Run Code Online (Sandbox Code Playgroud)

所以现在我能做到:

toMatrix $ Rotation theta1 `mappend` Translation dx1 dy1 `mappend` Translation dx2 dy2 `mappend` Rotation theta2
Run Code Online (Sandbox Code Playgroud)

或者更简单地说:

(toMatrix . mconcat) [Rotation theta1, Translation dx1 dy1, Translation dx2 dy2, Rotation theta2]
Run Code Online (Sandbox Code Playgroud)

或更一般地说:

(toMatrix . (fold[r|r'|l|l'] mappend)) [Rotatio...], etc
Run Code Online (Sandbox Code Playgroud)

在上面的例子中,第一次旋转和平移将(昂贵地)组合到矩阵中; 然后,该矩阵与翻译相结合(也使用乘法),然后再次乘法将用于产生最终结果,即使(由于关联性)中间的两个平移可以廉价地组合,总共两次乘法三个.

无论如何,我的Mergable课程一直在拯救:

instance Mergable AffineTransform where
    x `merge` Identity = Just x
    Identity `merge` x = Just x
    x@(Translation _ _) `merge` y@(Translation _ _) = Just $ x `mappend` y
    x@(Rotation _) `merge` y@(Rotation _) = Just $ x `mappend` y
    x@(Scaling _ _) `merge` y@(Scaling _ _) = Just $ x `mappend` y
    _ `merge` _ = Nothing
Run Code Online (Sandbox Code Playgroud)

所以现在(toMatrix.mconcat.合并列表)〜(toMatrix.mconcat),因为它应该:

mergeList [Rotation theta1, Translation dx1 dy1, Translation dx2 dy2, Rotation theta2] == [Rotation theta1, Translation (dx1+dx2) (dy1+dy2), Rotation theta2]
Run Code Online (Sandbox Code Playgroud)

我想到的其他例子更多(代码方面),所以我只是陈述想法.

比方说我有一些

data Message = ...
Run Code Online (Sandbox Code Playgroud)

和a

dispatch :: [Message] -> IO a
Run Code Online (Sandbox Code Playgroud)

其中dispatch从列表中获取消息,具体取决于它的类型打开适当的通道(文件,流等),写入该消息,关闭通道并继续下一条消息.因此,如果打开和关闭通道是一项"昂贵"的操作,那么简单地编写(dispatch.合并列表)可以帮助您以最小的努力提高性能.

其他时候我用它来处理gui应用程序中的事件,比如合并鼠标移动,按键,undo-redo系统中的命令等.

一般模式是我从列表中取两个项目,检查它们是否以某种方式"合并",如果是这样,尝试将结果与列表中的下一个项目合并,否则我将第一个项目保留原样并继续下一对(现在我认为它有点像广义的运行长度编码)

我的问题是我无法摆脱我重新发明轮子的感觉,并且必须在haskell中使用类似的结构.如果不是这样的话:

1)如何将其推广到列表以外的其他容器?2)你能发现任何其他结构Mergable是一个实例吗?(特别是Arrows,如果适用的话,我很难绕过它们)3)有关mergeList的严格/懒惰的任何见解以及如何将它呈现给用户?4)优化提示?堆栈溢出?还要别的吗?

谢谢!

dav*_*420 2

我认为图书馆里还没有这样的东西。胡格尔和哈尤没有找到任何合适的东西。

Mergeable(我认为它是这样拼写的)看起来像是 的概括Monoid。不是Arrow,抱歉。

有时您需要合并并保留顺序。有时合并时不需要保留顺序。

我可能会做类似的事情

newtype MergedInOrder a = MergedInOrder [a] -- without exporting the constructor

mergeInOrder :: Mergeable a => [a] -> MergedInOrder a
mergeInOrder = MergedInOrder . foldr f []
  where f x []            = [x]
        f x xs @ (y : ys) = case merge x y of
                                 Just z  -> z : ys
                                 Nothing -> x : xs
Run Code Online (Sandbox Code Playgroud)

以及无序列表的类似新类型,它们分别利用实例和不需要Ord实例。

这些新类型都有明显的Monoid例子。

我不认为我们可以编写代码来合并Mergeables 的任意容器,我认为必须为每个容器显式完成。