如何在 int 列表中转换对列表,其中结果 int 是对的总和

How turn list of pair in list of int, where result int is sum of pair

我尝试使用以下协议定义函数: [(1,2), (6,5), (9,10)] -> [3, 11, 19]

这是我现在拥有的:

fun sum_pairs (l : (int * int) list) =
  if null l
  then []
  else (#1 hd(l)) + (#2 hd(l))::sum_pairs(tl(l))      

根据类型检查器,我有一些 type mismatch,但我不知道我到底错在哪里。

此代码在 PolyML 5.2 中运行:

fun sum_pairs (l : (int * int) list) =
    if null l
    then []
    else ((#1 (hd l)) + (#2 (hd l))) :: sum_pairs(tl l)
(* ------------^-------------^ *)

与您的区别很小,但很重要:(#1 hd(l))(#1 (hd l)) 不同;前者并不像你想的那样 - 它试图提取 hd 的第一个元组字段,这是一个函数!

虽然我们正在做这件事,但我们为什么不尝试重写该函数以使其更加地道呢?对于初学者,我们可以通过函数头中参数的 matching 消除 if 表达式和笨重的元组提取,如下所示:

fun sum_pairs [] = []
  | sum_pairs ((a, b)::rest) = (a + b)::sum_pairs(rest)

我们将函数分成两个子句,第一个匹配空列表(递归基本情况),第二个匹配非空列表。如您所见,这大大简化了函数,而且在我看来,它更易于阅读。

事实证明,将函数应用于列表元素以生成新列表是一种非常常见的模式。基础库提供了一个名为 map 的内置函数来帮助我们完成这项任务:

fun sum_pairs l = map (fn (a, b) => a + b) l

我在这里使用 anonymous function to add the pairs together. But we can do even better! By exploiting currying 我们可以简单地将函数定义为:

val sum_pairs = map (fn (a, b) => a + b)

函数 map 已柯里化,以便将其应用于函数 returns 接受列表的新函数 - 在本例中为整数对列表。

但是等一下!看起来这个匿名函数只是将加法运算符应用于它的参数!它的确是。让我们也摆脱它:

val sum_pairs = map op+

这里,op+ 表示应用加法运算符的内置函数,很像我们的函数文字(上面)所做的。


编辑:后续问题的答案:

  1. What about arguments types. It looks like you've completely eliminate argument list in the function definition (header). Is it true or I've missed something?

通常编译器能够 infer 来自上下文的类型。例如,给定以下函数:

fun add (a, b) = a + b

编译器可以很容易地推断出类型int * int -> int,因为参数涉及加法(如果你想要real,你必须这样说)。

  1. Could you explain what is happening here sum_pairs ((a, b)::rest) = (a + b)::sum_pairs(rest). Sorry for may be dummy question, but I just want to fully understand it. Especially what = means in this context and what order of evaluation of this expression?

这里我们在两个子句中定义了一个函数。第一个子句 sum_pairs [] = [] 匹配一个空列表,returns 匹配一个空列表。第二个 sum_pairs ((a, b)::rest) = ... 匹配以一对开头的列表。当您不熟悉函数式编程时,这可能看起来很神奇。但是为了说明发生了什么,我们可以使用 case 重写从句定义,如下所示:

fun sum_pairs l =
  case l of
    [] => []
  | ((a, b)::rest) => (a + b)::sum_pairs(rest)

将按顺序尝试子句,直到匹配一个。如果没有子句匹配,则引发 Match 表达式。例如,如果您省略第一个子句,该函数将始终失败,因为 l 最终将是一个空列表(它从一开始就是空的,或者我们一直递归到最后)。

至于等号,其含义与任何其他函数定义中的含义相同。它将函数的参数与函数体分开。至于评估顺序,最重要的观察是 sum_pairs(rest) 必须发生在 cons (::) 之前,因此函数不是 tail recursive.