【发布时间】:2023-03-17 02:27:01
【问题描述】:
我有一个函数的以下开头,我不确定我应该如何返回中间数(即既不是最大也不是最小的数字):
middleNumber :: Int -> Int -> Int -> Int
middleNumber a b c
| ...
【问题讨论】:
我有一个函数的以下开头,我不确定我应该如何返回中间数(即既不是最大也不是最小的数字):
middleNumber :: Int -> Int -> Int -> Int
middleNumber a b c
| ...
【问题讨论】:
我建议您将函数分为两个步骤:首先,对三个数字进行排序。然后,取中间元素。第一步,还要考虑是否可以一步一步来;每一步都使它更接近完全排序,然后尾递归返回以使其更接近。
【讨论】:
“中间数字”大于其中一个数字,但小于另一个数字。而且只有一个中间数。解决这个问题最机械的方法是开始
middleNumber a b c
| a < b && a > c = a
通过小于b 但大于c 来检查a 是否是中间数。
现在如果a 是中间数,但它实际上大于 比b 并且小于 比c?还有一个守卫。如果b 是中间数字怎么办?还有2个守卫。如果c 是中间数字怎么办?还有 2 名警卫,总共有 6 种不同的情况。
(顺便说一句,| a < b && a > c = a这个表达式被称为守卫。如果你还没有牢牢掌握守卫是什么,那么我推荐LYAH # Guards)
当然有更好的方法来编写函数,但为了便于理解,最好能够手动系统地分解所有可能的情况,并确定在每种情况下要做什么。 How To Design Programs 是一本学习如何以这种方式系统化的好书。
【讨论】:
必选的 Rube-Goldberg-answer:
import Control.Applicative
middleNumber a b c = sum $ [sum, negate.minimum, negate.maximum] <*> [[a,b,c]]
[编辑]
这是另一个版本:
middleNumber a b c = fst $ maximumBy (compare `on` abs.snd) [(a,b-c),(b,c-a),(c,a-b)]
我确信我们可以将其转换为箭头语法以进一步混淆,但我将这项任务留给感兴趣的读者。
【讨论】:
let l = [a,b,c] in delete (minimum l) . delete (maximum l) $ l ... :-)
middleNumber a b c = minimum [max a b, max a c, max b c](反之亦然)
我做了一个快速的蛮力方法,但这肯定不是最好的解决方案
import Data.List
middleNum :: Int -> Int -> Int -> Int
middleNum a b c = (\[_,m,_] -> m) $ sort $ a:b:c:[]
显然这是一个糟糕的想法,因为它明确依赖于列表中有 3 个项目,但它确实可以完成工作
【讨论】:
扩展 Dan Burton 对警卫的回答,我评估了 a、b、c 案例,每个案例都有自己的警卫。但是,当两个数字相等时会发生什么?那么中间的数字应该是重复的数字之一。
middleNumber :: Int -> Int -> Int -> Int
middleNumber a b c
| (a > b && a < c) || (a > c && a < b) = a
| (b > a && b < c) || (b > c && b < a) = b
| (c > a && c < b) || (c > b && c < a) = c
| otherwise = if a == b then a else c
【讨论】:
您可以利用警卫和where 以简单的方式获得相同的结果:
middleNumber :: Int -> Int -> Int -> Int
middleNumber x y z
| a == x = max y z
| a == y = max x z
| a == z = max x y
where
a = max x $ max y z
如果您无权访问内置的max。您可以轻松编写自己的代码。
max' :: Int -> Int -> Int
max' x y
| x > y = x
| otherwise = y
【讨论】: