如何在SML中更新记录值?

时间:2019-03-24 05:58:55

标签: functional-programming sml smlnj

我正在编写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

请提出建议。

2 个答案:

答案 0 :(得分:2)

您偶然发现了一些困难:更新深层嵌套的记录。

对于记录,您有 getters ,因此#fname (#name (#p employee))获取要检查的字段,以知道这是您要更新其姓氏的员工。但是记录不会授予您等效的设置者,因此您必须进行设置。如果您感到好奇,lenses(Haskell)是解决此问题的通用方法,但是我不知道标准ML镜头的任何实现方式。

我将继续删除您的list类型的employee部分;如果您想建模多个员工,而不是说一个员工是多个人,那么您可能应该使用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

关于设置者(可以使用 lens 自动获得的设置者)

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

答案 1 :(得分:0)

根据您的情况,此处可能需要参考。

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

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

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

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