SML列表分解问题

SML list decomposition issue

我找到了华盛顿大学的旧课程 CSE341:编程语言 并且正在努力跟进,这样我就有朝一日可以解决 Paulson 的 ML for the Working程序员。所以我有这个:

fun number_in_month5 (m : int, ms : (int*int*int) list) =
    List.length (List.filter (fn (_,x2,_) => x2 = m) ms)

这给了我一个匹配的三元组列表。我可以在前面扔一个 List.length 并得到计数。但是这个呢

fun number_in_month6 (m, (_,d2,_) :: ms) =
    List.length (List.filter (fn (_,x2,_) => x2 = m) ms)

: stdIn:279.36 Warning: calling polyEqual
: stdIn:278.5-279.43 Warning: match nonexhaustive
:           (m,(_,d2,_) :: ms) => ...
:   
: val number_in_month6 = fn : ''a * ('b * ''a * 'c) list -> ('b * ''a * 'c) list

测试有时会给出错误的答案

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

为什么这不起作用?但是我没有阅读 ML 错误消息的技能。有人建议 不要 在函数的开头声明类型——我试过了,但没有成功。然后我发现了这个

fun sumlists [] [] : int list = []
    |   sumlists (n :: ns) (m :: ms) = (n + m) :: sumlists ns ms;

    ;;; ML WARNING - Clauses of function declaration are non-exhaustive
    ;;; CONTEXT  :  fun sumlists
    val sumlists = fn : int list -> int list -> int list

The definition leaves an expression such as sumlists [1] []; undefined.

所以我理解非穷尽的概念,但我无法理解为什么我的函数是非穷尽的并且给出了错误的答案。

fun number_in_month6 (m, (_,d2,_) :: ms) =
List.length (List.filter (fn (_,x2,_) => x2 = m) ms)

: stdIn:279.36 Warning: calling polyEqual
: stdIn:278.5-279.43 Warning: match nonexhaustive
:           (m,(_,d2,_) :: ms) => ...
:   
: val number_in_month6 = fn : ''a * ('b * ''a * 'c) list -> ('b * ''a * 'c) list

I can't fathom why my function is non-exhaustive and giving bad answers.

  • 非穷举:存在模式 (_,d2,_) :: ms 无法匹配的值,即 []。问题似乎是,当您实际上只需要在 List.filter.

    的谓词函数内执行此操作时,您根本无法对 number_in_month6 的输入进行模式匹配

    也许这里的误解是“如果我不在参数中进行模式匹配,输入将不会是三元组列表,因为我删除了类型注释”。知道 SML 的类型推断足够强大,仅通过使用 List.filterfn (_,month2,_) => ....

    就可以推断出 ms 是一个三元组列表,这可能很有趣
  • 错误的答案:结果有时会相差一个,因为您丢弃了第一个元素 (_,d2,_),然后查看列表的过滤剩余部分 ms 有多长, 是.

    在您的示例中,第一个日期应该算作正确结果 3 的一部分,但它被丢弃了,3 个日期中剩余的 2 个日期被正确计算。

解决这个问题:

fun number_in_month (month1, dates) =
    List.length (List.filter (fn (_, month2, _) => month1 = month2) dates)

并测试这个:

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

一些要点:给你的变量更好的名字可以更容易理解正在发生的事情。您可能需要考虑使用记录而不是元组。它们基本上是一样的,除了元组有编号的条目,记录有命名的条目,而且记录语法有点复杂。但好处是您不会不小心忘记这是美国日期、欧洲日期还是 ISO 日期:

type date = { year : int, month : int, day : int }

fun number_in_month (month1, dates : date list) =
    List.length (List.filter (fn ({ month = month2, ... }) => month1 = month2) dates)

val test_1 = number_in_month (2, [ { year = 2018, month = 2, day = 1 }
                                 , { year = 2018, month = 2, day = 2 }
                                 , { year = 2018, month = 1, day = 3 }
                                 , { year = 2018, month = 2, day = 4 }
                                 ])

不过,记录语法可能有点棘手,所以只要您觉得元组实用,请随时继续使用它们。