有maximumWith之类的东西吗?

时间:2018-10-09 10:59:29

标签: sorting haskell foldable

具体地说,我正在搜索函数'maximumWith',

maximumWith :: (Foldable f, Ord b) => (a -> b) -> f a -> a

以下行为:

maximumWith length [[1, 2], [0, 1, 3]] == [0, 1, 3]
maximumWith null [[(+), (*)], []] == []
maximumWith (const True) x == head x

我的用例是选择列表中最长的单词。
为此,我想要类似maximumWith length的东西。

我以为存在这样的事情,因为sortWith等存在。

2 个答案:

答案 0 :(得分:4)

让我一起收集评论中的所有注释...

让我们看一下sort。该系列有4个功能:

  • sortBy是实际的实现。
  • sort = sortBy compare使用Ord重载。
  • sortWith = sortBy . comparing与您所需的maximumWith类似。但是,此功能有问题。元素的排名是通过将给定的映射函数应用于元素来进行的。但是,排名不会被记录下来,因此如果一个元素需要多次比较,排名将被重新计算。如果排名功能非常便宜,则只能无罪地使用。此类函数包括选择器(例如fst)和newtype构造函数。 YMMV在简单的算术和数据构造函数上。在效率低下,定义的简单性及其在GHC.Exts中的位置之间,很容易推断出它的使用频率不高。
  • sortOn通过在排名功能下成对装饰每个元素的图像,按等级排序然后删除它们来解决效率低下的问题。

前两个在maximum中具有类似物:maximumBymaximumsortWith没有比喻;您最好每次都写出maximumBy (comparing _)。也没有maximumOn,即使这样会更有效。定义maximumOn的最简单方法可能就是复制sortOn

maximumOn :: (Functor f, Foldable f, Ord r) => (a -> r) -> f a -> a
maximumOn rank = snd . maximumBy (comparing fst) . fmap annotate
  where annotate e = let r = rank e in r `seq` (r, e)

maximumBy中有一些有趣的代码,无法在列表上进行适当的优化。也可以使用

maximumOn :: (Foldable f, Ord r) => (a -> r) -> f a -> a
maximumOn rank = snd . fromJust . foldl' max' Nothing
    where max' Nothing x = let r = rank x in r `seq` Just (r, x)
          max' old@(Just (ro, xo)) xn = let rn = rank xn
                                         in case ro `compare` rn of
                                                 LT -> Just (rn, xo)
                                                 _ -> old

这些实用指示可能有用:

{-# SPECIALIZE maximumOn :: Ord r => (a -> r) -> [a] -> a #-}
{-# SPECIALIZE maximumOn :: (a -> Int) -> [a] -> a #-}

答案 1 :(得分:2)

HTNW已经解释了如何执行您所要求的操作,但是我想我应该提到的是,对于您提到的特定应用程序,在某些情况下有一种更有效的方法(假设单词由String表示) 。假设你要

longest :: [[a]] -> [a]

如果您要求maximumOn length [replicate (10^9) (), []],那么最终将不必要地计算一个很长的列表的长度。有几种方法可以解决此问题,但是这是我的处理方法:

data MS a = MS
  { _longest :: [a]
  , _longest_suffix :: [a]
  , _longest_bound :: !Int }

我们将确保longest是迄今为止看到的最长的字符串中的第一个,并且确保longest_bound + length longest_suffix = length longest

step :: MS a -> [a] -> MS a
step (MS longest longest_suffix longest_bound) xs =
    go longest_bound longest_suffix xs'
  where
    -- the new list is not longer
    go n suffo [] = MS longest suffo n
    -- the new list is longer
    go n [] suffn = MS xs suffn n
    -- don't know yet
    go !n (_ : suffo) (_ : suffn) =
      go (n + 1) suffo suffn

    xs' = drop longest_bound xs

longest :: [[a]] -> [a]
longest = _longest . foldl' step (MS [] [] 0)

现在,如果倒数第二个最长的列表包含q个元素,我们将最多q个约束进入每个列表。这是最大可能的复杂性。当然,当最长的列表比第二长的列表长得多时,它仅比maximumOn解决方案好得多。