Functional programming 如何更新SML中的记录值?

Functional programming 如何更新SML中的记录值?,functional-programming,sml,smlnj,Functional Programming,Sml,Smlnj,我正在编写SML程序来更新列表中的记录。例如,我键入person\u name 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, pay

我正在编写SML程序来更新列表中的记录。例如,我键入person\u name

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} 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

请提出建议。

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

对于有getter的记录,因此
#fname(#name(#p employee))
获取要检查的字段,以确定这是要更新其姓氏的员工。但是记录不能给你同等的设置,所以你必须做出这些。如果你好奇的话,(Haskell)是解决这个问题的一般方法,但我不知道标准ML中有任何透镜的实现

我将继续并删除您的
员工
类型中的
列表
部分;如果您希望对多个员工进行建模,而不是说一个员工是多人,那么您可能需要一个
员工列表

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
至于设定器(你可以使用镜头自动衍生的设定器)

您可以编写这些,例如:

- 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."}, ... }]

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

对于有getter的记录,因此
#fname(#name(#p employee))
获取要检查的字段,以确定这是要更新其姓氏的员工。但是记录不能给你同等的设置,所以你必须做出这些。如果你好奇的话,(Haskell)是解决这个问题的一般方法,但我不知道标准ML中有任何透镜的实现

我将继续并删除您的
员工
类型中的
列表
部分;如果您希望对多个员工进行建模,而不是说一个员工是多人,那么您可能需要一个
员工列表

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
至于设定器(你可以使用镜头自动衍生的设定器)

您可以编写这些,例如:

- 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."}, ... }]

根据您的情况,此处可能适合提供参考资料

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

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和副本(传递给函数)都指向同一个字符串,我们在函数中修改该字符串。在任何一点上,我们实际上都不会更改任何记录中的任何数据,我们只是更改记录指向的数据。这是一个细微的区别,在本例中并没有真正的区别,但它可能会导致难以调试的奇怪错误。

根据您的情况,这里的参考可能是合适的

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

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和副本(传递给函数)都指向同一个字符串,我们在函数中修改该字符串。在任何一点上,我们实际上都不会更改任何记录中的任何数据,我们只是更改记录指向的数据。这是一个细微的区别,在本例中并没有真正的区别,但它可能会导致难以调试的奇怪错误。

SML中的值是不可变的,
\fname(#name(#p hd l)=“abc”
是一个比较。您需要用更新的记录构建一个新列表。(阅读模式匹配——它使一切变得不那么凌乱和更可读。)从比记录更简单的东西开始,比如整数。谢谢,我有点困惑。要仅更新lname,如果无法直接更新,我必须删除并以某种方式添加它。我这样做是为了更新lastname,但不起作用。有趣的UpdateName(fnm:string,lnm:string,[])=[]更新名称(fnm:string,lnm:string,x::xs)=(如果fnm=(#fname(#name(#px))),那么如果lnm(#lname(#name(#p(x)),那么lnm-else(#lname(#p(p(x#)name(#p(x#))::更新名称(fnm,lnm,lnm,xs)在SML代码中的值是不可变的,abc(#fnl
是一种比较。你需要用更新后的记录建立一个新的列表。(阅读模式匹配——它使一切变得不那么混乱,更易读。)从一些比记录更简单的东西开始,比如整数。谢谢,我有点困惑。要仅更新lname,如果无法直接更新,我必须以某种方式删除并添加它?我这样做是为了更新lastname,但不起作用。有趣的UpdateName(fnm:string,lnm:string,[])=[]|UpdateName(fnm:string,lnm:string,x::xs)=(如果fnm=(#fname(#name(#px)))那么如果lnm(#lname(#name(#p(x))),那么lnm else(#lname(#p