生成一个由右移元素生成的列表n次

时间:2012-10-24 11:47:16

标签: haskell

我最近在尝试一个问题。在这种情况下,我遇到的问题很少。

Input: generatingListforRightShifting 2 [1,2,3,4,5,6,7,8]
Output: [[1,2,3,4,5,6,7,8],[8,1,2,3,4,5,6,7],[7,8,1,2,3,4,5,6]]

如您所知,此程序将向正确的方向移动元素。第一个参数表示它将进行多少次转移。 作为一个新手,我正在尝试解决几个众所周知的列表功能。并使用递归。但对我来说,递归的想法并不清楚。我的代码是:

generatingListforRightShifting' _ []=[]
generatingListforRightShifting' 0 x=x
generatingListforRightShifting' n xs=   newList where
                        newList=takeWhile(\i->[1..n] 
                                            <=n)(reverse(take 
                                           i(reverse xs))++reverse(drop i(reverse xs)))

据我所知,我正在做的主要错误是在部分takeWhile。但我怎么能迭代n次。我已经制作了一个直接显示移位结果的程序,如     输入:generatingListforRightShifting 2 [1,2,3,4,5,6,7,8]     产出:[7,8,1,2,3,4,5,6] 但是,当我试图获得所有先前的转变时,我不能。

任何人都可以帮助我。如果你给我解决的想法,我也欢迎你。

6 个答案:

答案 0 :(得分:3)

这通常被称为旋转而不是移位。右侧旋转列表很简单,因为有方法可以获取last elementsublist of all elements but the last

rotateOnce lst = (last lst):(init lst)

另请注意,旋转两次相当于调用rotateOnce两次。因此,该方法可以简单地实现为来自先前结果的递归:

rotateN 0 lst = [lst]
rotateN n lst = lst : rotateN (n-1) ((last lst):(init lst))

(注意:这可能不是最佳解决方案。)

答案 1 :(得分:2)

您可以递归定义“shift”:shift 0是无操作,shift 1+n (x:xs)shift n xs

类似的东西:

shift 0 = \x -> x
shift n = \lst@(x:xs) -> (shift (n-1) xs)

-- example:
sh3 = shift 3        

然后'旋转'问题变得更容易:

rotate n = \lst -> (shift lst) ++ (take n lst)

答案 2 :(得分:2)

您似乎更喜欢我们修复您的代码而不是重新开始,所以 让我们来看看你的代码。首先,主要列表斩:

reverse (take i (reverse xs)) ++ reverse (drop i (reverse xs)) 

现在reverse (take i (reverse xs))从列表末尾获取i个元素, 但是为了达到这个目的,你将列表反转两次,这样做会更好 drop (length xs - i) xs。同样,您可以实施reverse (drop i (reverse xs)))take (length xs - i) xs。这给了我们

drop (length xs - i) xs ++ take (length xs - i) xs 

现在您的代码\i->[1..n]<=n没有意义,因为它会比较列表[1..n]n,这是行不通的。我想你正在努力建立一个i运行的循环 1n,这是一个很好的计划。让我们使用列表理解来获得我们想要的那些:

[drop (length xs - i) xs ++ take (length xs - i) xs | i <- [1 .. length xs], i <= n]

但是现在我们从1开始运行到列表的长度但丢弃了n以上的数字, 哪个会写得更好

[drop (length xs - i) xs ++ take (length xs - i) xs | i <- [1..n]]

这允许n超过length xs,但我没有看到一个大问题,我们可以先检查一下。

现在请注意,我们仅使用i形式的(length xs - i),我们真的在重新计算 length xs比我们应该做的多得多,所以不要让i1运行到n,而是使用 length xs - i,我们为什么不j=length xs -i j length xslength xs - n[drop j xs ++ take j xs | j <- [length xs,length xs - 1 .. length xs - n]]

[6,5..1] == [6,5,4,3,2,1]

因为例如let l = length xs in [drop j xs ++ take j xs | j <- [l,l - 1 .. l - n]]

而起作用

这样做比较简洁

take

或者你可能比let l = length xs in take n [drop j xs ++ take j xs | j <- [l,l - 1 .. 0]] 更喜欢做算术,所以我们可以使用:

generatingListforRightShifting

它有额外的好处,可以阻止你做太多,停止 当你回到起点时。

我会将您的功能从rotationsR重命名为rotationsR n xs = let l = length xs in take n [drop j xs ++ take j xs | j <- [l,l - 1 ..]] ,并提供

rotationsR 6 [1..4] == [[1,2,3,4],[4,1,2,3],[3,4,1,2],[2,3,4,1],[1,2,3,4]]

提供rotationsL n xs = take n [drop j xs ++ take j xs | j <- [0..length xs]]

左旋转看起来更简单:

xs

题外话:我无法自拔,抱歉,我又开始了。

我仍然不喜欢所有那些每次都掉下来的东西,我宁愿自大 无限多个cycle xs的副本彼此相邻(tails)并无限采取 许多cycle xs,将它们全部砍成正确的长度,但只是给你第一个n:

rotationsL'n xs = let l = length xs in       拿n。地图(拿l)。尾巴。循环$ xs

由于懒惰评估,只计算了有限数量的rotationsL' 10 [1..4], 但是这个可以运行并运行: [[1,2,3,4],[2,3,4,1],[3,4,1,2],[4,1,2,3],[1,2,3,4],[2,3,4,1],[3,4,1,2],[4,1,2,3],[1,2,3,4],[2,3,4,1]] 给你:

rotationsR' n xs = let l = length xs in
  take n . map (reverse.take l) . tails . cycle . reverse $ xs

以这种方式做正确的旋转也会很好,但它不起作用,因为 我需要从无限列表的末尾开始,然后继续前进。让我们重用一下 反过来,拿走你需要的东西,然后再次反转,但是:

generatingListforRightShifting n xs = 
    [reverse (take i (reverse xs)) ++ reverse (drop i (reverse xs)) | i <- [1..n]]

Undigression:如果你想更贴近原始代码,你可以做到

{{1}}

答案 3 :(得分:1)

我会放弃当前的方法,这是非常复杂的。相反,专注于抽象操作的不同组件。如果将操作分解为多个部分,您会注意到有两个对称组件:向左旋转列表,向右旋转列表。您希望定义的操作在某个列表上迭代右旋转指定的次数。这表明可以通过 右旋转指定指定数量的迭代来定义所需操作>。例如,

left :: [a] -> [a]
left [] = []
left xs = tail xs ++ [head xs]

right :: [a] -> [a]
right [] = []
right xs = last xs : init xs

shiftL :: Int -> [a] -> [[a]]
shiftL n = take n . iterate left

shiftR :: Int -> [a] -> [[a]]
shiftR n = take n . iterate right

答案 4 :(得分:1)

在这里使用cycle似乎不错:

shifts n xs = take (n+1) $ shifts' (cycle xs)
  where
    len = length xs
    shifts' ys = take len ys:shifts' (drop (len-1) ys)

答案 5 :(得分:1)

我发现左旋转使用splitAt非常直接:

import Data.Tuple (swap)
rotateLeft n = uncurry (++) . swap . splitAt n

> rotateLeft 2 "Hello World!"
>>> "llo World!He"