[英]Haskell: arithmetic operations on data types
我试图了解数据类型,并且要这样做,我想尝试模拟整数值。 我用以下方式定义它:
data Number = Zero | One | Two | Three deriving (Eq,Ord,Show)
因此,基本上,我希望能够使用这种类型的元素进行基本的算术运算。 类似于
addNumber :: Number -> Number -> Number
像addNumber一二这样调用将得到三。 但是,我真的不确定如何正确执行此操作。 我认为可以通过相互比较Number来做到这一点,但是要从中得到任何东西,我需要能够以这种特定顺序访问下一个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
至少将其“转换”为整数,以便实际上可以对其进行算术运算。 但是,这感觉有点静态,整体上是错误的(我可能可以通过开关,防护罩或类似的东西更快地完成此操作。是否有更好的方法?
我假设您希望避免使用内置算术-否则,因为您观察到可以将其转换为内置数字,执行算术并转换回去,但是从“学习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
当然,这会稍微降低效率。 并且您不想对较大的数字类型执行此操作。 但这需要更少的手指打字。 (无论如何,对于较大的数字类型,您可能想要的是不同于大枚举的实现(例如,比特流),但是我认为这是重点。)
如果还派生一个Enum
实例,则可以使用fromEnum :: Enum a => a -> Int
toEnum :: Enum a => Int -> a
fromEnum :: Enum a => a -> Int
和toEnum :: Enum a => Int -> a
在数据类型和与它们在数据定义中位置相对应的整数之间进行转换。 既然你有Zero
在第0位置,等等,这将是正是你想要什么要和整数。
但是您还应该考虑: addNumber Three Two
的结果是什么? 那里没有正确的值,因为您对数字的定义不会达到5。 无论您设置了什么上限,您都将拥有一个“部分”函数,该函数在其域中的某些值上是未定义的。 如果您只是对数据类型进行练习,那么这可能对您来说很好,但是通常在Haskell程序中,我们会尽量避免使用部分函数,因为在“应该”在编译时被捕获的情况下,它们可能会导致运行时错误。 例如,您可以返回Maybe Number
而不是Number
,如果没有有效答案,则返回Nothing
。 然后,调用者可以显式应对失败的可能性,而不是隐式接受失败并遭受异常。
您的电话号码非常混乱。 这样,如果您想直接定义加法(而不是通过标准类型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”数字的递归数据类型。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.