OCaml:提取元组的第n个元素?

use*_*037 13 ocaml functional-programming

对于列表,您可以进行模式匹配并迭代直到第n个元素,但对于元组,您将如何获取第n个元素?

Mic*_*ald 11

TL; DR; 停止尝试直接访问t- try的第n个元素并使用记录或数组,因为它们允许随机访问.

您可以抓取Ñ由个元素拆包与值解构-uple,或者通过一个let构建体,match构建体或函数定义:

let ivuple = (5, 2, 1, 1)

let squared_sum_let =
  let (a,b,c,d) = ivuple in
  a*a + b*b + c*c + d*d

let squared_sum_match =
  match ivuple with (a,b,c,d) -> a*a + b*b + c*c + d*d

let squared_sum_fun (a,b,c,d) =
  a*a + b*b + c*c + d*d
Run Code Online (Sandbox Code Playgroud)

match-construct在这里有没有凭借在let-construct,它只是为了保持完整性的缘故.

不要使用牛逼 -uples,Don¹

只有少数情况下使用t -uples来表示类型是正确的.很多时候,我们选择一个牛逼 -uple因为我们懒得去定义一个类型,我们应该解释访问的问题ñ一的个场牛逼 -uple或迭代一个领域牛逼 -uple作为严重的信号,是时候切换到合适的类型.

t -uples 有两个自然替代品:记录数组.

何时使用记录

我们可以看到一个记录作为一个牛逼 -uple其项被标记,这样,他们肯定是最自然的更换,以牛逼 -uples如果我们想直接访问它们.

type ivuple = {
  a: int;
  b: int;
  c: int;
  d: int;
}
Run Code Online (Sandbox Code Playgroud)

然后,我们通过写入直接访问类型a值的字段.请注意,记录很容易通过修改进行复制,如.没有自然的方法来迭代记录的字段,主要是因为记录不需要是同构的.xivuplex.alet y = { x with d = 0 }

何时使用数组

大型²同构值集合由数组充分表示,允许直接访问,迭代和折叠.可能不方便的是,数组的大小不是其类型的一部分,但对于固定大小的数组,通过引入私有类型(甚至是抽象类型)可以很容易地避免这种情况.我在回答 "OCaml编译器检查向量长度"这个问题时描述了这种技术的一个例子.

关于浮拳的注意事项

t- uples中使用浮点数时,在仅包含浮点数和数组的记录,这些都是未装箱的.因此,在数值计算中从一种类型更改为另一种类型时,我们不应该注意到任何性能修改.


¹请参阅TeXbook.²大于4开始.


use*_*830 8

由于OCaml元组的长度是类型的一部分,因此在编译时已知(并且已修复),因此您可以通过元组上的直接模式匹配来获得第n个项目.出于同样的原因,在实践中不能发生提取"任意长度元组"的第n个元素的问题- 这样的"元组"不能在OCaml的类型系统中表达.

您可能仍然不希望每次需要投射元组时都写出模式,并且没有什么能阻止您生成函数get_1_1...... 从-tuple get_i_j中提取i-th元素以j用于任何可能的组合ij发生在你的代码中,例如

let get_1_1 (a) = a
let get_1_2 (a,_) = a
let get_2_2 (_,a) = a
let get_1_3 (a,_,_) = a
let get_2_3 (_,a,_) = a
...
Run Code Online (Sandbox Code Playgroud)

不一定漂亮,但可能.

注意:以前我声称OCaml元组最多可以有255个长度,你可以简单地一劳永逸地生成所有可能的元组投影.正如@Virgile在评论中指出的那样,这是不正确的 - 元组可能是巨大的.这意味着预先生成所有可能的元组投影函数是不切实际的,因此上面的限制" 发生在您的代码中 ".

  • 元组有长度限制,但它远高于 255(边界是 OCaml 块的最大大小,通常为 2^22 或 2^54,参见例如 https://realworldocaml.org/v1/en/html/ memory-representation-of-values.html)。255(实际上类似于 250,因为某些标记被保留)是 sum 类型中可以拥有的非常量构造函数的最大数量。 (2认同)