F# 整数比较

3

给定一个整数列表从-273到5526,我想打印最接近零的整数。如果有相等的情况(n和-n),我们应该选择n。

let temps = // this contains => 1 -2 -8 4 5

let (|Greater|_|) a b = if a > b then Some() else None
let (|Smaller|_|) a b = if a < b then Some() else None

let compareTemperatures a b = 
   let distanceA = abs a 
   let distanceB = abs b
   match distanceA with
   | Greater distanceB -> b
   | Smaller distanceB -> a
   | _ -> abs a

printfn "%i" (temps |> Seq.reduce compareTemperatures)

我翻译一下,这段代码返回的是-8而不是1。我认为看起来是正确的,但是由于我刚学习F#可能在任何地方都有错误,我找不到错误的位置:(

提前感谢您的帮助。


你没有完成关于相等性的句子。我猜想如果你有(-n和+n),你希望它打印出+n。但是如果你有(-n和-n),你希望它打印出-n还是+n? - Dax Fohl
确实,我会更新这个。不知道发生了什么,我确定它应该在这里。 - Cedric Royer-Bertrand
3个回答

10

我认为你把比较反了 - 当你写:

match distanceA with
| Greater distanceB -> b
| Smaller distanceB -> a

然后将distanceA作为第二个参数传递给Greater,因此在b距离零更远的情况下返回b(在第一种情况下)。以下代码可以使其正常工作:

match distanceA with
| Greater distanceB -> a
| Smaller distanceB -> b

话虽如此,对于这个问题使用活动模式只会让代码变得不必要的复杂(还会容易引入这种错误)。以下代码实现相同效果,易于理解且更简单:
let compareTemperatures a b = 
  if abs a > abs b then b else a

temps |> Seq.reduce compareTemperatures

我认为这里的教训是,模式匹配对于代数数据类型和可选值等事物非常有效,但对于简单的数值比较而言,它并不是那么有用,if 语句可以很好地完成工作!


这确实是比赛中的交换。我不确定如何读取它,如果(对于第一个匹配i我的错误解决方案)=> DistanceA大于DistanceB,则返回a? - Cedric Royer-Bertrand
我添加另一个评论,因为在您的if else解决方案中,您没有考虑默认情况,当您有-n和n时,我们应该返回n,因此您需要添加另一个if else,并且它变得不那么易读。但仍然比Active Pattern好吧? - Cedric Royer-Bertrand

2

所以您想比较值,首先使用绝对值,然后使用符号。这是我的一行代码:

temps |> Seq.minBy (fun x -> abs x, -sign x)

测试用例(输出2):

let li = [-2; 2; -2; 3; -5]
li |> Seq.minBy (fun x -> abs x, -sign x) |> printfn "%d"

这段代码很简短。我现在无法测试,因为我正在工作,但是如果您只有负值(比如在测试列表中添加-1),这段代码是否有效?我想知道它是否总是返回正值。(抱歉,我还是F#的新手,即使我有一个好猜测,也不知道-sign函数) - Cedric Royer-Bertrand
是的。在比较数字时,它会创建元组并进行比较。例如,3 变成 (3, -1),而 -5 变成 (5, 1)。如果元组中的第一个元素相等,则会比较第二个元素。在 sign 前面有 -,这样正数就得到 -1,负数就得到 1。该函数将返回具有最小元组的数字。 - Laurent
1
如果简洁是目标,这里并不严格需要 sign。只有 -x 就足够了。 - Dax Fohl

1
这应该是对Thomas回答的评论,但是我太蠢了,无法在移动应用上进行评论...
如果abs(2*a-1) > abs(2*b),那么选b,否则选a,您觉得怎么样?

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