列表交互的SML递归函数

时间:2019-05-19 02:54:44

标签: list recursion sml

我在另一则SO帖子中找到了此代码:

fun number_in_month ([], _) = 0
  | number_in_month ((_,x2,_) :: xs, m) = 
    if x2 = m then
    1 + number_in_month(xs, m)
    else
    number_in_month(xs, m)

令我惊讶的是它起作用。

- number_in_month ([(2018,1,1),(2018,2,2),(2018,2,3),(2018,3,4),(2018,2,30)],2);
val it = 3 : int

我的困惑是,首先不熟悉这种形式的经典数学递归函数(我是初学者),然后是如何逐步遍历列表。我的直觉是在if-then-else中进行递归调用以发送列表的末尾,即

...
1 + number_in_month((tl xs), m)
...

但这不起作用。每次递归调用如何遍历列表?我只能想象这是某种内置的SML魔术。

2 个答案:

答案 0 :(得分:1)

没有魔术,xs 列表的结尾。

需要了解两件事:列表和模式匹配。

在SML中,列表语法[a, b, c]只是a :: b :: c :: nil的简写,其中::是(infix)cons构造函数。除了这个简写之外,SML中的列表没有什么神奇之处,它们被预先定义为这种类型:

datatype 'a list = nil | :: of 'a * 'a list
infixr 5 ::

后面的定义将::转换为优先级5的右关联中缀运算符。

第二,该定义对参数使用模式匹配。像x::xs这样的模式可以匹配相同形状的(非空)列表,将x绑定到列表的头部,并将xs绑定到列表的尾部,与上面的定义相对应。在您的函数中,x进一步被另一个模式本身取代。

仅此而已。没魔术这同样适用于自定义列表表示形式:

datatype my_list = empty | cons of (int * int * int) * my_list
infixr 5 cons

fun count (empty, x) = 0
  | count ((_,y,_) cons xs, x) =
    if x = y then 1 + count (xs, x) else count (xs, x)

val test = count ((1,2,3) cons (3,4,5) cons (6,2,7) cons empty, 2)

答案 1 :(得分:1)

  

但是如何将其更改为例如建立一个新的比赛列表,而不是仅仅对它们进行计数?

在这种情况下,您需要对当前解决方案进行两次修改:

  1. 您想将递归案例的模式更改为一种,如果匹配,则可以提取整个三元组的日期。现在,您只提取月份部分进行比较,而丢弃其他位,因为您只想在月份匹配的情况下增加一个计数器。

  2. 该函数的结果不应为1 + ...,而应为(x1,x2,x3) :: ...

一个快速解决方案:

fun dates_of_month ([], _) = []
  | dates_of_month ((year,month,day) :: dates, month1) =
    if month = month1
    then (year,month,day) :: dates_of_month (dates, month1)
    else                     dates_of_month (dates, month1)
  

我将...((_,x2,_) :: xs, m)更改为...((x1,x2,x3) :: xs, m)...并奏效,但这似乎有点麻烦。

以下是Andreas Rossberg提出的两种选择:

使用进阶

fun dates_of_month ([], _) = []
  | dates_of_month (date :: dates, month1) =
    let val (_, month, _) = date
    in
      if month = month1
      then date :: dates_of_month (dates, month1)
      else         dates_of_month (dates, month1)
    end

使用as

fun dates_of_month ([], _) = []
  | dates_of_month ((date as (_,month,_)) :: dates, month1) =
    if month = month1
    then date :: dates_of_month (dates, month1)
    else         dates_of_month (dates, month1)

这是第三个选项,它通过使用高阶列表组合器抽象出递归:

fun dates_of_month (dates, month1) =
    List.filter (fn (_, month, _) => month = month1) dates