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