2016-09-19 121 views
0

下面是一個簡單的例子,我用較大的記錄和函數進行一些數據操作。在我的實際代碼name就像是一個訪問:當過濾記錄時從邏輯轉換中解耦邏輯

name = snd . fst . fst

不過我並不滿足於可讀性name不是隻有一件事我要覈對。基於這個例子,是否有一種更簡單,慣用的方法將邏輯與場轉換分開?問題的

> data Athlete = Athlete { name :: String } deriving Show 

> let registered = map Athlete ["John", "Mike"] 
> let input = map Athlete ["John", "Ann"] 

> filter (not . (`elem` (map name registered)) . name) input 
[Athlete {name = "Ann"}] 

部分是謂詞不僅僅取決於參數本身,並不一定檢查的Athlete平等。由於我明確不想爭取平等的測試,理想情況下,邏輯部分應該如果input類型改爲維持不變如下:

> data AthleteInput = AthleteInput { name :: String } deriving Show 
+3

我覺得這裏有一個有趣的問題,但你提出的問題並不清楚。 – leftaroundabout

+0

類似於:'let func f input registered = filter(not。(\'elem \'(map f registered))。f)input'?不是100%確定要問什麼。 – Ophirr

+0

我覺得你想要結構記錄 - 內置的記錄系統不會幫助你,但有很多庫實現記錄可供選擇。 – user2407038

回答

1

我建議是這樣的:我們將使用Data.Map給共享兩個映射一個關鍵空間。構建這些Map將對應於「謂詞」部分;這將與計算「相交」或其他組合功能完全分離。我們從您的設置開始:

import Data.Map (Map) 
import qualified Data.Map as M 

data Athlete = Athlete { name :: String } deriving Show 
registered = map Athlete ["John", "Mike"] 
input = map Athlete ["John", "Ann"] 

現在我們將構建我們的兩張地圖。爲了說明,我會命名它們;但在你的代碼中,你可以選擇內聯他們的定義,以避免選擇一個糟糕的名字。

registeredMap :: Map String Athlete 
registeredMap = M.fromList [(name athlete, athlete) | athlete <- registered] 

inputMap :: Map String Athlete 
inputMap = M.fromList [(name athlete, athlete) | athlete <- input] 

我在這裏強調,我們有發生使用name作爲選擇這裏這兩種情況下,和發生使用相同類型Athlete作爲值類型的兩個地圖,但這些都不是下一部分需要 - 只是巧合。關鍵的非重合位與關鍵空間是相同的,否則相交就沒有意義。這解決了您以後需要更改一個訪問器或另一個訪問器或更改一種底層類型或另一種訪問器的設計擔憂。

現在的交集就像使用現有的庫函數intersection一樣簡單。

registeredInputAthletes :: [Athlete] 
registeredInputAthletes = M.elems (M.intersection registeredMap inputMap) 

這有價值[Athlete { name = "John" }]

如果是需要,也可以使用intersectionWith來組合兩個包含的值,而不是保留左邊的參數值而忽略右邊的參數值。

+0

我沒注意到'b'不受限制:'intersection:Ord k => Map k a - > Map k b - > Map k a'。看起來'Map k b'中的'Map'是一個過度約束的參數,允許使用類型唯一性進行高效的計算。 – sevo