我一直在尝试创建一个haskell函数,它获取列表中最大的奇数。听起来很简单,我确信我能做到,但看起来我已经卡住了!这是我的代码,到目前为止它返回列表中的最大数字,包括奇数和偶数:
maximum :: (Ord a) => [a] -> a
maximum [x] = x
maximum (x:xs) = max x (maximum xs)
提前感谢您的帮助!
答案 0 :(得分:2)
首先 - 如果你的列表中只有偶数元素就会出现问题,因为你的函数不是全部(即产生错误) - 因此最好修改结果的类型来解释可能性:
正如其他人已经提到的那样 - odd
要求您的约束为Integral
而不是Ord
。
maximodd :: Integral a => [a] -> Maybe a
maximodd xs = case filter odd xs' of
[] -> Nothing
xs' -> Just (maximum xs')
函数filter odd
删除所有非奇数元素(即所有偶数) - 然后我们对结果进行模式匹配以解决失败。
如果您想使用原始功能,那么代码会变得更加棘手:
maximum :: Integral a => [a] -> a
maximum [x] = x
maximum (x:xs) = let y = maximum xs
in if x >= y && odd x
then x
else if x < y && odd y
then y
else ??
嗯 - 我认为你不能让它像这样工作 - 但也许有辅助功能。
maximum :: Integral a => [a] -> Maybe a
maximum xs = maximum' Nothing xs
where maximum' Nothing (x:xs) = if odd x
then maximum' (Just x) xs
else maximum' Nothing xs
maximum' (Just y) (x:xs) = if odd x
then maximum' (Just $ max x y) xs
else maximum' (Just y) xs
maximum' x _ = x
这也更复杂 - 而且haskell绝对不是因为复杂而闻名;)
答案 1 :(得分:2)
maximumOdd :: Integral a => [a] -> Maybe a
maximumOdd = foldl' go Nothing
where
go Nothing x | odd x = Just x
go (Just gr) x | odd x && x > gr = Just x
go acc _ = acc
答案 2 :(得分:1)
有几个选项可以教你一些语言。 首先, epsilonhalbe 提到的整体性问题。现在,我只会让代码抛出错误。
maximumOddError :: a
maximumOddError = error "maximumOdd: list contains no odd element"
最重要的复杂因素是,当你开始时,你根本不知道是否会有任何奇数。因此,您必须推迟该测试(我们将会这样做)或过滤并一次性检查。
最简单的解决方案是同时进行两项检查。通常,这也是一种很好的优化技术。目前,它将错误处理和迭代分开。
maximumOdd :: (Integral a) => [a] -> a
maximumOdd ls = case dropWhile even ls of
[] -> maximumOddError
n:ns -> findOddMax n ns -- n is definitely odd
where
findOddMax currentMax [] = currentMax
findOddMax currentMax (x:xs)
| even x = findOddMax currentMax xs
| x < currentMax = findOddMax currentMax xs
| otherwise = findOddMax x xs
不是看每一件作品,你也可以采取高层次的观点。为什么要搜索,何时只需对列表进行排序并将搜索的元素放在一端?当然你必须仍然过滤,所以你想要的基本上是last . sort . filter odd
。但是last
是另一次遍历,所以最好按降序对列表进行排序:
import Data.List ( sortBy )
maximumOdd :: (Integral a) => [a] -> a
maximumOdd = head . sortBy reverseOrder . filter odd
where
reverseOrder a b = compare b a
请注意head
在空列表中失败。所以错误处理并不好。但它应该很容易添加。与简单的maximum . filter odd
相比,性能有何影响?你告诉我!
现在让我们通过Maybe
添加一些更好的错误处理。最简单的版本是尾递归,因为我们分离了错误处理和迭代。
maximumOdd :: (Integral a) => [a] -> Maybe a
maximumOdd ls = case dropWhile even ls of
[] -> Nothing
n:ns -> Just $ findOddMax n ns -- n is definitely odd
where
findOddMax … -- same as above
这与 dfeuer 的方法非常相似。他只是使用了更多的现有函数来使其更短,更高效,并且他将错误处理集成到递归中。这是最有经验的Haskell程序员将要做的事情。 (你可以争论包装和展开的性能成本,但是呃。当你需要它时这样做。)
这个也很容易,因为head
的一个很好的版本完全符合我们的需要。
import Data.Maybe ( listToMaybe )
maximumOdd :: (Integral a) => [a] -> Maybe a
maximumOdd = listToMaybe . sortBy reverseOrder . filter odd
where
reverseOrder a b = compare b a
最后,我们回到最初的方法。但是Maybe
现在允许我们推迟错误检查。问题是:您需要了解一些特殊工具,或者您必须发明它们。我只是使用现有的。
import Control.Applicative ( (<|>) )
maximumOdd :: (Integral a) => [a] -> Maybe a
maximumOdd [] = Nothing
maximumOdd [x] | even x = Nothing
| otherwise = Just x
maximumOdd (x:xs) | even x = maximumOdd xs
| otherwise = max x <$> maximumOdd xs -- either "max x" applied inside the Maybe-construct
<|> Just x -- or x, if that produced an error