Haskell:对数据类型的算术运算

时间:2015-11-11 20:12:10

标签: haskell math comparison

我正在尝试理解数据类型,为此,我想尝试模拟整数值。我用以下方式定义它:

data Number = Zero | One | Two | Three deriving (Eq,Ord,Show)

基本上,我希望能够对这种类型的元素进行基本的算术运算。类似于

的东西
addNumber :: Number -> Number -> Number

调用addNumber One Two会给出Three作为结果。但是,我真的不确定如何正确地做到这一点。我假设通过比较Number相互之间可以做到这一点,但是为了得到任何东西,我需要能够以这个特定的顺序访问下一个数字,但我不知道你如何用给定的数据类型做到这一点。到目前为止,我正在做这样的事情:

getIntDex :: Number -> Int
getIntDex n = intDex n 0 nList

nList :: [Number]
nList = [Zero, One, Two, Three]

intDex :: Number -> Int -> [Number] -> Int
intDex e i (x:xs) = if((compare e x) == EQ)
            then i
            else intDex e (i+1) xs

至少将它“转换”成一个整数,所以我实际上可以用它做一些算术运算。然而,这感觉有点静态和整体错误(我可以通过开关或警卫或类似的东西更快地做到这一点。有更好的方法吗?

3 个答案:

答案 0 :(得分:3)

我假设您希望避免使用内置算法 - 如果没有,因为您观察到可以转换为内置数字,算术运算并转换回来,但这不是很令人满意的“学习Haskell“观点。

在新数据类型上定义新函数的标准方法是使用模式匹配。例如,一个天真的问题方法是列出所有输入Number对以及它们的总和:

add :: Number -> Number -> Number
add Zero  Zero  = Zero
add Zero  One   = One
add Zero  Two   = Two
add Zero  Three = Three
add One   Zero  = One
add One   One   = Two
-- ...

当然,从程序员的角度来看,这看起来有点令人生畏和锅炉板。另一方面,拆分一些功能可能会减少它;例如,您可以编写一个函数来添加一个并迭代它:

addOne :: Number -> Number
addOne Zero  = One
addOne One   = Two
addOne Two   = Three
addOne Three = Zero

add :: Number -> Number -> Number
add Zero  = id
add One   = addOne
add Two   = addOne . addOne
add Three = addOne . addOne . addOne

当然,效率会略低一些;并且您不希望为更大数量的类型执行此操作。但它需要更少的手指打字。 (无论如何,对于较大数字类型,你可能需要一个不同的实现而不是一个大的枚举 - 例如一个位流 - 但是我认为这不是重点。)

答案 1 :(得分:1)

如果您还派生Enum实例,则可以使用fromEnum :: Enum a => a -> InttoEnum :: Enum a => Int -> a在数据类型和与数据定义中的位置对应的整数之间进行转换。由于你在{0}位置有Zero,依此类推,这正是你想要进出的整数。

但你也应该考虑:addNumber Three Two的结果是什么?那里没有正确的值,因为你对数字的定义不会达到五。无论你设置什么上限,你都会有一个“部分”函数,对于它的域中的某些值是未定义的。也许对你来说这很好,如果你只是在对数据类型进行练习,但是通常在Haskell程序中我们试图避免部分函数,​​因为它们会在编译时“应该”被捕获的情况下导致运行时错误。例如,您可以返回Maybe Number而不是Number,如果没有有效答案,则返回Nothing。然后调用者可以明确地处理失败的可能性,而不是隐式地接受它并遭受异常。

答案 2 :(得分:1)

您的号码非常非结构化。因此,如果您想直接定义添加(而不是通过使用标准类型Int并使用标准+),则必须执行此操作:

addNumber Zero n = n -- Zero added to anything is n
addNumber One One = Two
addNumber One Two = Three
addNumber Two One = Three
addNumber _ _ = error "overflow, we only go up to Three"

详尽地列出每个案例(幸运的是,由于你只有Three,所以并不是很多!)。

您可以想象其他稍微多一些的结构数字,您可以使用该结构更经济地定义添加。着名的例子是

data N = Z | S N

...... Peano'的递归数据类型号。