理解并使用Haskell的Integral类型类的基本概念

时间:2015-11-04 15:04:35

标签: haskell types typeclass

我正在学习一门专注于Haskell& amp; Prolog,我正在研究即将进行的测试。

我们获得了签名:

myList
  :: (Integral a)
  => [a]

我们必须创建一个变量myList,它将返回一个与标准正整数列表不同的无限列表,方法是通过向右移动两个位置来改变每个第三个元素的位置,从第一个元素开始之一。

例如,开头看起来像: 2,3,1,5,6,4,8,9,7..在包含正面元素的标准列表中。

我尝试用这样的代码来解决这个问题:

myList (x:y:z:xs)
  = y:z:x:(myList(xs))
myList [] = []
myList [x] = [x]

它提供了所需的结果,但它没有遵循签名。有人可以解释如何解决它,以便符合签名及其原因。

感谢。

2 个答案:

答案 0 :(得分:5)

该功能(事实上你完全正确的实施)

myList (x:y:z:xs) = y:z:x:(myList xs)
myList []         = []
myList [x]        = [x]

足够通用,不依赖于列表中元素的类型Integral a => a。因此,如果您让Haskell推断其类型,它将推断[a] -> [a]。如果将类型约束为Integral a => [a] -> [a],它仍然可以工作,但不那么通用,这将限制只使用整数类型。

以下是该原则的演示:

Prelude> :{
Prelude| let myList (x:y:z:xs) = y:z:x:(myList(xs))
Prelude|     myList [] = []
Prelude|     myList [x] = [x]
Prelude| :}
Prelude> :t myList
myList :: [a] -> [a]

Prelude> take 15 $ myList ['a'..]
"bcaefdhigkljnom"
Prelude> take 15 $ myList [1..]
[2,3,1,5,6,4,8,9,7,11,12,10,14,15,13]

Prelude> :{
Prelude| let myList :: Integral a => [a] -> [a]
Prelude|     myList (x:y:z:xs) = y:z:x:(myList(xs))
Prelude|     myList [] = []
Prelude|     myList [x] = [x]
Prelude| :}
Prelude> :t myList
myList :: Integral a => [a] -> [a]

Prelude> take 15 $ myList [1..]
[2,3,1,5,6,4,8,9,7,11,12,10,14,15,13]

Prelude> take 15 $ myList ['a'..]    
<interactive>:34:11:
    No instance for (Integral Char) arising from a use of ‘myList’
    In the second argument of ‘($)’, namely ‘myList ['a' .. ]’
    In the expression: take 15 $ myList ['a' .. ]
    In an equation for ‘it’: it = take 15 $ myList ['a' .. ]

所以重点是,两个定义都是等价的,并且能够做到和另一个一样做同样的事情,但受约束的类型签名是(并且我说不合理)比具有一般类型签名的那个有用。

如果赋值需要Integral a => [a] -> [a]类型的函数,那么您真正需要做的就是使用该类型签名简单地注释您已经拥有的函数。但是,没有(合理的/合理的)方式以某种方式指导Haskell从函数定义中推断出该类型,因为这需要以某种方式间接指示列表必须包含支持{{1}中的操作的类型的值} ... yada yada。

作为最后一点:你完全正确地实现了算法/算法,但在类型签名和普遍性概念上却做得不够。

编辑:如果你真正需要的不是一个函数而是一个列表(你的问题在这方面有点模棱两可),你需要做的就是重命名{{1例如IntegralmyList(我认为这是嵌套递归助手的典型名称)或其他东西(可以但不必隐藏在列表myList'中)然后将go传递给它,将结果分配给myList

[1..]

当然看起来是这样的,myList 确实是列表的一般签名(但不是最一般的,因为我被引导到myList :: Integral a => [a] myList = go [1..] where go (x:y:z:xs) = y:z:x:(go xs) go [] = [] go [x] = [x] 通过dfeuer的评论来实现,因为Integral a => [a]类型不能通过传递给函数的输入类型来确定,因为你总是传递(Enum a, Num a) => [a]

答案 1 :(得分:0)

如果我们想用正确的签名给出答案而不用手工提供任何签名,我们必须查看Integral类并查看它实现的方法。应用任何此类方法都可能会强制使用正确的签名。

Prelude> :i Integral
class (Real a, Enum a) => Integral a where
  quot :: a -> a -> a
  rem :: a -> a -> a
  div :: a -> a -> a
  mod :: a -> a -> a
  quotRem :: a -> a -> (a, a)
  divMod :: a -> a -> (a, a)
  toInteger :: a -> Integer

由于我们的序列与除以3的余数有关,divmod看起来很有希望。

在算术运动之后,我们得到类似

的东西
Prelude> let myList = map (\x -> x - x `mod` 3 + (x+2) `mod` 3 + 1) [0..]
Prelude> :t myList
myList :: Integral b => [b]