rebanadas de Python también apoyan paso:
>>> range(10)[::2]
[0, 2, 4, 6, 8]
>>> range(10)[2:8:2]
[2, 4, 6]
Así inspirado por Dan Burton dropping every Nth element he implementado una rebanada con el paso. ¡Funciona en listas infinitas!
takeStep :: Int -> [a] -> [a]
takeStep _ [] = []
takeStep n (x:xs) = x : takeStep n (drop (n-1) xs)
slice :: Int -> Int -> Int -> [a] -> [a]
slice start stop step = takeStep step . take (stop - start) . drop start
Sin embargo, también es compatible con Python inicio negativo y se detiene (se cuenta desde el final de la lista) y la etapa negativa (se invierte la lista, se convierte en parada iniciar y viceversa, y los pasos a través de la lista).
from pprint import pprint # enter all of this into Python interpreter
pprint([range(10)[ 2: 6], # [2, 3, 4, 5]
range(10)[ 6: 2:-1], # [6, 5, 4, 3]
range(10)[ 6: 2:-2], # [6, 4]
range(10)[-8: 6], # [2, 3, 4, 5]
range(10)[ 2:-4], # [2, 3, 4, 5]
range(10)[-8:-4], # [2, 3, 4, 5]
range(10)[ 6:-8:-1], # [6, 5, 4, 3]
range(10)[-4: 2:-1], # [6, 5, 4, 3]
range(10)[-4:-8:-1]]) # [6, 5, 4, 3]]
¿Cómo implemento eso en Haskell? Necesito revertir la lista si el paso es negativo, empiezo a contar desde el final de la lista si estos son negativos, y tenga en cuenta que la lista resultante debe contener elementos con índices start < = k < stop (con paso positivo) o start> = k> stop (con paso negativo).
takeStep :: Int -> [a] -> [a]
takeStep _ [] = []
takeStep n (x:xs)
| n >= 0 = x : takeStep n (drop (n-1) xs)
| otherwise = takeStep (-n) (reverse xs)
slice :: Int -> Int -> Int -> [a] -> [a]
slice a e d xs = z . y . x $ xs -- a:start, e:stop, d:step
where a' = if a >= 0 then a else (length xs + a)
e' = if e >= 0 then e else (length xs + e)
x = if d >= 0 then drop a' else drop e'
y = if d >= 0 then take (e'-a') else take (a'-e'+1)
z = takeStep d
test :: IO() -- slice works exactly in both languages
test = forM_ t (putStrLn . show)
where xs = [0..9]
t = [slice 2 6 1 xs, -- [2, 3, 4, 5]
slice 6 2 (-1) xs, -- [6, 5, 4, 3]
slice 6 2 (-2) xs, -- [6, 4]
slice (-8) 6 1 xs, -- [2, 3, 4, 5]
slice 2 (-4) 1 xs, -- [2, 3, 4, 5]
slice (-8)(-4) 1 xs, -- [2, 3, 4, 5]
slice 6 (-8)(-1) xs, -- [6, 5, 4, 3]
slice (-4) 2 (-1) xs, -- [6, 5, 4, 3]
slice (-4)(-8)(-1) xs] -- [6, 5, 4, 3]
El algoritmo sigue funcionando con infinitas listas dadas argumentos positivos, pero con el paso negativo que devuelve una lista vacía (en teoría, todavía podría devolver una lista secundaria invertida) y con arranque negativo o detener entra en un bucle infinito. Así que ten cuidado con los argumentos negativos.
Si 'slice 1 2 ['a', 'b', 'c', 'd']' es muy prolijo para usted, también puede agregar su propio azúcar 'xs! @ (From, to) = slice ft xs', para que pueda hacer '['a', 'b', 'c', 'd']! @ (1,2)' – rampion
@rampion: Aquí hay otro abuso de diversión de operadores de infijo: '(!>) = drop', '( ['A' .. 'z']
Me gusta '' map ("abcd" !!) [1 .. 2] '' (para obtener "bc"), aunque es terriblemente ineficiente (cuadrático). ("Me gusta" en el sentido "lindo", debido a la cuadrática, nunca lo usaría en la práctica). – jon