具体地说,我正在搜索函数'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
等存在。
答案 0 :(得分:4)
让我一起收集评论中的所有注释...
让我们看一下sort
。该系列有4个功能:
sortBy
是实际的实现。sort = sortBy compare
使用Ord
重载。sortWith = sortBy . comparing
与您所需的maximumWith
类似。但是,此功能有问题。元素的排名是通过将给定的映射函数应用于元素来进行的。但是,排名不会被记录下来,因此如果一个元素需要多次比较,排名将被重新计算。如果排名功能非常便宜,则只能无罪地使用。此类函数包括选择器(例如fst
)和newtype
构造函数。 YMMV在简单的算术和数据构造函数上。在效率低下,定义的简单性及其在GHC.Exts
中的位置之间,很容易推断出它的使用频率不高。sortOn
通过在排名功能下成对装饰每个元素的图像,按等级排序然后删除它们来解决效率低下的问题。前两个在maximum
中具有类似物:maximumBy
和maximum
。 sortWith
没有比喻;您最好每次都写出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
解决方案好得多。