【问题标题】:number_in_month exercise (Count elements in a list)number_in_month 练习(计算列表中的元素)
【发布时间】:2020-02-26 05:41:34
【问题描述】:

我一直在尝试计算整数 3 元组列表中的元素,这等于使用 SML 的给定整数,但它不起作用。谁能帮我弄清楚下面的代码有什么问题或为我整理一下?

 fun number_in_month(x : int*int*int list, m: int) =
    if null x then 0

         else
           let fun inc x = x + 1;
         in
             val counter = 0;
             if m = #2 (hd x) andalso m > 0 then inc counter
            number_in_month((tl x), m)
           `  else
             number_in_month((tl x), m)
        end

这个函数应该返回 m 等于列表中每个元组的第二个元素的次数。

【问题讨论】:

    标签: list sml


    【解决方案1】:

    显然你很难放弃你的命令式思维。

    让我尝试解决您的一些问题

    • 您应该使用模式匹配而不是使用null xhd xtl x。 这也适用于分解元组和记录。例如

      fun number_in_month ((x1, x2, x3) :: xs, m) = ...
      

      或者,因为我们从不使用 x1 和 x3

      fun number_in_month ((_, x2, _) :: xs, m) = ...
      

      这样可以清楚地看到第一个参数是一个三元组列表,并且没有类型注释 需要

      当你省略显式类型注释时,这就是拥有类型系统的全部想法 可以为你推断它们(见下一点),然后是这段代码

      fun foo42 xs = map (fn x => #2 x) xs
      

      会给你一些关于“未解决的弹性记录”的令人讨厌的错误(这个错误消息来自 SML/NJ)

      /tmp/sml20620PlF:105.5-105.44 Error: unresolved flex record
         (can't tell what fields there are besides #2)
      

      通过分解三元组很容易解决

      fun foo42 xs = map (fn (_, x2, _) => x2) xs
      
    • 说到类型注解。它们(几乎总是)不需要,而且它们使 代码的可读性。更不用说它们不必要地限制了您使用的类型 可以用在上面。

      根据你真正想要的,你给出的类型注释也是错误的。你 int * int * int 周围应该有括号。目前它被解释为 两个整数的三元组和一个整数列表int * int * (int list)

      如果你真的坚持对你的函数进行类型注释,那么你可以这样做

      val number_in_month : (int * int * int) list * int -> int = 
          fn ([]            , m) => 0
           | ((_,x2,_) :: xs, m) => 42
      

      这“几乎”像 Haskell,类型在函数声明之前给出。

    • 在缩进代码的方式上尽量保持一致。这会给你更好的清晰度。 在这里,我特别想到了您将else 部分缩进in ... end 的方式 部分。下面的部分显然在很多方面仍然是错误的,我无法开始想象,但它 给出如何做的想法

      fun number_in_month(x : int*int*int list, m: int) =
          if null x then 0
          else
            let fun inc x = x + 1;
            in
              val counter = 0;
              if m = #2 (hd x) andalso m > 0 then
                 inc counter
                 number_in_month((tl x), m)
              else
                 number_in_month((tl x), m)
            end
      
    • 您不能在 let 表达式的 in ... end 部分内声明变量 val counter = 0。 let 表达式的语义是

      let
        dec
      in
        exp_1; ...; exp_n
      end
      

      因此所有声明(函数和值绑定等)都必须放在let ... in 部分。

    • 实际上不需要增量函数,它只会影响可读性。 请记住,SML 使用单一赋值,因此变量在声明后是不可变的。

    • 嵌套 if 表达式中的序列事物

      inc counter
      number_in_month((tl x), m)
      

      完全没有意义。您可以在内部拥有多个表达式的唯一方法 then ... else 部分(实际上是任何需要单个表达式的地方),带有 序列(exp_1;...;exp_n)。但是,这仅在除最后一个表达式之外的所有表达式都具有 副作用,因为它们的结果被忽略/丢弃

      - (print "Foo\n"; print "Bar\n"; 42);
      Foo
      Bar
      val it = 42 : int
      

    如果你在这里搜索一下 SO,你会发现最近有一个非常相似的问题是 askedanswered。尽管最后一个参数的类型有所不同,但您仍然可能会得到一些有用的指针。

    总而言之,解决方案可能看起来像

    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)
    

    但是,由于您的问题比前面提到的更简单,您可以轻松地使用基础库中 list 模块中的一些高阶函数

    fun number_in_month (xs, m) = length (List.filter (fn (_, x2, _) => x2 = m) xs)
    

    甚至(可以说)更简单,通过折叠列表并在每次匹配时沿途增加一个变量

    fun number_in_month (xs, m) = foldl (fn ((_, x2, _), b) => if x2 = m then b+1 else b) 0 xs
    

    【讨论】:

    • Jesper.. 你今天很开心.. :)
    • 我看到了这个问题,但我无法理解代码。这就是为什么我需要一些我能理解的东西。非常感谢。问题是,我是函数式编程的新手,但看起来很有趣。我不知道 => 做了什么,我想要一个真正的递归函数,因为我根本不知道折叠是如何工作的。但你的想法真的很棒。我要全部尝试。明天晚上,最晚,如果你介意,我会告诉你结果。顺便谢谢。很多
    • 你好杰斯珀!可能会有所帮助 - 这个问题 - stackoverflow.com/questions/14431526/… - 看起来非常相似:-) - 这是有趣的部分 - 我们的答案在最后变得相似:-D
    • 是的,这是第三次(你的第四次),(基本上)已经提出了“相同”的问题。我想只有“一种”方法可以做到,只要做得对:)
    • 此答案使用隐含课程中尚未涵盖的概念。
    【解决方案2】:
    fun number_in_month (L : (int*int*int) list, m : int) =
    if L = nil
    then 0
    else
        (if #2 (hd L) = m then 1 else 0) + number_in_month (tl L,m);
    

    测试:

    number_in_month ([] , 2);
    number_in_month ([(1,2,3)] , 2);
    number_in_month ([(1,2,3),(2,2,2)] , 2);
    number_in_month ([(1,2,3),(2,2,2),(19,11,29)] , 2);
    number_in_month ([(1,2,3),(2,2,2),(19,11,29),(10,28,19)] , 2);
    number_in_month ([(1,2,3),(2,2,2),(19,11,29),(10,2,19)] , 2);
    number_in_month ([(1,2,3),(2,2,2),(19,11,29),(10,28,19)] , 2);
    number_in_month ([(1,2,3),(2,2,2),(19,11,29),(10,28,19)] , 2);
    number_in_month ([(1,2,3),(2,2,2),(19,11,29),(10,28,19),(16,2,7)] , 2);
    

    参考:

    http://www.cs.sunysb.edu/~leo/CSE215/smllistexamples.txt

    http://www.standardml.org/Basis/list.html

    【讨论】:

    • 部分问题在于,每周都会引入 SML 的新功能,并且通常会限制使用更高级的功能。也许对于这些类型的问题,应该说明对特征的限制。或者,也许演示了“正确”技术以及受限示例,这将允许概念映射。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-06-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多