如何在Haskell中返回中间数字

4
我有以下函数的开头,不确定该如何返回中间数字(即既不是最大值也不是最小值的数字):
middleNumber :: Int -> Int -> Int -> Int
middleNumber a b c
    | ...
6个回答

4
“中间数”比其中一个数字大但比另一个数字小,且只有一个中间数。最机械的解决方法是从起点开始。”
middleNumber a b c
    | a < b && a > c = a

检查 a 是否为中间数,即小于 b 但大于 c

现在如果 a 是中间数,但实际上它比 b 大且比 c 小怎么办?这里有另一个保护条件。如果 b 是中间数呢?还有另外两个保护条件。如果 c 是中间数呢?那就有了总共6种不同的情况需要考虑。

(顺便说一下,表达式 | a < b && a > c = a 被称为保护条件。如果您还没有对保护条件有很好的掌握,我建议您看看 LYAH # Guards)

当然,有更好的编写函数的方法,但为了理解目的,手动系统地分解所有可能的情况并确定在每种情况下该做什么是很好的。 如何设计程序 是学习如何以这种方式系统化的好书。


4

我建议您将该函数分为两个步骤:首先对三个数字进行排序,然后取中间元素。对于第一步,还要考虑是否可以逐步进行;每一步都使其更接近完全排序,然后进行尾递归以使其更加接近。


3
必要的鲁伯·戈尔德堡式答案:
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)] 

我相信我们可以将这个转换为箭头语法来进一步混淆,但这个任务留给感兴趣的读者。


1
import Data.List; middleNumber a b c = (sort [a,b,c]) !! 1 - Tim Perry
让l = [a,b,c],然后删除(l中的最小值) . 删除(l中的最大值) $ l - sclv
另一个例子:middleNumber a b c = minimum [max a b, max a c, max b c](反过来也成立) - sclv
@所有人:太好了!我认为玩弄语言是更好地掌握它的好方法,而教科书上的答案只是无聊的。 - Landei

1

在丹·伯顿(Dan Burton)的答案上,通过使用保护条件来扩展,我对a、b、c三种情况分别进行了评估。但是,如果其中2个数字相等会发生什么?那么中间的数字应该是其中一个重复的数字。

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

1

您可以利用守卫和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

0

我使用了一个快速的暴力方法,但这绝对不是最好的解决方案

import Data.List
middleNum :: Int -> Int -> Int -> Int
middleNum a b c = (\[_,m,_] -> m) $ sort $ a:b:c:[]

显然,这是一个糟糕的想法,因为它明确依赖于列表中有3个项目,但它能够完成任务。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接