如何更新SML中的记录值?

How to update record value in SML?

我正在编写 SML 程序来更新 list.For 示例中的记录,我的类型为 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

求推荐。

您遇到了困难:更新深度嵌套的记录。

对于您有 getters 的记录,因此 #fname (#name (#p employee)) 获取您正在检查的字段以了解这是您要访问其姓氏的员工更新。但是记录不会授予您等效的 setters,因此您必须创建它们。如果你很好奇,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

至于 setters(您可以使用 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."}, ... }]

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

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

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

如果您打算经常修改一条记录中的数据,将它作为一个引用可能是个好主意,这样您的程序就不会在每次需要更改一个值时都重写内存(尽管在许多情况下 compiler/interpreter就能优化这个)。如果您的记录签名发生更改,它还使您不必重写 setter 函数。缺点是使用引用会给程序带来复杂性。

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