【问题标题】:How to update record value in SML?如何更新 SML 中的记录值?
【发布时间】:2019-08-14 17:02:51
【问题描述】:

我正在编写 SML 程序来更新列表中的记录。例如,我输入了 person_name。

type person_name = {fname:string, lname:string, mname:string}

然后我有 person_bio,其中嵌入了 person_name。

type person_bio = {age:real, gender:string, name:person_name, status:string}

接下来我有一个拥有 person_bio 的员工。

type employee = {p:person_bio, payrate:real, whours:real} list;

现在,我必须通过传递名字来定义函数“updateLastName”。

截至目前,使用以下数据创建了一条记录“e1”。

{p={age=40.0,gender="M",name{fname="rob",lname="sen",mname=""},status="M"},
  payrate=30.0,whours=10.0} 

但我面临遍历列表然后更新记录中的一个字段的挑战。

fun updateLastName(x:string,l:employee)=
  if (L=[]) then []
  else if (x= #fname(#name(#p hd l))  //cheking name of 1st record in list

  //not getting how to update,this kind of line did not work
  #fname(#name(#p hd l) = "abc"

  else updateLastName(x,tl(l));    // hope this is right

请提出建议。

【问题讨论】:

  • SML 中的值是不可变的,#fname(#name(#p hd l) = "abc" 是一个比较。您需要使用更新的记录构建一个新列表。 (并阅读模式匹配——它让一切变得不那么凌乱,更具可读性。)
  • 从比记录更简单的东西开始,比如整数。
  • 谢谢,我有点困惑。要仅更新 lname,如果无法直接更新,我必须以某种方式删除并添加它?
  • 我这样做是为了更新姓氏,但没有工作。有趣的 updateLname(fnm:string,lnm:string,[]) = [] | updateLname(fnm:string,lnm:string,x::xs)= ( if fnm= (#fname ( #name ( #px))) then if lnm (#lname ( #name ( #p ( x)) )) 然后 lnm else (#lname ( #name ( #p ( x))))) :: updateLname(fnm,lnm,xs)

标签: functional-programming sml smlnj


【解决方案1】:

您偶然发现了一件困难的事情:更新一个深深嵌套的记录。

对于您拥有getters 的记录,因此#fname (#name (#p employee)) 获取您要检查的字段,以了解这是您要更新其姓氏的员工。但是记录不会授予您等效的setter,因此您必须制作这些。如果您好奇,lenses (Haskell) 是解决此问题的一般方法,但我不知道任何标准 ML 镜头的实现。

我将继续删除您的 employee 类型中的 list 部分;如果您想要对多个员工进行建模,而不是说一个员工是多个人,您可能应该想要一个 employee list

type person_name = { fname:string, lname:string, mname:string }
type person_bio = { age:real, gender:string, name:person_name, status:string }
type employee = { p:person_bio, payrate:real, whours:real }

val name1 = { fname = "John", lname = "Doe", mname = "W." } : person_name
val bio1 = { age = 42.0, gender = "M", name = name1, status = "?" } : person_bio
val my_employee1 = { p = bio1, payrate = 1000.0, whours = 37.0 } : employee

val name2 = { fname = "Freddy", lname = "Mercury", mname = "X." } : person_name
val bio2 = { age = 45.0, gender = "M", name = name2, status = "?" } : person_bio
val my_employee2 = { p = bio2, payrate = 2000.0, whours = 37.0 } : employee

val my_employees = [ my_employee1, my_employee2 ] : employee list

至于 setter(您可以使用 lenses 自动导出的那些),

fun setP (p : person_bio, e : employee) =
    { p = p
    , payrate = #payrate e
    , whours = #whours e } : employee

fun setName (name : person_name, pb : person_bio) =
    { age = #age pb
    , gender = #gender pb
    , name = name
    , status = #status pb } : person_bio

fun setLname (lname, pn : person_name) =
    { fname = #fname pn
    , lname = lname
    , mname = #mname pn } : person_name

您可以编写这些,例如喜欢:

- setP (setName (setLname ("Johnson", #name (#p my_employee1)), #p my_employee1), my_employee1)
> val it =
    {p =
           {age = 42.0, gender = "M",
            name = {fname = "John", lname = "Johnson", mname = "W."},
            status = "?"}, payrate = 1000.0, whours = 37.0} :
      {p :
         {age : real, gender : string,
          name : {fname : string, lname : string, mname : string},
          status : string}, payrate : real, whours : real}

或者您可以将该行分开一点以使其更具可读性:

fun updateLname (fname, lname, employees) =
    let fun update employee =
            if #fname (#name (#p employee)) = fname
            then let val new_name = setLname (lname, #name (#p employee))
                     val new_bio = setName (new_name, #p employee)
                     val new_employee = setP (new_bio, employee)
                 in new_employee end
            else employee
    in List.map update employees
    end

试试这个:

- updateLname ("Freddy", "Johnson", my_employees);
> val it =
    [{p = ... {fname = "John", lname = "Doe", mname = "W."}, ... },
     {p = ... {fname = "Freddy", lname = "Johnson", mname = "X."}, ... }]

- updateLname ("John", "Johnson", my_employees);
> val it =
    [{p = ... {fname = "John", lname = "Johnson", mname = "W."}, ... },
     {p = ... {fname = "Freddy", lname = "Mercury", mname = "X."}, ... }]

【讨论】:

  • 镜片的想法似乎很有趣。我想知道在直接 SML 中实现它的可能性有多大。我不明白为什么不这样做,但另一方面,当它引用“Template Haskell”时,我不知道该链接是什么意思。
  • 您可以在预处理步骤中生成镜头。如果我要制作这样的预处理器,我可能会扩展 Morten Brøns-Pedersen 的 PreML SML 预处理器的工作。
  • 非常感谢您的详尽解释。它帮助了很多。
【解决方案2】:

根据您的情况,此处可能适合参考。

对于您可能需要更改的任何值,您可以将它们作为参考,即

type person_name = {fname:string, lname:string ref, mname:string}
type person_bio = {age:real, gender:string, name:person_name, status:string}
fun change_lname(new_lname: string, bio: person_bio) = (#lname (#name bio)) := new_lname

val p1 = ...
print !(#lname (#name p1)) ==> LastName1

change_lname("LastName2", p1)
print !(#lname (#name p1)) ==> LastName2

如果您打算大量修改记录中的数据,最好将其设为引用,这样您的程序就不会在每次需要更改一个值时都重写内存(尽管在许多情况下编译器/解释器将能够优化这一点)。如果记录的签名发生更改,它还可以使您不必重写 setter 函数。缺点是您将通过使用引用在程序中引入复杂性。

例如,在上面的代码中,我们实际上并没有修改 p1 的姓氏,而是 p1 和一个副本(传递给函数)都指向同一个字符串,我们在函数中修改了那个字符串。我们实际上并没有改变任何记录中的任何数据,我们只是改变记录指向的数据。这是一个微妙的区别,在这个例子中并没有真正的区别,但它可能会导致难以调试的奇怪错误。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-05-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-10-05
    • 1970-01-01
    相关资源
    最近更新 更多