我有两个定义
data Person = Person Name Surname [Projects]
data Employees = Employees [Person] Salary
我希望通过使用员工中的人员列表来获取给定姓名的相应人员,并在相应的人员中进行一些更改。例如,我想将项目添加到我想要执行项目的人员的项目列表中。我怎么能在Haskell中做到这一点?
函数签名应该像
myfunction :: Employees -> Name -> Employees
答案 0 :(得分:1)
我们可以通过模式匹配和使用ext_modules
直接写出来。
在这种情况下,我们可以从编写一个小帮助函数开始,对单个map
值执行本地修改,并使用Person
之类的东西将我们的转换应用于一大堆其他数据。所以我们可能有
map
然后我们可以编写更多代码来将此转换统一应用于我们的 updatePerson :: Name -> Person -> Person
updatePerson targetName (Person name sur projects)
| targetName == name = Person name sur (NewProject : project)
| otherwise = Person name sur projects
数据
Employee
在您的方案中,这是正确的做法。但是,我们可能会大量访问这些数据结构的一部分,因此对它进行一些优化是有意义的!通过编写一些辅助函数来操作我们的数据类型,我们可以看到如何获得更通用的方法:
myFunction :: Person -> Person
myFunction name (Employee ps salary) =
Employee (map (updatePerson name) ps) salaray
然后写一些类似
的内容personName :: Person -> Name
personName (Person name sur projects) = name
modifyProjects :: ([Project] -> [Project]) -> Person -> Person
modifyProjects f (Person name sur p) = Person name sur (f p)
现在我用辅助函数而不是直接模式匹配来编写它,因为它建议我们使用Haskell的内置功能来生成这些函数,记录。
myFunction name (Employee people salary) = Employee (map go people) salary
where go p | personName p == name = modifyProject ... p
| otherwise = p
然后我们可以做类似
的事情data Person = Person { _name :: Name
, _surname :: Surname
, _projects :: [Projects]
data Employees = Employees {_people :: [Person], _salary :: Salary}
但现在这仍然很笨重,Haskell的记录系统有点难以使用。这实际上是许多疯狂强大的库实现“镜头”的推动力,它使我们能够使用一套通用组合器实现这些记录,如转换。我提供相应的镜头代码作为Haskell扩展的一个例子来做一些很酷的事情,但我上面提到的3个替代方案中的任何一个都非常精细且复杂得多
myFunction name e = e {_people = map go (_people e}}
where go p | _name p == name = p {_projects = New :: _projects p}
| otherwise = p