计算列表中的元素

时间:2013-01-20 21:49:46

标签: 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

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

2 个答案:

答案 0 :(得分:6)

显然,你很难放下你的命令性思维。

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

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

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

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

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

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

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

    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)
    

    可以通过分解3元组轻松修复

    fun foo42 xs = map (fn (_, x2, _) => x2) xs
    
  • 说到类型注释。它们(几乎总是)不需要它们,它们会混乱 代码的可读性。更不用说它们会不必要地限制您运行的类型 可用于。

    根据你真正想要的,你给出的类型注释也是错误的。您 应该在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
    

    这几乎就像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-expression的val counter = 0部分内声明变量in ... end。 let-expression的语义是

    let
      dec
    in
      exp_1; ...; exp_n
    end
    

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

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

  • 嵌套if-expression

    中的序列事物
    inc counter
    number_in_month((tl x), m)
    

    绝对没有意义。你可以在里面拥有多个表达式的唯一方法 then ... else部分(实际上是任何预期单个表达式的地方)都带有 sequence(exp_1; ...; exp_n)。但是,只有在除最后一个表达式之外的所有表达式之外, 副作用,因为他们的结果被忽略/扔掉

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

如果你在这里搜索一下,你会发现最近一个非常相似的问题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)

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

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

答案 1 :(得分:1)

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