Haskell中的旋转功能

时间:2018-09-26 22:39:24

标签: haskell recursion pattern-matching

我想在Haskell中编写一个函数,该函数将由第二个参数给出的列表旋转第一个参数指示的位置数。使用模式匹配,实现递归函数

我编写了以下函数:

rotate :: Int -> [a] -> [a]
rotate 0 [y]= [y]
rotate x [y]= rotate((x-1) [tail [y] ++ head [y]])

,但是此函数始终会产生错误。有什么办法解决吗? 该函数在运行时应执行以下操作:

rotate 1 "abcdef"
"bcdefa"

3 个答案:

答案 0 :(得分:7)

[y]并不意味着“让y为列表”。这意味着“此参数是一个包含一个名为y的元素的列表”。您的结构正确,但不需要y周围的括号。

rotate :: Int -> [a] -> [a]
rotate 0 y = y
rotate x y = rotate (x-1) (tail y ++ [head y])

答案 1 :(得分:1)

我认为您想要这样的东西:

rotate :: Int -> [a] -> [a]
rotate 0 x = x
rotate times (x:xs) = rotate (times - 1) (xs ++ [x])

答案 2 :(得分:1)

TL&DR

rotate :: Int -> [a] -> [a]
rotate = drop <> take

在Haskell中,最简洁但又奇怪的方式是旋转列表的方法可能是使用函数类型Semigroup的{​​{1}}类型类实例。让我们检查实例的相关部分。

(a -> b)

首先,instance Semigroup b => Semigroup (a -> b) where f <> g = \x -> f x <> g x 实际上是<>类型类中mappend函数的内联版本。

然后我们看到,类型签名中的Monoid约束指出返回类型Semigroup b =>也应该是b类型类的成员。由于我们使用Semigroupdrop :: Int -> [a] -> [a],我们可以很快注意到take :: Int -> [a] -> [a]实际上是具有类型签名b的另一个函数,因此它是[a] -> [a]类型类的成员当且仅当它是Semigroup且恰好是b也是[a]类型类的成员,并且也是。

Semigroup

到目前为止,所有内容都可以保存,但是它如何工作?

我们可以从类型签名中推断出以下内容;

  • instance Semigroup [a] where (<>) = (++)
  • (drop :: Int -> ([a] -> [a])) <> (take :: Int -> ([a] -> [a]))
  • \n -> (drop n :: [a] -> [a]) <> (take n :: [a] -> [a])
  • \n -> \xs -> (drop n xs :: [a]) <> (take n xs :: [a])

这基本上比使用递归\n -> \xs -> (drop n xs) ++ (take n xs)运算符将头部作为单例列表添加到尾部末尾的答案要好,因为它会产生O(n ^ 2)的时间复杂度。