2011-06-06 69 views
13

我剛開始學習Haskell。我想我已經掌握了基礎知識,但我想確保我實際上也在強迫自己在功能上進行思考。通過枚舉創建函數

data Dir = Right | Left | Front | Back | Up | Down deriving (Show, Eq, Enum) 
inv Right = Left 
inv Front = Back 
inv Up = Down 

無論如何,我想要做JIST是創建一個功能,每個「目錄」,它的對立面/ INV之間進行映射。我知道我可以輕鬆地繼續這3條線,但我不禁想知道是否有更好的方法。我試圖加入:

inv a = b where inv b = a 

但顯然你不能這樣做。所以我的問題是:有沒有一種方法可以生成其餘的反向函數或者更好的方法來創建這個函數?

非常感謝。

回答

18

如果UpDown等之間的配對是一個重要的功能,那麼也許這種知識應該反映在類型。

data Axis = UpDown | LeftRight | FrontBack 
data Sign = Positive | Negative 
data Dir = Dir Axis Sign 

inv現在很容易。

+0

這是更好的。準確捕捉類型中的核心概念(維度和方向)。 – 2011-06-06 02:22:59

+0

有道理。在「Sign」上找到「inv Positive」的反轉要比在所有的「Dir」上找到「inv Right」的反轉要好。因此,乾淨地做一個'反向'的唯一方法是讓你的反轉非常小...... – rcbuchanan 2011-06-06 02:33:55

+1

@ rcb451,你錯過了這一點。 「Sign」具有較少數據構造函數的事實是偶然的(儘管當然有益)。關鍵的觀察結果是,「左」與「右」之間的密切關係應體現在它們的類型中,同樣也反映出(例如)「左」與「頂」的遠距離關係。類型系統可以幫助不變式:更多的類型可以表達更精確的不變量。 – Lambdageek 2011-06-06 22:33:15

2

你對這個函數的索引有一個封閉的解決方案嗎?如果是這樣,是的,你可以使用Enum派生來簡化事情。例如,

import Prelude hiding (Either(..)) 

data Dir = Right 
     | Front 
     | Up 

     | Left 
     | Back 
     | Down 
    deriving (Show, Eq, Ord, Enum) 

inv :: Dir -> Dir 
inv x = toEnum ((3 + fromEnum x) `mod` 6) 

注意,這依賴於構造的訂購!

*Main> inv Left 
Right 
*Main> inv Right 
Left 
*Main> inv Back 
Front 
*Main> inv Up 
Down 

這非常類C,利用構造函數的順序,並且是非Haskelly。妥協是使用更多類型,定義構造函數和它們的鏡像之間的映射,避免使用算術。

import Prelude hiding (Either(..)) 

data Dir = A NormalDir 
     | B MirrorDir 
    deriving Show 

data NormalDir = Right | Front | Up 
    deriving (Show, Eq, Ord, Enum) 

data MirrorDir = Left | Back | Down  
    deriving (Show, Eq, Ord, Enum) 

inv :: Dir -> Dir 
inv (A n) = B (toEnum (fromEnum n)) 
inv (B n) = A (toEnum (fromEnum n)) 

例如,

*Main> inv (A Right) 
B Left 
*Main> inv (B Down) 
A Up 

所以至少我們不必做算術運算。並且類型區分鏡子案件。但是,這是非Haskelly。列舉案例絕對沒問題!其他人將不得不在一些點閱讀你的代碼...

2
pairs = ps ++ map swap ps where 
    ps = [(Right, Left), (Front, Back), (Up, Down)] 
    swap (a, b) = (b, a) 

inv a = fromJust $ lookup a pairs  

[編輯]

還是怎麼樣?

inv a = head $ delete a $ head $ dropWhile (a `notElem`) 
     [[Right,Left],[Front,Back],[Up,Down]] 
1

我不認爲我會推薦這一點,但在我的腦海中簡單的答案將是補充一點:

inv x = fromJust $ find ((==x) . inv) [Right, Front, Up] 

我無法抗拒調整Landei的答案適合我的風格;這裏是一個類似的,稍微更推薦的解決方案,不需要其他定義:

inv a = fromJust $ do pair <- find (a `elem`) invList 
         find (/= a) pair 
    where invList = [[Right, Left], [Up, Down], [Front, Back]] 

它使用Maybe monad。

+0

嗯?錯誤?這並不比陳述'inv x = inv x'好。 – comonad 2011-06-07 02:25:28

+1

我的意思是在他爲'inv Right','inv Front','inv Up'寫過的定義之後加上這個。 [請親自看看它是否有效](https://ideone.com/Uc9Px)。 – 2011-06-07 18:32:27

+0

啊,好的。有了這些額外的線,它的工作。 – comonad 2011-06-24 23:03:54

1

很高興知道,Enum eration從零開始。

助記符:fmap fromEnum [False,True] == [0,1]


import Data.Bits(xor) 

-- Enum:  0 1   2 3  4 5 
data Dir = Right | Left | Front | Back | Up | Down 
      deriving (Read,Show,Eq,Ord,Enum,Bounded) 

inv :: Dir -> Dir 
inv = toEnum . xor 1 . fromEnum