【问题标题】:Error: Operator is not a function - SML错误:运算符不是函数 - SML
【发布时间】:2016-05-08 20:01:15
【问题描述】:

我正处于编程和 SML 的第一步,我正在尝试创建一个程序。事实上,发生了很多错误。

在这篇文章中我得到了错误:

操作符不是函数

我是函数式编程的新手,很难理解下面代码的逻辑。是否可以在“let”中使用多个“val”声明,并且在模式匹配中使用了let/val/in?

fun action(_,_,[]) = ([],[]) 
   |action(cin,cout,h::[]) = 
    let 
      val (s1,d1,d2) = (pass_cout(pass_cin h cin) cout, #1          (find2digits((pass_cout(pass_cin h cin) cout))),#2     (find2digits((pass_cout(pass_cin h cin) cout))))

  in
   if (d1<>d2) then 
    (d1::[],[])
   else ([],[])
  end
 |action (cin,cout,first::t) = 
  let
   val s1  = pass_cout ((pass_cin first cin) cout) 
   val s2 = pass_cout  (last_element t) cout (**)
   val d1 = #1 find2digits(s2)
   val d2 = #2 find2digits(s2)
   val neo_in = produce_cin s1 s2 (**)
   val neo_out = produce_cout s2

  in 
    if (neo_in<2 andalso neo_out<2) then
    (d1::action(neo_in,neo_out,cut (first::t)),d2::action(neo_in,neo_out,cut (first::t))) (**)
    else ([],[])
    end;

【问题讨论】:

  • 您的代码包含对未定义函数的多个引用(例如find2digitspass_cout),因此很难说出任何有意义的内容,尤其是因为您的意图似乎不清楚。你想做什么?不过,我会这么说——当您在 let 内有多个 val 绑定时,它们需要用分号而不是换行符分隔。
  • @JohnColeman:声明之间的分号是可选的,在 let 绑定中也是如此。有点相关,表达式之间的分号运算符(SML 中唯一真正有用的分号)需要在它们周围加上括号,例如(f x; ...; g y) 除了在 let-bindings 内:let ... in f x; ...; g y end。这是为了用声明之间不太有用的分号来消除歧义。由于let ... inend 内不能直接声明(除非引入了内部 let 绑定),因此可以省略括号。
  • @SimonShine 你当然是对的。我知道但暂时忘记了。我查看了 Ullman,但他是那些经常使用分号的作者之一。
  • 我怀疑这是因为您在定义s1 时将(pass_cin first cin) 应用于cout
  • 非常感谢您的帮助。 pass_cin 和 pass_cout 有 2 个整数作为输入,它们的输出也是一个整数。所以我从 s1 的错误开始,说运算符不是函数

标签: operator-keyword sml


【解决方案1】:

我看到你问了三个问题。

我可以在 let 中使用多个 val 声明吗?

是的。你已经在大规模地这样做了。

let/val/in[/end]用于模式匹配吗?

我不完全确定你的意思,所以我会尽量回答我想你可能会问的所有问题。

首先,let-bindings 允许您将某些计算或值的结果临时绑定到名称。如果您需要多次使用结果,并且计算成本很高,或者需要您不希望重复的副作用,这很有用。但它们也可以使代码更具可读性(因为名称可能会增加含义)。 模式匹配允许您以逐个案例的方式处理不同输入的多个案例。您可以匹配到任意深度,编译器通常能够判断您是否缺少任何模式。

所以如果你问是否在模式匹配中使用了 let-bindings,至少在以下意义上答案是肯定的:

  1. 就像 let-binding 临时将计算或值绑定到名称一样,当您的模式匹配中有 变量模式 时,也会发生同样的事情。例如

    let val (a, b, c) = f (x, y) in ... end
    

    等价于

    (case f (x, y) of
         (a, b, c) => ...)
    

    abc 是始终匹配并绑定到 ... 中的某个值的模式。

  2. 您可以在其他 let-bindings 或 case-ofs 中包含 let-bindings 和 case-ofs,例如

    let val result =
        (case f (i, j) of
             SOME (x, y) => let val (a, b, c) = (g x, g y, g (x + y))
                            in
                              ...
                            end
           | NONE => ...)
    in ... end
    

    但这些示例通常可以简化。

如果你问是否在 let-bindings 中使用了模式匹配,答案是肯定的,实际上在进行 let-binding 时会进行一些模式匹配;例如在

let
  val (s1, d1, d2) = (pass_cout(pass_cin h cin) cout,
                      #2 (find2digits((pass_cout(pass_cin h cin) cout))),
                      #2 (find2digits((pass_cout(pass_cin h cin) cout))))
in ... end

val (s1, d1, d2) = ... 绑定在三元组上使用模式匹配。 let-bindings 中的模式匹配只是在处理 产品类型,从不使用sum types。请参阅What are "sums-and-products" data structures? - 即,永远不要这样做

val SOME x = f x

因为它会在f xNONE 的那一刻炸毁。 case-of 结构比 let-in-end 更适合更大范围的模式,而 let-in-end 更好适合进行一系列任务时,例如

fun solve2 (a : real, b : real, c : real) =
    let val discr  = b * b - 4.0*a*c
        val sqr = Math.sqrt discr
        val denom = 2.0 * a
    in ((~b + sqr) / denom,
        (~b - sqr) / denom) end

为什么我的程序给我错误“操作符不是函数”?

正如约翰所说,这很难知道,因为您编写的代码只有在为值 pass_coutpass_cinfind2digitslast_elementproduce_cin 和 @ 提供有意义的定义时才会运行987654339@。您转发错误消息的方式并不表明问题所在,并且您编写的代码无法运行以完整地重现错误消息。如果我对这些有有意义的值,那么这段代码似乎还有其他类型不统一的问题。

由于您的代码没有传达自己的意图(使用通用名称,如 actions1d2 等),我也无法推荐更好的编写方式,因为我不知道是什么它应该这样做,所以这里有一些关于编码风格的一般建议:

  1. 使用 let-bindings 避免调用像 find2digits 这样的函数两次,例如喜欢

    let
      val (d1, d2) = find2digits ...
      val s1 = pass_cout ...
    in
      ...
    end
    
  2. 正确拆分行,例如喜欢

    if neo_in < 2 andalso neo_out < 2
    then (d1::action(neo_in,neo_out,cut (first::t)),
          d2::action(neo_in,neo_out,cut (first::t)))
    else ([],[])
    

    甚至更好,比如

    if neo_in < 2 andalso neo_out < 2
    then let
           val wat = action (neo_in, neo_out, cut (first::t))
         in
           (d1::wat, d2::wat)
         end
    else ([], [])
    

    wat 被替换为一些有意义的名称来描述这是什么。

  3. Choose good variable names!

【讨论】:

  • 感谢您的帮助。我的函数 pass_cin 和 pass_cout 将 2 个整数作为输入并产生 1 个整数作为输出。那么您知道为什么会出现 s1 中的错误吗?
  • @iwia:我能够推断出这些函数的类型,所以这不是我关心的问题。您确实需要专注于使您的源代码作为阅读材料易于理解。例如阅读this Best Practices guide for making code more readable。 (这些示例不在 ML 中,但它们应该足够通用,可以从中学习。)
猜你喜欢
  • 2021-11-01
  • 2019-04-19
  • 2018-02-11
  • 1970-01-01
  • 2013-01-19
  • 1970-01-01
  • 2020-05-19
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多