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

时间:2015-05-23 07:12:14

标签: sml ml

我尝试使用以下协议定义函数: [(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,但我无法弄清楚我到底错在哪里。

1 个答案:

答案 0 :(得分:3)

此代码在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的第一个元组字段,这是一个函数!

虽然我们正在努力,但为什么我们不尝试重写该功能以使其更具惯用性?对于初学者,我们可以消除函数头中参数if表达式和matching的笨重元组提取,如下所示:

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将对添加到一起。但我们可以做得更好!通过利用currying,我们可以简单地将函数定义为:

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

函数map是curry,因此将它应用于函数会返回一个接受列表的新函数 - 在本例中是一个整数对列表。

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

val sum_pairs = map op+

这里,op+表示应用加法运算符的内置函数,就像我们的函数文字(上图)那样。

编辑:后续问题的答案:

  
      
  1. 参数类型怎么样?看起来你已经完全消除了函数定义(标题)中的参数列表。是真的还是我错过了什么?
  2.   

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

fun add (a, b) = a + b

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

  
      
  1. 你能解释一下这里发生了什么sum_pairs ((a, b)::rest) = (a + b)::sum_pairs(rest)。对不起,可能是虚拟问题,但我只是想完全理解它。特别是在这种情况下= =表示这个表达式的评价顺序是什么?
  2.   

这里我们在两个子句中定义一个函数。第一个子句sum_pairs [] = []匹配一个空列表并返回一个空列表。第二个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)必须在缺点(::)之前发生,因此函数不是tail recursive