Как определить функцию вращения
Как определить функцию rotates, которая генерирует все вращения данного списка?
Например: вращает [1,2,3,4] =[[1,2,3,4],[2,3,4,1],[3,4,1,2],[4,1,2,3]]
Я написал функцию сдвига, которая может изменить порядок
shift ::[Int]->[Int]
shift x=tail ++ take 1 x
но я не могу сгенерировать эти новые массивы и добавить их вместе.
Ответы
Ответ 1
Следующие
shift :: [a] -> Int -> [a]
shift l n = drop n l ++ take n l
allRotations :: [a] -> [[a]]
allRotations l = [ shift l i | i <- [0 .. (length l) -1]]
дает
> ghci
Prelude> :l test.hs
[1 of 1] Compiling Main ( test.hs, interpreted )
Ok, modules loaded: Main.
*Main> allRotations [1,2,3,4]
[[1,2,3,4],[2,3,4,1],[3,4,1,2],[4,1,2,3]]
который, как вы ожидаете.
Я думаю, что это достаточно читаемо, хотя и не особенно эффективно (никаких воспоминаний о предыдущих сдвигах не происходит).
Если вы заботитесь об эффективности, то
shift :: [a] -> [a]
shift [] = []
shift (x:xs) = xs ++ [x]
allRotations :: [a] -> [[a]]
allRotations l = take (length l) (iterate shift l)
позволит вам повторно использовать результаты предыдущих смен и избежать их пересчета.
Обратите внимание, что iterate
возвращает бесконечный список, и из-за ленивой оценки мы всегда оцениваем его до length l
в списке.
Обратите внимание, что в первой части я расширил вашу функцию сдвига, чтобы спросить, сколько сдвигается, и затем я понимаю список для allRotations
.
Ответ 2
Другим способом расчета всех поворотов списка является использование предопределенных функций tails
и inits
. Функция tails
дает список всех конечных сегментов списка, а inits
выводит список всех начальных сегментов. Например,
tails [1,2,3] = [[1,2,3], [2,3], [3], []]
inits [1,2,3] = [[], [1], [1,2], [1,2,3]]
То есть, если мы соединим эти списки поточечно, как указано в отступе, мы получим все вращения. Мы получаем только исходный список дважды, а именно: один раз добавляем пустой начальный сегмент в конце исходного списка и один раз, добавляя пустой конечный сегмент в начало исходного списка. Поэтому мы используем функцию init
, чтобы удалить последний элемент результата применения zipWith
к хвостам и именам списка. Функция zipWith
применяет свой первый аргумент точечно к предоставленным спискам.
allRotations :: [a] -> [[a]]
allRotations l = init (zipWith (++) (tails l) (inits l))
Это решение имеет преимущество перед другими решениями, поскольку оно не использует length
. Функция length
является довольно строгой в том смысле, что она не дает результата до того, как она полностью оценит структуру списка его аргумента. Например, если мы оцениваем приложение
allRotations [1..]
то есть мы вычисляем все вращения бесконечного списка натуральных чисел, ghci с радостью начинает печатать бесконечный список в качестве первого результата. Напротив, реализация, основанная на length
, как предлагается здесь, не заканчивается, поскольку она вычисляет длину бесконечного списка.
Ответ 3
shift (x:xs) = xs ++ [x]
rotates xs = take (length xs) $ iterate shift xs
iterate f x
возвращает поток ( "бесконечный список" ) [x, f x, f (f x), ...]
. Есть n
вращения списка n
-элементов, поэтому мы take
первый n
из них.
Ответ 4
Ответы, полученные до сих пор, отлично подходят для конечных списков, но в конечном итоге будут выходить из строя, когда будут предоставлены бесконечные списки. (Все они называют length
в списке.)
shift :: [a] -> [a]
shift xs = drop 1 xs ++ take 1 xs
rotations :: [a] -> [[a]]
rotations xs = zipWith const (iterate shift xs) xs
В моем решении вместо этого используется zipWith const
. zipWith const foos bars
может показаться на первый взгляд идентичным foos
(напомним, что const x y = x
). Но список, возвращаемый из zipWith
, завершается, когда заканчивается любой из входных списков.
Поэтому, когда xs
является конечным, возвращаемый список имеет ту же длину, что и xs
, как мы хотим; и когда xs
бесконечно, возвращаемый список не будет усечен, поэтому будет бесконечным, снова, как мы хотим.
(В вашем конкретном приложении может не иметь смысла пытаться повернуть бесконечный список. С другой стороны, он может. Я отправлю этот ответ только для полноты.)
Ответ 5
Я бы предпочел следующие решения, используя встроенные функции cycle
и tails
:
rotations xs = take len $ map (take len) $ tails $ cycle xs where
len = length xs
Для вашего примера [1,2,3,4]
функция cycle
создает бесконечный список [1,2,3,4,1,2,3,4,1,2...]
. Функция tails
генерирует все возможные хвосты из данного списка, здесь [[1,2,3,4,1,2...],[2,3,4,1,2,3...],[3,4,1,2,3,4...],...]
. Теперь все, что нам нужно сделать, - это сократить "хвосты" -листов до длины 4 и сократить общий список до длины 4, который выполняется с помощью take
. Был введен псевдоним len
, чтобы избежать повторного пересчета length xs
несколько раз.
Ответ 6
Я думаю, что это будет что-то вроде этого (у меня сейчас нет ghc, поэтому я не мог попробовать)
shift (x:xs) = xs ++ [x]
rotateHelper xs 0 = []
rotateHelper xs n = xs : (rotateHelper (shift xs) (n - 1))
rotate xs = rotateHelper xs (length xs)
Ответ 7
myRotate lst = lst : myRotateiter lst lst
where myRotateiter (x:xs) orig
|temp == orig = []
|otherwise = temp : myRotateiter temp orig
where temp = xs ++ [x]
Ответ 8
Я предлагаю:
rotate l = l : rotate (drop 1 l ++ take 1 l)
distinctRotations l = take (length l) (rotate l)