Я пишу 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
Пожалуйста, предложите.
Начните с чего-то более простого, чем записи, например с целых чисел.
спасибо, я немного запутался. Чтобы обновить только имя, мне нужно как-то удалить и добавить его, если прямое обновление невозможно?
Я сделал это, чтобы обновить фамилию, но не работает. fun updateLname(fnm:string,lnm:string,[]) = [] | updateLname(fnm:string,lnm:string,x::xs)= ( if fnm= (#fname ( #name ( #px))) then if lnm <> (#lname ( #name ( #p ( x)) )) затем lnm else (#lname ( #name ( #p ( x))))) :: updateLname(fnm,lnm,xs)
Вы наткнулись на что-то сложное: обновление глубоко вложенной записи.
Для записей у вас есть добытчики, поэтому #fname (#name (#p employee))
получает поле, которое вы проверяете, чтобы знать, что это сотрудник, чью фамилию вы собираетесь обновить. Но записи не дают вам эквивалента сеттеры, так что вы должны сделать это. Если вам интересно, линзы (Haskell) — это общий способ решить эту проблему, но я не знаю ни одной реализации линз для Standard 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
Что касается сеттеры (тех, которые вы можете получить автоматически, используя линзы),
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."}, ... }]
Идея линз кажется интересной. Интересно, насколько возможно было бы реализовать это в прямом SML. Я не понимаю, почему нет, но, с другой стороны, я не знаю, что означает эта ссылка, когда она относится к «Шаблону Haskell».
Вы можете создавать линзы как часть этапа предварительной обработки. Если бы мне нужно было сделать такой препроцессор, я бы, вероятно, расширил работу препроцессора премл SML Мортена Бронс-Педерсена.
Большое спасибо за подробное объяснение, очень помогло.
В зависимости от вашей ситуации здесь могут быть уместны ссылки.
Для любых значений, которые вам может потребоваться изменить, вы можете сделать их ссылкой, т.е.
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 и копия (передаваемая в функцию) указывают на одну и ту же строку, и мы изменяем эту строку в функции. На самом деле мы ни в коем случае не изменяем какие-либо данные в любой записи, мы изменяем только данные, на которые указывают записи. Это тонкая разница, и она не имеет большого значения в этом примере, но может привести к странным ошибкам, которые трудно отладить.
Значения в SML неизменяемы, а
#fname(#name(#p hd l) = "abc"
— это сравнение. Вам нужно создать новый список с обновленной записью. (И почитайте о сопоставлении с образцом — это делает все менее запутанным и более читабельным.)