Подтвердить что ты не робот

Имеет ли Haskell список фрагментов (например, Python)?

Есть ли у Haskell аналогичный синтаксический сахар для Python List Slices?

Например, в Python:

x = ['a','b','c','d']
x[1:3] 

дает символы от индекса 1 к индексу 2, включенному (или к индексу 3 исключен):

['b','c']

Я знаю, что у Haskell есть функция (!!) для конкретных индексов, но есть ли эквивалентная функция "нарезки" или списка?

4b9b3361

Ответ 1

Нет встроенной функции для нарезки списка, но вы можете легко написать его самостоятельно, используя drop и take:

slice from to xs = take (to - from + 1) (drop from xs)

Следует отметить, что поскольку списки Haskell являются односвязными списками (в то время как списки python являются массивами), создание подобных подписок будет O(to), а не O(1), как в python (предполагая, конечно, что весь список фактически оценивается - в противном случае ловкость Haskell вступает в силу).

Ответ 2

Если вы пытаетесь сопоставить "списки" Python (который не является списком, как отмечают другие), вы можете использовать Haskell vector, который имеет встроенный slice. Кроме того, Vector может быть оцениваться параллельно, что, я думаю, действительно круто.

Ответ 3

Нет синтаксического сахара. В тех случаях, когда это необходимо, вы можете просто take и drop.

take 2 $ drop 1 $ "abcd" -- gives "bc"

Ответ 4

Я не думаю, что он включен, но вы можете написать достаточно просто:

slice start end = take (end - start + 1) . drop start

Конечно, с предварительным условием, что start и end являются границами и end >= start.

Ответ 5

Сеты Python также поддерживают шаг:

>>> range(10)[::2]
[0, 2, 4, 6, 8]
>>> range(10)[2:8:2]
[2, 4, 6]

Итак, вдохновленный Дэном Бертоном отбрасывая каждый N-й элемент, я реализовал срез с шагом. Он работает с бесконечными списками!

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

Однако Python также поддерживает отрицательный запуск и остановку (он отсчитывается от конца списка) и отрицательный шаг (он меняет список, останавливается, становится стартом и наоборот, и выполняет шаги по списку).

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]]

Как реализовать это в Haskell? Мне нужно отменить список, если шаг отрицательный, начните считать начало и конец с конца списка, если они отрицательные, и имейте в виду, что результирующий список должен содержать элементы с индексами start <= k < stop (с положительным шагом) или start >= k > stop (с отрицательным шагом).

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]

Алгоритм по-прежнему работает с бесконечными списками с положительными аргументами, но с отрицательным шагом он возвращает пустой список (теоретически он все еще может возвращать реверсивный подсписок) и с отрицательным запуском или прекращением его входа в бесконечный цикл. Поэтому будьте осторожны с отрицательными аргументами.

Ответ 6

Другой способ сделать это с помощью функции splitAt из Data.List - я нахожу, что ее немного легче читать и понимать, чем использовать take и drop - но это только личное предпочтение:

import Data.List
slice :: Int -> Int -> [a] -> [a]
slice start stop xs = fst $ splitAt (stop - start) (snd $ splitAt start xs)

Например:

Prelude Data.List> slice 0 2 [1, 2, 3, 4, 5, 6]
[1,2]
Prelude Data.List> slice 0 0 [1, 2, 3, 4, 5, 6]
[]
Prelude Data.List> slice 5 2 [1, 2, 3, 4, 5, 6]
[]
Prelude Data.List> slice 1 4 [1, 2, 3, 4, 5, 6]
[2,3,4]
Prelude Data.List> slice 5 7 [1, 2, 3, 4, 5, 6]
[6]
Prelude Data.List> slice 6 10 [1, 2, 3, 4, 5, 6]
[]

Это должно быть эквивалентно

let slice' start stop xs = take (stop - start) $ drop start xs

который, безусловно, будет более эффективным, но который я считаю немного более запутанным, чем думать об индексах, где список разбивается на переднюю и заднюю половинки.

Ответ 7

У меня была аналогичная проблема и я использовал понимание списка:

-- Where lst is an arbitrary list and indc is a list of indices

[lst!!x|x<-[1..]] -- all of lst
[lst!!x|x<-[1,3..]] -- odd-indexed elements of lst
[lst!!x|x<-indc]

Возможно, не такой аккуратный, как кусочки питона, но он выполняет эту работу. Обратите внимание, что indc может быть в любом порядке, и не обязательно быть смежным.

Как отмечалось, использование списков LINKED в Haskell делает эту функцию O (n), где n - это максимальный доступ к индексу, а не python slicing, который зависит от количества доступных значений.

Отказ от ответственности: я все еще новичок в Haskell, и я приветствую любые исправления.

Ответ 8

Очевидно, что моя версия foldl проиграет против подхода с захватом, но, возможно, кто-то видит способ улучшить его?

slice from to = reverse.snd.foldl build ((from, to + 1), []) where
   build [email protected]((_, 0), _) _ = res  
   build ((0, to), xs) x = ((0, to - 1), x:xs)  
   build ((from, to), xs) _ = ((from - 1, to - 1), xs)

Ответ 9

sublist start length = take length . snd . splitAt start

slice start end = snd .splitAt start . take end