Как обновить значение записи в SML? - PullRequest
0 голосов
/ 24 марта 2019

Я пишу программу 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 ]

2 голосов
/ 25 марта 2019

Вы наткнулись на что-то сложное: обновление глубоко вложенной записи.

Для записей у вас есть получатели , поэтому #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."}, ... }]
0 голосов
/ 20 апреля 2019

В зависимости от вашей ситуации, ссылки могут быть уместны здесь.

Для любых значений, которые вам может потребоваться изменить, вы можете сделать их ссылкой, т.е.

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 и копия (передаваемая в функцию) указывают на одну и ту же строку, и мы модифицируем эту строку в функции. Мы ни в коем случае не меняем данные в любой записи, мы только меняем данные, на которые указывают записи. Это небольшая разница, и в данном примере это не имеет большого значения, но может привести к странным ошибкам, которые трудно отладить.

...