通过枚举创建函数

问题描述:

我刚开始学习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 

但显然你不能这样做。所以我的问题是:有没有一种方法可以生成其余的反向函数或者更好的方法来创建这个函数?

非常感谢。

如果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

你对这个函数的索引有一个封闭的解决方案吗?如果是这样,是的,你可以使用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。列举案例绝对没问题!其他人将不得不在一些点阅读你的代码...

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]] 

我不认为我会推荐这一点,但在我的脑海中简单的答案将是补充一点:

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

很高兴知道,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