我正在编写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
请提出建议。
答案 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和一个副本(传递给函数)都指向相同的字符串,然后在函数中修改该字符串。实际上,我们实际上并没有更改任何一条记录中的任何数据,我们只是更改了记录指向的数据。这是一个细微的差异,在此示例中并没有真正的差异,但是它会导致难以调试的奇怪错误。