是在编译时或运行时评估的F#范围

时间:2010-09-21 17:37:01

标签: f# range

F#中的(..)和(.. ..)运算符在某些时候展开,是编译时操作还是运行时操作?

在任何一种情况下,这有什么表现?即是否可以构建一个更快地完成这些操作的自定义函数?

2 个答案:

答案 0 :(得分:3)

运行时间。作为编译的一部分,F#很少会运行您的代码 - 我能想到的唯一情况是NumericLiteralX模块中的代码。此外,在这样的代码中:

let f n = [1 .. n]

在编译时甚至不知道上限。当然,作为一个实现细节,F#编译器可能显式地展开定义,其中两个边界都是已知类型的静态已知值(例如int),但在语义上它应该始终与在运行时完成的相同时间。

关于你的第二个问题,比什么更快?

答案 1 :(得分:3)

我认为来自 kvb 的回复回答了大多数问题。但是,我认为更准确的答案是范围在运行时懒惰地评估。以下是范围如何工作的更多细节......

当您在代码中的某处使用1 .. 10时,它只是转换为某个方法调用。调用取决于使用的上下文和数字类型。

  • 对于[ 1 .. 10 ]或其他序列表达式和for循环,编译器将生成类似RangeInt32(1, 1, 10)的内容(附加参数是步骤)。

  • 如果你有obj.[ 1 .. ]obj这样的东西是支持切片的对象(例如矩阵类型),那么它将被翻译为obj.GetSlice(Some(1), None)(请注意,在此如果上限/下限可能丢失了。

现在很容易回答你的问题 - 这是一个将在运行时进行评估的方法调用。然而,重要的是要注意整个范围可能不需要评估!例如:

let nums = seq { 1 .. 10 } |> Seq.take 1

序列表达式将转换为对RangeInt32的调用。这将只返回一个懒惰评估的seq<int>类型的值。对take 1的调用仅采用第一个元素,因此只需要该范围中的第一个数字并进行评估。

我认为您自己的范围实现可能与标准实现不同,但您可以将您的实现作为对象的成员提供。然后你可以写myObj.[1 .. 10](结果可以是你想要的任何类型)。为此,您需要一个实例方法GetSlice,其中更详细discussed here