Julia 是如何解释 10:1 的?

How does Julia interpret 10:1?

我长期专注于 R,其中 :(冒号)运算符创建从第一个参数到第二个参数的整数序列:

1:10
# [1]  1  2  3  4  5  6  7  8  9 10
10:1
# [1] 10  9  8  7  6  5  4  3  2  1

注意到这在 Julia 中似乎同样有效,我 运行 陷入了一个错误:

1:10 .== 10:1

DimensionMismatch("arrays could not be broadcast to a common size")

因为:

10:1

输出

10:9

我对这怎么会发生感到困惑。不需要使用 10:-1:1 似乎很自然——为什么 Julia 认为 10:9 是对 10:1 的正确解释?

在 Julia 中,我们假设 a:b 构造一个步长为 1 的范围,所以 10:1 是一个 UnitRange ,它应该是一个空范围。由于 a:a-1 也是一个空范围,因此它等效于 a:b 其中 b<a, 请查看源代码 here.

julia> dump(10:1)
UnitRange{Int64}
  start: Int64 10
  stop: Int64 9

julia> dump(10:-1:1)
StepRange{Int64,Int64}
  start: Int64 10
  step: Int64 -1
  stop: Int64 1

这里的10:-1:1是步长为-1的StepRange,我认为这样更准确自然地表达了“10比1”的思想。

Julia 不是 R。还有其他语言对冒号语法的解释与 Julia 相似。 MATLAB 将 10:1 视为空数组,Python 的切片语法(虽然在其他方面有所不同)也将使用 10:1 的索引视为空选择。 Julia 选择规范化空整数范围,使得开始和停止之间的差异始终为 -1,因此它变为 10:9

所以我不认为 10:1 有明确的解释。然而,在我看来,有一些非常有利于 Julia 解释的论据:

  • 空范围10:9用于表示some APIs中索引9和10之间的位置。

  • 范围是 Julia 中的核心构造,for x in 1:10 绝对且明确地必须与等效的 C 循环一样快。因为语法 x:y 总是递增 1(从不递减 1),Julia(和 LLVM)在编译 for 循环时可以利用该常量来实现进一步的优化。使这不是常量——或者更糟的是,根据端点的值在 UnitRangeStepRange 之间动态切换会阻碍这种优化或类型不稳定。

  • 就个人而言,我发现 R 的解释与您发现 Julia 的一样令人惊讶。我认为需要明确表示您想要 -1 的步骤在可读性和错误预防方面都是有利的。但我承认我对先前语言的体验与你的一样有偏见。