【问题标题】:How turn list of pair in list of int, where result int is sum of pair如何在 int 列表中转换对列表,其中结果 int 是对的总和
【发布时间】:2015-09-09 21:20:04
【问题描述】:

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

【问题讨论】:

    标签: sml ml


    【解决方案1】:

    此代码在 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 是柯里化的,因此将其应用于函数会返回一个接受列表的新函数——在本例中,是整数对的列表。

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

    val sum_pairs = map op+
    

    这里,op+ 表示一个应用加法运算符的内置函数,就像我们的函数字面量(上面)所做的那样。


    编辑:后续问题的答案:

    1. 参数类型呢?看起来您已经完全消除了函数定义(标题)中的参数列表。是真的还是我遗漏了什么?

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

    fun add (a, b) = a + b
    

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

    1. 你能解释一下sum_pairs ((a, b)::rest) = (a + b)::sum_pairs(rest)这里发生了什么吗?抱歉,这可能是个愚蠢的问题,但我只想完全理解它。特别是在这种情况下 = 是什么意思,以及这个表达式的求值顺序是什么?

    这里我们在两个子句中定义一个函数。第一个子句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)必须发生在cons之前(::),所以函数不是tail recursive

    【讨论】:

    • fun sum_pairs [] = [] | sum_pairs ((a, b)::rest) = (a + b)::sum_pairs(rest) 这绝对看起来很棒,但我不确定我是否完全理解它。我需要花一些时间来破解它。
    • 1.参数类型呢?看起来您已经完全消除了函数定义(标题)中的参数列表。是真的还是我错过了什么?
    • 2.你能解释一下这里发生了什么sum_pairs ((a, b)::rest) = (a + b)::sum_pairs(rest)。抱歉,这可能是个愚蠢的问题,但我只想完全理解它。特别是 = 在这种情况下的含义以及该表达式的评估顺序是什么?
    • @NikitaLuparev 我编辑了我的帖子。让我知道我的回答是否有意义!
    • 如果可能的话,请在 facebook facebook.com/nikita.luparev 找到我,我想我们会找到一个话题来讨论 :)
    猜你喜欢
    • 2015-12-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-03-16
    • 2010-12-11
    • 2013-08-25
    • 1970-01-01
    相关资源
    最近更新 更多