我想将列表向左移动n个元素。如果要将顺序从(x:xs)
更改为(xs:x)
,为什么会出错?
shift n list@(x:xs)
| n == 0 = list
| otherwise = shift (n-1) (xs:x) -- (xs:x) error
进行检查:无法构造无限类型:a〜[a]
我不知道如何解释此错误。也许有人可以帮助我。 非常感谢。
编辑:如前所述,正确使用的术语是旋转而不是移位
答案 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]
因此,此构造函数上的模式匹配将始终为您提供head
和tail
,而不是init
和last
。因此,同时使用x:xs
和xs:x
,您已经宣布a
和[a]
是相同的类型,并且编译器不愿意这样做。您可以使用例如执行附加xs ++ [x]
。
Data.Sequence的类型确实支持运算符:<|
和|>:
中的首尾模式。
通常,(singly linked)列表仅使head元素有效访问;诸如追加甚至检查长度之类的操作都很昂贵。当您只需要一个流或堆栈时,它们很棒,但是使用rope或circular buffer类型可以更好地处理长距离的重新排序操作,例如旋转。在Haskell中都可以使用,例如container's Seq或ring-buffer's RingBuffer。 Data.ByteString.Lazy和Data.Text.Lazy也是绳索类型。