亚洲在线久爱草,狠狠天天香蕉网,天天搞日日干久草,伊人亚洲日本欧美

為了賬號安全,請及時綁定郵箱和手機立即綁定
已解決430363個問題,去搜搜看,總會有你想問的

是否有Haskell慣用法來更新嵌套數據結構?

是否有Haskell慣用法來更新嵌套數據結構?

人到中年有點甜 2019-11-19 10:21:50
假設我有以下數據模型,用于跟蹤棒球運動員,球隊和教練的統計信息: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 -> BBTeamaddManagerSteak (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 -> BBTeamaddManStk 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-因此,我認為我試圖update-in在函數式編程,Haskell和靜態類型化的上下文中進行理解。
查看完整描述

3 回答

?
波斯汪

TA貢獻1811條經驗 獲得超4個贊

正如Lambdageek所建議的,這就是使用語義編輯器組合器(SEC)的方式。


首先是幾個有用的縮寫:


type Unop a = a -> a

type Lifter p q = Unop p -> Unop q

這Unop是一個“語義編輯器”,并且Lifter是語義編輯器的組合器。一些起重器:


onManager :: Lifter Coach BBTeam

onManager f (BBTeam n m p) = BBTeam n (f m) p


onDiet :: Lifter Diet Coach

onDiet f (Coach n c d) = Coach n c (f d)


onStakes :: Lifter Integer Diet

onStakes f (Diet n s e) = Diet n (f s) e

現在,只需簡單地使SEC組成即可說出您想要的內容,即在(經理)經理的飲食中加1:


addManagerSteak :: Unop BBTeam

addManagerSteak = (onManager . onDiet . onStakes) (+1)

與SYB方法相比,SEC版本需要額外的工作來定義SEC,而我僅在此示例中提供了所需的內容。SEC允許有針對性的應用程序,如果玩家節食但我們不想對其進行調整,這將很有幫助。也許還有一種很漂亮的SYB方法來處理這種區別。


編輯:這是基本SEC的替代樣式:


onManager :: Lifter Coach BBTeam

onManager f t = t { manager = f (manager t) }


查看完整回答
反對 回復 2019-11-19
?
素胚勾勒不出你

TA貢獻1827條經驗 獲得超9個贊

稍后,您可能還需要看一下一些通用的編程庫:當數據的復雜性增加并且發現自己編寫了更多的樣板代碼(例如,增加了球員,教練的飲食和看守者的啤酒含量)時,即使是不太冗長的形式,也仍然是樣板。 SYB可能是最著名的庫(Haskell Platform附帶)。實際上,有關SYB的原始論文使用了非常相似的問題來演示該方法:


考慮以下描述公司組織結構的數據類型。公司分為部門。每個部門都有一個經理,并且由一組子部門組成,其中一個部門可以是一個雇員或一個部門。經理和普通雇員都是領薪的人。


[跳過]


現在假設我們想將公司中每個人的薪水提高指定的百分比。也就是說,我們必須編寫函數:


增加::浮動->公司->公司


(其余內容在論文中-建議閱讀)


當然,在您的示例中,您只需要訪問/修改一個微小數據結構的一部分,因此它不需要通用方法(仍然在下面為您的任務提供基于SYB的解決方案),但是一旦您看到重復的代碼/訪問/模式您想檢查此修改或其他通用編程庫。


{-# LANGUAGE DeriveDataTypeable #-}


import Data.Generics


data BBTeam = BBTeam { teamname :: String, 

manager :: Coach,

players :: [BBPlayer]}  deriving (Show, Data, Typeable)


data Coach = Coach { coachname :: String, 

favcussword :: String,

 diet :: Diet }  deriving (Show, Data, Typeable)


data Diet = Diet { dietname :: String, 

steaks :: Integer, 

eggs :: Integer}  deriving (Show, Data, Typeable)


data BBPlayer = BBPlayer { playername :: String, 

hits :: Integer,

era :: Double }  deriving (Show, Data, Typeable)



incS d@(Diet _ s _) = d { steaks = s+1 }


addManagerSteak :: BBTeam -> BBTeam

addManagerSteak = everywhere (mkT incS)


查看完整回答
反對 回復 2019-11-19
  • 3 回答
  • 0 關注
  • 406 瀏覽
慕課專欄
更多

添加回答

舉報

0/150
提交
取消
微信客服

購課補貼
聯系客服咨詢優惠詳情

幫助反饋 APP下載

慕課網APP
您的移動學習伙伴

公眾號

掃描二維碼
關注慕課網微信公眾號