SML 模式匹配记录如何工作

问题描述

免责声明,这是在线课程的一部分,但我已经解决了作业。我的问题是关于记录工作的模式匹配?

所以在第一个函数中我不必指定记录的结构

fun generateName (ls,name)=
case ls of
[] => name::[]
  | x::xs =>
{
  first= x,middle= #middle name,last= #last name
}
:: generateName(xs,name);

在这函数中我是这样做的。否则我会收到 flex 记录错误

fun createName (f,name :{first:string,middle:string,last:string}) =
case f of
x =>
{
  first= x,last= #last name
}

更令人困惑的是这个没有错误

fun generateName2 (nms,name) =
let fun aux (ls,name,acc) =
    case ls of
        [] => name::acc
      | x::xs =>
        aux(xs,{
          first= x,last= #last name
        } :: acc)
in
aux (nms,[])
end

我什么时候必须指定记录字段?

解决方法

SML 没有行多态性,因此编译器需要知道记录的所有字段名称。

在返回列表的函数中,递归限制了可能的类型;如果 name 的类型为 R,则 generateName (ls,name) 的类型为 R list,所有字段名都可以从非空递归案例中推断出来。

中间的例子在输入和输出之间没有这样的关系,所以除非你指定输入字段名称,否则SML不知道它们是什么。

,

@molbdnilois 的回答很好。因为我坚信充分利用编程语言的优点可以使代码更易于推理,所以让我们稍微整理一下代码示例。理论上这属于评论,但不太适合这种级别的评论。

我希望这里显示的想法可以帮助您。好好学习!

fun generateName (ls,name)=
case ls of
[] => name::[]
  | x::xs =>
{
  first= x,middle= #middle name,last= #last name
}
:: generateName(xs,name);

当我们看到 case 作为函数体中的第一件事,并且它只是对一个参数起作用时,我们可以在函数签名本身中使用模式匹配。

fun generateName([],name) = name :: []
  | generateName(x::xs,name) = 
      { first = x,middle = #middle name,last = #last name } :: generateName(xs,name);

我们还可以进一步使用模式匹配来消除在函数体内提取记录字段的需要。

fun generateName([],name as {first:string,middle:string,last:string}) = 
      { first = x,middle = middle,last = last } :: generateName(xs,name);

在你的第二个例子中,模式匹配一​​个参数和一个包罗万象的模式是没有意义的。我们可以摆脱它。

fun createName (f,name :{first:string,last:string}) =
  { first = f,last = #last name }

如果我们将其作为模式提供,我们就不必指定类型。

fun createName (f,{first:string,last = last }

您的第三个代码示例也可以从这些想法中受益。

fun generateName2(nms,name) =
  let 
    fun aux([],name,acc) = name :: acc
      | aux(x::xs,name as { first:string,last:string },acc) =
          aux(xs,{ first = x,last = last } :: acc)
  in
    aux(nms,[])
  end