Haskell班次列表

时间:2019-05-13 08:44:45

标签: haskell recursion

我想将列表向左移动n个元素。如果要将顺序从(x:xs)更改为(xs:x),为什么会出错?

shift n list@(x:xs)
    | n == 0 = list
    | otherwise = shift (n-1) (xs:x) -- (xs:x) error
  

进行检查:无法构造无限类型:a〜[a]

我不知道如何解释此错误。也许有人可以帮助我。 非常感谢。

  

编辑:如前所述,正确使用的术语是旋转而不是移位

2 个答案:

答案 0 :(得分:6)

  

如果要将顺序从(x:xs)更改为(xs:x),为什么会出错?

因为类型不匹配。 (:)的类型为(:) :: a -> [a] -> [a]。因此,它期望元素x(类型a)和包含其余元素的列表(类型[a])。您不能仅以相反的方式使用(:)

您可以使用(++) :: [a] -> [a] -> [a]将两个列表连接在一起。因此,我们可以通过从列表中删除n个元素并将其与列表中的前n个元素串联到此来向左旋转。

rotateL :: Int -> [a] -> [a]
rotateL 0 list = list
rotateL n list | n < 0 = error "Negative index"
               | otherwise = drop n list ++ take n list

或者我们可以像@YannVernier所说的那样使用splitAt :: Int -> [a] -> ([a], [a])

rotateL :: Int -> [a] -> [a]
rotateL 0 list = list
rotateL n list | n < 0 = error "Negative index"
               | otherwise = lb ++ la
    where (la, lb) = splitAt n list

,或者不提及list参数:

rotateL :: Int -> [a] -> [a]
rotateL 0 = id
rotateL n | n < 0 = error "Negative index"
          | otherwise= uncurry (flip (++)) . splitAt n
  

注意:根据您的尝试,我认为您实际上是想将列表旋转到左侧,而不是将其移动,因为这意味着您只需{ {1}}前drop个元素,并用一些额外的值填充它。

  

注意:如果n大于列表的长度,则n将用作身份函数。那可能不是理想的行为。我将其保留为解决这种情况的练习。

答案 1 :(得分:2)

不是定义类型的名称,而是函数调用或类型声明中的位置。如果我们检查:的类型,则会看到第二个参数是一个列表:

Prelude> :t (:)
(:) :: a -> [a] -> [a]

因此,此构造函数上的模式匹配将始终为您提供headtail,而不是initlast。因此,同时使用x:xsxs:x,您已经宣布a[a]是相同的类型,并且编译器不愿意这样做。您可以使用例如执行附加xs ++ [x]

Data.Sequence的类型确实支持运算符:<||>:中的首尾模式。

通常,(singly linked)列表仅使head元素有效访问;诸如追加甚至检查长度之类的操作都很昂贵。当您只需要一个流或堆栈时,它们很棒,但是使用ropecircular buffer类型可以更好地处理长距离的重新排序操作,例如旋转。在Haskell中都可以使用,例如container's Seqring-buffer's RingBufferData.ByteString.LazyData.Text.Lazy也是绳索类型。