假设我有以下数据模型,用于跟踪棒球运动员,球队和教练的统计数据:
data BBTeam = BBTeam { teamname :: String,
manager :: Coach,
players :: [BBPlayer] }
deriving (Show)
data Coach = Coach { coachname :: String,
favcussword :: String,
diet :: Diet }
deriving (Show)
data Diet = Diet { dietname :: String,
steaks :: Integer,
eggs :: Integer }
deriving (Show)
data BBPlayer = BBPlayer { playername :: String,
hits :: Integer,
era :: Double }
deriving (Show)
字符串
现在让我们假设经理们,他们通常是牛排的狂热爱好者,想要吃更多的牛排--所以我们需要能够增加经理饮食中的牛排含量。以下是此函数的两种可能实现方式:
1)这使用了大量的模式匹配,我必须正确地获得所有构造函数的所有参数顺序。看起来它不能很好地扩展,也不能很好地维护/可读性。
addManagerSteak :: BBTeam -> BBTeam
addManagerSteak (BBTeam tname (Coach cname cuss (Diet dname oldsteaks oldeggs)) players) = BBTeam tname newcoach players
where
newcoach = Coach cname cuss (Diet dname (oldsteaks + 1) oldeggs)
型
2)这使用了Haskell的记录语法提供的所有访问器,但我认为它也很难看和重复,并且很难维护和阅读。
addManStk :: BBTeam -> BBTeam
addManStk team = newteam
where
newteam = BBTeam (teamname team) newmanager (players team)
newmanager = Coach (coachname oldcoach) (favcussword oldcoach) newdiet
oldcoach = manager team
newdiet = Diet (dietname olddiet) (oldsteaks + 1) (eggs olddiet)
olddiet = diet oldcoach
oldsteaks = steaks olddiet
型
我的问题是,在Haskell社区中,其中一个比另一个更好,还是更受欢迎?有没有更好的方法来做到这一点(修改数据结构内部的值,同时保持上下文)?我不担心效率,只担心代码的优雅性/通用性/可维护性。
我注意到有一些东西为这个问题(或类似的问题?)在Clojure中:update-in
--所以我认为我试图在函数式编程、Haskell和静态类型的背景下理解update-in
。
4条答案
按热度按时间xurqigkl1#
记录更新语法是编译器的标准语法:
字符串
太可怕了!但还有更好的办法。Hackage上有几个实现函数引用和镜头的包,这绝对是你想做的。例如,对于fclabels包,您可以在所有记录名前面加上下划线,然后写
型
2017年编辑补充:目前,对于X1 E1 F1 X包是一种特别好的实现技术存在广泛的共识。虽然它是一个非常大的包,但在网络上的各个地方也有非常好的文档和介绍性材料。
9rnv2umw2#
下面是如何使用语义编辑器组合子(SEC),就像Lambdageek建议的那样。
首先是几个有用的缩写:
字符串
这里的
Unop
是一个“语义编辑器”,而Lifter
是语义编辑器的组合子。一些升降机:型
现在简单地组成SEC,说出你想要的,即在经理(团队)的饮食中增加1:
型
与SYB方法相比,SEC版本需要额外的工作来定义SEC,在本例中我只提供了需要的SEC。美国证券交易委员会允许有针对性的应用,这将是有益的,如果球员有饮食,但我们不想调整他们。也许有一个漂亮的SYB的方式来处理这种区别。
型
tyky79it3#
稍后,您可能还想看看一些通用编程库:当你的数据复杂性增加,你发现自己写了更多的样板代码(比如增加球员的牛排含量,教练的饮食和观察员的啤酒含量),即使在不那么冗长的形式下,这仍然是样板代码。SYB可能是最知名的库(并随Haskell平台提供)。事实上,original paper on SYB使用非常类似的问题来演示该方法:
考虑以下描述公司组织结构的数据类型。公司分成几个部门。每个部门都有一个经理,并由子单元的集合组成,其中单元可以是单个员工或部门。无论是管理者还是普通员工,都只是领取工资的人。
[跳过]
现在假设我们想把公司里每个人的工资都增加一个特定的百分比。也就是说,我们必须编写函数:
增加::浮动->公司->公司
(the其余的都在报纸上-建议阅读)
当然,在你的例子中,你只需要访问/修改一个微小的数据结构的一部分,所以它不需要通用的方法(仍然是基于SYB的解决方案为您的任务是下面),但一旦你看到重复的代码/模式的访问/修改,你可能会想检查这个或other通用编程库。
字符串
q9yhzks04#
现代的解决方案是record dot syntax,在过去的几年里慢慢增加了as compiler extensions。
目前,您需要GHC 9.2这样做:
字符串
您也可以使用新的模式匹配语法(
NamedFieldPuns
和RecordWildCards
)来匹配steaks
而不是team
,并使用 that 而不是team.manager.diet.steaks
,但它会更长。此外,
succ
是一个比+ 1
更通用的函数,在Haskell中,所有变量都是常量,因此直接使用+=
语法可能会产生误导。所以其他的方法,特别是透镜包(它一直是一个丑陋的黑客,试图在错误的地方解决问题(作为一个库,而不是修复语言语法)出于简单的必要性,而且,如果出了什么问题,就会给出无法理解的错误消息)被认为是过时的。
您甚至可以使用
{-# LANGUAGE NoTraditionalRecordSyntax #-}
禁用旧的内置语法。