是否有用于更新嵌套数据结构的 Haskell 习惯用法? [英] Is there a Haskell idiom for updating a nested data structure?

查看:30
本文介绍了是否有用于更新嵌套数据结构的 Haskell 习惯用法?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

假设我有以下数据模型,用于跟踪棒球运动员、球队和教练的统计数据:

Let's say I have the following data model, for keeping track of the stats of baseball players, teams, and coaches:

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)

现在让我们假设经理,他们通常是牛排狂热者,想要吃更多的牛排——所以我们需要能够增加经理饮食中的牛排含量.下面是这个函数的两种可能的实现:

Now let's say that managers, who are usually steak fanatics, want to eat even more steak -- so we need to be able to increase the steak content of a manager's diet. Here are two possible implementations for this function:

1) 这使用了大量的模式匹配,我必须让所有构造函数的所有参数顺序都正确......两次.它似乎无法很好地扩展或非常易于维护/可读.

1) This uses lots of pattern matching and I have to get all of the argument ordering for all of the constructors right ... twice. It seems like it wouldn't scale very well or be very maintainable/readable.

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 的记录语法提供的所有访问器,但我认为它也很丑陋和重复,并且难以维护和阅读.

2) This uses all of the accessors provided by Haskell's record syntax, but it is also ugly and repetitive, and hard to maintain and read, I think.

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 社区中更受欢迎?有没有更好的方法来做到这一点(在保持上下文的同时修改数据结构深处的值)?我不担心效率,只担心代码优雅/通用性/可维护性.

My question is, is one of these better than the other, or more preferred within the Haskell community? Is there a better way to do this (to modify a value deep inside a data structure while keeping the context)? I'm not worried about efficiency, just code elegance/generality/maintainability.

我注意到 Clojure 中存在针对此问题(或类似问题?)的问题:update-in -- 所以我想我正在尝试理解 update-in 在函数式编程、Haskell 和静态类型的上下文中.

I noticed there is something for this problem (or a similar problem?) in Clojure: update-in -- so I think that I'm trying to understand update-in in the context of functional programming and Haskell and static typing.

推荐答案

记录更新语法是编译器的标准配置:

Record update syntax comes standard with the compiler:

addManStk team = team {
    manager = (manager team) {
        diet = (diet (manager team)) {
             steaks = steaks (diet (manager team)) + 1
             }
        }
    }

太糟糕了!但是有更好的方法.Hackage 上有几个包实现了功能引用和镜头,这绝对是你想要做的.例如,对于 fclabels 包,您可以在所有记录名称前放置下划线,然后写

Terrible! But there's a better way. There are several packages on Hackage that implement functional references and lenses, which is definitely what you want to do. For example, with the fclabels package, you would put underscores in front of all your record names, then write

$(mkLabels ['BBTeam, 'Coach, 'Diet, 'BBPlayer])
addManStk = modify (+1) (steaks . diet . manager)

于 2017 年编辑添加:如今,人们对 lens 包是一个广泛的共识特别好的实现技术.虽然它是一个非常大的包,但在网络的各个地方也有非常好的文档和介绍材料.

Edited in 2017 to add: these days there is broad consensus on the lens package being a particularly good implementation technique. While it is a very big package, there is also very good documentation and introductory material available in various places around the web.

这篇关于是否有用于更新嵌套数据结构的 Haskell 习惯用法?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆