简体   繁体   English

了解和使用Haskell积分类型类的基本概念

[英]Understand and use the basic concept of Haskell's Integral typeclass

I'm taking a course which focuses on Haskell & Prolog and there's an upcoming test for which i'm studying. 我正在上一门针对Haskell&Prolog的课程,并且我正在学习即将进行的测试。

We're given a signature: 我们得到了签名:

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

And we have to create a variable myList which will return an infinite list which is different from standard positive integer list by changing the position of every third element by moving it two positions to the right, starting from the first one. 并且我们必须创建一个变量myList ,该变量将通过从第一个元素向右移动两个位置来改变每个第三个元素的位置,从而返回与标准正整数列表不同的无限列表。

So for example, the beginning would look like: 2,3,1,5,6,4,8,9,7.. on a standard list which contains positive elements. 因此,例如,开始看起来像:在包含肯定元素的标准列表上的2,3,1,5,6,4,8,9,7..

I tried to solve this with a code like this: 我试图用这样的代码来解决这个问题:

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

It gives the result needed but it's not following the signature. 它提供所需的结果,但不遵循签名。 Could someone explain how to solve it so it would fit the signature and why it does. 有人可以解释如何解决它,使其适合签名以及为什么这样做。

Thanks. 谢谢。

The function (the implementation of which you got perfectly right, as a matter of fact) 函数(事实上,您完全正确的实现)

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

is generic enough not to depend on the type of the elements in the list to be Integral a => a . 是足够通用的,不依赖于列表中元素的类型为Integral a => a So if you let Haskell infer its type, it will infer [a] -> [a] . 因此,如果让Haskell推断其类型,它将推断[a] -> [a] If you constraint the type to Integral a => [a] -> [a] , it will still work, but be less generic, which will limit uses to just integral types. 如果将类型限制为Integral a => [a] -> [a] ,它将仍然有效,但通用性较低,这将限制使用仅整数类型。


Here's a demonstration of the principle: 这是该原理的演示:

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]

but

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

So the point is , both definitions are equivalent and capable of doing the same thing just as well as the other, but the constrained type signature is (and I'd say unjustifiably) less useful than the one with the general type signature. 因此,关键是 ,这两个定义是等效的,并且能够完成相同的事情,但是受约束的类型签名的作用(并且我会说得不合理)比具有通用类型签名的定义少。

If the assignment demands a function of type Integral a => [a] -> [a] , all you really need to do is simply annotate the function you already have with exactly that type signature. 如果赋值需要一个类型为Integral a => [a] -> [a]的函数,那么您真正需要做的就是简单地使用该类型签名来注释您已经拥有的函数。 There is, however, no (reasonable/rational) way to somehow guide Haskell to infer that type from the function definition, as that would necessitate somehow indirectly indicating that the list must contain values of a type that support the operations in Integral ... yada yada. 但是,没有任何(合理/合理的)方式可以指导Haskell从函数定义中推断出该类型,因为这将以某种方式间接表明该列表必须包含支持Integral操作的类型的值。 yada yada。

As a final note : you got the implementation/algorithm perfectly right, but fell short on type signatures and the notion of generality. 最后一点 :您的实现/算法完全正确,但是在类型签名和通用性概念上不够。


EDIT: if what you actually need is not a function but a list (your question is a bit ambiguous in this respect), all you need to do is rename the below definition of myList to eg myList' or go (which I think is quite a typical name for nested recursive helpers) or something (which can but doesn't have to be an hidden within the list myList ) and then pass [1..] to it, assigning the result to myList : 编辑:如果您实际需要的不是函数而是列表(您的问题在这方面有点模棱两可),您需要做的就是将myList的以下定义重命名为例如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]

of course looked this way, Integral a => [a] is indeed quite general a signature for the list (but not the most general, which would be (Enum a, Num a) => [a] as I was led to realize by dfeuer's comment), because the type a cannot be determined by the type of the input passed to the function, because you're always passing [1..] . 当然看了这样, Integral a => [a]确实列表中相当普遍的签名(但不是最普遍的,这将是(Enum a, Num a) => [a]因为我是领导实现(根据dfeuer的评论),因为类型a不能由传递给函数的输入的类型确定,因为您总是在传递[1..]

If we want to give an answer with the right signature without supplying any signatures by hand, we have to look at the Integral class and look which methods it implements. 如果我们想给出具有正确签名的答案, 而无需手工提供任何签名,则必须查看Integral类,并查看它实现了哪些方法。 Applying any such method is likely to force the right signature. 应用任何此类方法都可能会强制执行正确的签名。

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

Since our sequence has something to do with remainders of division by 3, div and mod look promising. 由于我们的序列与除以3的余数有关,因此divmod看起来很有希望。

After some fiddling with arithmetic, we arrive at something like 经过对算术的摸索之后,我们得出类似

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM