我刚刚在F#中发现了一些我称之为怪癖的东西,并且想知道它是设计还是错误,如果它是设计的,为什么会这样...... 如果您编写任何范围表达式,其中第一个术语大于第二个术语,则返回的序列为空。看看反射器表明这是设计的,但我无法找到它必须如此的原因。 重现它的一个例子是:
[1..10] |> List.length
[10..1] |> List.length
第一个将打印出10,而第二个将打印出0。 测试在F#CTP 1.9.6.2中进行。
编辑:感谢您建议明确该范围,但仍有一个案例(这是启发我提出这个问题的原因)将不会被涵盖。如果A和B是变量并且没有一个比其他变量更大,尽管它们总是不同的怎么办? 考虑到范围表达式似乎在编译时似乎没有得到优化,在A和B不是允许否定步骤的情况下,是否有任何充分的理由来确定步骤(未明确指定)?
答案 0 :(得分:12)
正如其他答案所示,你可以做到
[10 .. -1 .. 1] |> List.iter (printfn "%A")
e.g。
[start .. step .. stop]
答案 1 :(得分:3)
“应该”发生的事情当然是主观的。我脑海中的正常范围表示法将[x..y]定义为大于或等于x AND且小于或等于y的所有元素的集合;如果y <&lt; X。在这种情况下,我们需要诉诸F#规范。
范围表达式expr1 .. expr2被计算为对重载运算符(..)的调用,其默认绑定在Microsoft.FSharp.Core.Operators中定义。这将生成IEnumerable&lt; _&gt;对于给定的start(expr1)和finish(expr2)值之间的值范围,使用增量1.运算符要求在静态类型expr1上存在静态成员(..)(长名称GetRange)适当的签名。
范围表达式expr1 .. expr2 .. expr3被计算为对重载运算符(.. ..)的调用,其默认绑定在Microsoft.FSharp.Core.Operators中定义。这将生成IEnumerable&lt; _&gt;对于给定的start(expr1)和finish(expr3)值之间的值范围,使用expr2的增量。运算符要求在具有适当签名的静态类型expr1上存在静态成员(..)(长名称GetRange)。
标准似乎没有定义..运算符(至少,我能找到)。但是你应该能够以你喜欢的任何方式更改你感兴趣的类型的绑定(包括x> y时倒计时)。
答案 2 :(得分:3)
亚当·赖特 - 但你应该可以 更改类型的绑定 有兴趣以任何方式表现你 喜欢(包括如果x&gt;倒计时) Y)。
将Adam的建议纳入代码:
let (..) a b =
if a < b then seq { a .. b }
else seq { a .. -1 .. b }
printfn "%A" (seq { 1 .. 10 })
printfn "%A" (seq { 10 .. 1 })
这适用于int范围。看看(..)的源代码:您可以使用它来处理其他类型的范围,但不确定如何为特定类型获得正确的-1值。
答案 3 :(得分:2)
在haskell中,你可以写[10, 9 .. 1]
。也许它在F#中的作用相同(我还没试过)?
编辑:
似乎F#语法不同,可能类似于[10..-1..1]
答案 4 :(得分:1)
范围通常表示(在支持它们的语言和框架中),如下所示:
low_value <to> high_value
你能否提出一个很好的论据,为什么一个范围应该能够以不同的方式表达?既然你要求从较高数字到较低数字的范围,是不是因为结果范围没有成员呢?