计算列表中的元素

gut*_*hik 1 list sml

我一直在尝试计算整数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
Run Code Online (Sandbox Code Playgroud)

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

Jes*_*erg 6

很明显,你很难放下你的命令性思维.

让我试着解决你的一些问题

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

    fun number_in_month ((x1, x2, x3) :: xs, m) = ...
    
    Run Code Online (Sandbox Code Playgroud)

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

    fun number_in_month ((_, x2, _) :: xs, m) = ...
    
    Run Code Online (Sandbox Code Playgroud)

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

    此外,当您省略显式类型注释时,这是一个可以为您推断它们的类型系统的整体想法(参见下一点),那么此代码

    fun foo42 xs = map (fn x => #2 x) xs
    
    Run Code Online (Sandbox Code Playgroud)

    将在"未解决的flex记录"上给你一些讨厌的错误(此错误消息来自SML/NJ)

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

    通过分解3元组可以很容易地修复它

    fun foo42 xs = map (fn (_, x2, _) => x2) xs
    
    Run Code Online (Sandbox Code Playgroud)
  • 说到类型注释.它们(几乎总是)不需要它们,并且它们使代码的可读性变得混乱.更不用说它们会不必要地限制您可以使用的功能类型.

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

    如果您真的坚持使用类型注释您的功能,那么您可以这样做

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

    这几乎就像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
    
    Run Code Online (Sandbox Code Playgroud)
  • 您不能val counter = 0in ... endlet-expression 的部分内声明变量.let-expression的语义是

    let
      dec
    in
      exp_1; ...; exp_n
    end
    
    Run Code Online (Sandbox Code Playgroud)

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

  • 地球上没有必要具有增量功能,它只会使可读性变得混乱.请记住,SML使用单个赋值,因此变量在声明后是不可变的.

  • 嵌套if-expression中的序列事物

    inc counter
    number_in_month((tl x), m)
    
    Run Code Online (Sandbox Code Playgroud)

    绝对没有意义.在then ... else部件内部(实际上是任何需要单个表达式的地方)中可以有多个表达式的唯一方法 是使用序列(exp_1; ...; exp_n).但是,只有在除最后一个表达式之外的所有表达式都有副作用时才会使用它,因为它们的结果会被忽略/丢弃

    - (print "Foo\n"; print "Bar\n"; 42);
    Foo
    Bar
    val it = 42 : int
    
    Run Code Online (Sandbox Code Playgroud)

如果你在这里搜索一下,你会发现最近有一个非常相似的问题被问到回答了.虽然它在最后一个参数的类型上有所不同,但您仍然可以获得一些有用的指针.

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

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)
Run Code Online (Sandbox Code Playgroud)

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

fun number_in_month (xs, m) = length (List.filter (fn (_, x2, _) => x2 = m) xs)
Run Code Online (Sandbox Code Playgroud)

或者甚至(可以说)更简单,通过折叠列表并在每次匹配时沿着路径递增变量

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