4

我有一个函数的以下开头,并且不确定我应该如何返回中间数(即既不是最大也不是最小的数字):

middleNumber :: Int -> Int -> Int -> Int
middleNumber a b c
    | ...
4

6 回答 6

4

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

于 2011-05-24T20:44:29.450 回答
4

“中间数”大于其中一个数,但小于另一个数。而且只有一个中间数。解决这个问题的最机械的方法是开始

middleNumber a b c
    | a < b && a > c = a

a通过小于b但大于来检查是否是中间数c

现在如果是中间数a,但实际上大于b和小于c该怎么办?还有一个守卫。如果b是中间数呢?还有2个守卫。如果c是中间数呢?还有 2 名警卫,总共 6 种不同的情况。

(顺便说一句,这个表达| a < b && a > c = a被称为守卫。如果你还没有牢牢掌握守卫是什么,那么我推荐LYAH #Guards

当然有更好的方法来编写函数,但为了理解目的,最好能够手动和系统地分解所有可能的情况,并确定在每种情况下要做什么。How To Design Programs是一本很好的书,用于学习如何以这种方式进行系统化。

于 2011-05-24T21:54:59.553 回答
3

强制性的 Rube-Goldberg 答案:

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)] 

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

于 2011-05-24T22:08:04.960 回答
1

扩展 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
于 2021-01-12T20:44:28.373 回答
0

我做了一个快速的蛮力方法,但这肯定不是最好的解决方案

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

显然这是一个糟糕的主意,因为它明确依赖于列表中有 3 个项目,但它确实完成了工作

于 2011-05-25T07:35:36.520 回答
0

您可以利用警卫并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
于 2021-05-03T15:25:12.907 回答