Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/postgresql/10.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Haskell:使用幺半群和可折叠_Haskell - Fatal编程技术网

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)
@AdamSmith
fmap
将作用于列表元素,不在列表中。@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