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

时间:2014-09-23 22:15:05

标签: functional-programming ocaml

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

4 个答案:

答案 0 :(得分:11)

TL; DR; 停止尝试直接访问 t -uple的 n -th元素并使用记录或数组因为他们允许随机访问。

您可以通过解包 t -uple来获取 n -th元素,并使用价值解构,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

match - 构造在这里没有优于let - 构造的优点,只是为了完整性而包含它。

不要使用 t -uples,Don¹

只有少数情况下使用 t -uples来表示类型是正确的。大多数情况下,我们选择 t -uple,因为我们懒得定义类型,我们应该解释访问 n -th字段的问题em> t -uple或迭代 t -uple的字段作为一个严重的信号是时候切换到正确的类型

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

何时使用记录

我们可以看到记录为 t -uple,其条目被标记,因此,如果我们想要它们,它们绝对是 t -uples的最自然的替代品直接访问它们。

type ivuple = {
  a: int;
  b: int;
  c: int;
  d: int;
}

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

何时使用数组

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

关于浮动拳击的注意事项

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


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

答案 1 :(得分:8)

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

每次需要投射元组时,您可能仍然不想写出模式,没有什么可以阻止您生成函数get_1_1 ... get_i_j ...提取{ {1}} - 来自i - 元组的元素,代码中出现ji的任何可能组合,例如

j

不一定漂亮,但可能。

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

答案 2 :(得分:2)

在OCaml中完全无异地编写这样的函数是不可能的。一种看待这种情况的方法是考虑函数的类型。有两个问题。首先,元组的每个大小都是不同的类型。因此,您无法编写访问不同大小元组元素的函数。第二个问题是元组的不同元素可以有不同的类型。列表没有这些问题,这就是为什么你可以List.nth

如果您愿意使用其元素类型相同的固定大小的元组,您可以编写@ user2361830所示的函数。

<强>更新

如果您确实拥有要通过索引访问的相同类型的值集合,则应该使用数组。

答案 3 :(得分:0)

这是一个函数,可将您需要执行的ocaml函数的字符串返回给您;)我经常使用它非常有帮助。

let tup len n = 
if n>=0 && n<len then
    let rec rep str nn = match nn<1 with 
        |true ->""
        |_->str ^ (rep str (nn-1))in
    let txt1 ="let t"^(string_of_int len)^"_"^(string_of_int n)^" tup = match tup with |" ^ (rep "_," n) ^ "a" and
    txt2 =","^(rep "_," (len-n-2)) and
    txt3 ="->a" in
    if n = len-1 then
        print_string (txt1^txt3)
    else
        print_string (txt1^txt2^"_"^txt3)
else raise (Failure "Error") ;;

例如:

tup 8  6;;

返回:

let t8_6 tup = match tup with |_,_,_,_,_,_,a,_->a

当然:

val t8_6 : 'a * 'b * 'c * 'd * 'e * 'f * 'g * 'h -> 'g = <fun>