通过枚举创建函数
问题描述:
我刚开始学习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
但显然你不能这样做。所以我的问题是:有没有一种方法可以生成其余的反向函数或者更好的方法来创建这个函数?
非常感谢。
答
如果Up
和Down
等之间的配对是一个重要的功能,那么也许这种知识应该反映在类型。
data Axis = UpDown | LeftRight | FrontBack
data Sign = Positive | Negative
data Dir = Dir Axis Sign
inv
现在很容易。
答
你对这个函数的索引有一个封闭的解决方案吗?如果是这样,是的,你可以使用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。
答
很高兴知道,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
这是更好的。准确捕捉类型中的核心概念(维度和方向)。 – 2011-06-06 02:22:59
有道理。在“Sign”上找到“inv Positive”的反转要比在所有的“Dir”上找到“inv Right”的反转要好。因此,干净地做一个'反向'的唯一方法是让你的反转非常小...... – rcbuchanan 2011-06-06 02:33:55
@ rcb451,你错过了这一点。 “Sign”具有较少数据构造函数的事实是偶然的(尽管当然有益)。关键的观察结果是,“左”与“右”之间的密切关系应体现在它们的类型中,同样也反映出(例如)“左”与“顶”的远距离关系。类型系统可以帮助不变式:更多的类型可以表达更精确的不变量。 – Lambdageek 2011-06-06 22:33:15