【发布时间】:2021-12-23 18:30:08
【问题描述】:
我是 Haskell 的新手,我想从给定的 List 中提取最大元素,以便最终得到最大元素 x 和剩余列表 xs(不包含 x)。可以假设列表的元素是唯一的。
我要实现的函数类型有点像这样:
maxElement :: (Ord b) => (a -> b) -> [a] -> (a, [a])
值得注意的是,第一个参数是将元素转换为可比较形式的函数。此外,此函数是非全部的,因为如果给定一个空的 List,它会失败。
我当前的方法无法将剩余列表中的元素保留在适当的位置,这意味着给定[5, 2, 4, 6] 它返回(6, [2, 4, 5]) 而不是(6, [5, 2, 4])。此外,感觉应该有一个更好看的解决方案。
compareElement :: (Ord b) => (a -> b) -> a -> (b, (a, [a])) -> (b, (a, [a]))
compareElement p x (s, (t, ts))
| s' > s = (s', (x, t:ts))
| otherwise = (s, (t, x:ts))
where s' = p x
maxElement :: (Ord b) => (a -> b) -> [a] -> (a, [a])
maxElement p (t:ts) = snd . foldr (compareElement p) (p t, (t, [])) $ ts
更新
感谢@Ismor 的回答和@chi 评论的帮助,我更新了我的实现,我对结果感到满意。
maxElement :: (Ord b) => (a -> b) -> [a] -> Maybe (b, a, [a], [a])
maxElement p =
let
f x Nothing = Just (p x, x, [], [x])
f x (Just (s, m, xs, ys))
| s' > s = Just (s', x, ys, x:ys)
| otherwise = Just (s, m, x:xs, x:ys)
where s' = p x
in
foldr f Nothing
当给定列表为空时,结果要么是Nothing,要么是Maybe (_, x, xs, _)。我可以使用最初预期的类型编写另一个“包装器”函数并在后台调用maxElement,但我相信这也可以。
【问题讨论】:
-
如果有多个元素的最大值(例如
[2, 5, 6, 3, 2, 6, 1, 3])会发生什么? -
那么,只提取其中一个,是第一个还是最后一个都无所谓。
-
@MarkSeemann 你也可以假设没有重复。
-
let max = getMax $ foldMap Max xs :: Int in (max, filter (/= max) xs) -
我认为避免
foldr并通过直接递归进行会更简单。如果您确实想使用foldr,您可能应该考虑将您的 3 元组(b,a,[a])替换为 4 元组(b,a,[a],[a]),其中两个列表是 1) 已删除最大值的列表和 2) 未删除的列表任何事物。折叠后,您可以从 4 元组中提取所需的输出,丢弃一些组件。
标签: list haskell functional-programming