it-swarm.com.ru

Как я могу получить n-й элемент из списка?

Как я могу получить доступ к списку по индексу в Haskell, аналог этого кода C?

int a[] = { 34, 45, 56 };
return a[1];
84
Eonil

Смотрите здесь , оператор !!.

То есть [1,2,3]!!1 дает вам 2, так как списки индексируются 0.

136
phimuemue

Я не говорю, что с вашим вопросом или ответом что-то не так, но, возможно, вы хотели бы узнать о замечательном инструменте, который Hoogle , чтобы сэкономить время в будущем: с помощью Google, Вы можете искать стандартные библиотечные функции, которые соответствуют данной подписи. Так что, ничего не зная о !!, в вашем случае вы можете искать "что-то, что принимает Int и список whatevers и возвращает одно такое", а именно

Int -> [a] -> a

Lo и смотрите , с !! в качестве первого результата (хотя сигнатура типа фактически имеет два аргумента в обратном порядке по сравнению с тем, что мы искали). Аккуратно, а?

Кроме того, если ваш код основан на индексации (вместо того, чтобы использовать его в начале списка), списки могут фактически не соответствовать структуре данных. Для O(1) доступа на основе индекса существуют более эффективные альтернативы, такие как массивы или векторы .

82
gspr

Альтернативой использованию (!!) является использование пакета lens , его функции element и связанных с ней операторов. линза обеспечивает единый интерфейс для доступа к широкому спектру структур и вложенных структур над списками и за их пределами. Ниже я сконцентрируюсь на предоставлении примеров и замаскирую как сигнатуры типов, так и теорию, лежащую в основе пакета lens . Если вы хотите узнать больше о теории, лучше всего начать с файла readme в github repo .

Доступ к спискам и другим типам данных

Получение доступа к объективу

В командной строке:

$ cabal install lens
$ ghci
GHCi, version 7.6.3: http://www.haskell.org/ghc/  :? for help
Loading package ghc-prim ... linking ... done.
Loading package integer-gmp ... linking ... done.
Loading package base ... linking ... done.
> import Control.Lens


Доступ к спискам

Чтобы получить доступ к списку с оператором инфикса

> [1,2,3,4,5] ^? element 2  -- 0 based indexing
Just 3

В отличие от (!!) это не сгенерирует исключение при доступе к элементу за пределами и вместо этого вернет Nothing. Часто рекомендуется избегать частичных функций, таких как (!!) или head, поскольку они имеют больше угловых случаев и с большей вероятностью вызывают ошибку во время выполнения. Вы можете прочитать немного больше о том, почему следует избегать частичных функций на эта страница вики .

> [1,2,3] !! 9
*** Exception: Prelude.(!!): index too large

> [1,2,3] ^? element 9
Nothing

Вы можете заставить метод линзы быть частичной функцией и генерировать исключение, выходя за пределы, используя оператор (^?!) вместо оператора (^?).

> [1,2,3] ^?! element 1
2
> [1,2,3] ^?! element 9
*** Exception: (^?!): empty Fold


Работа с типами, отличными от списков

Однако это не ограничивается списками. Например, та же самая техника работает на деревья из стандартного пакета контейнеры .

 > import Data.Tree
 > :{
 let
  tree = Node 1 [
       Node 2 [Node 4[], Node 5 []]
     , Node 3 [Node 6 [], Node 7 []]
     ]
 :}
> putStrLn . drawTree . fmap show $tree
1
|
+- 2
|  |
|  +- 4
|  |
|  `- 5
|
`- 3
   |
   +- 6
   |
   `- 7

Теперь мы можем получить доступ к элементам дерева в порядке глубины:

> tree ^? element 0
Just 1
> tree ^? element 1
Just 2
> tree ^? element 2
Just 4
> tree ^? element 3
Just 5
> tree ^? element 4
Just 3
> tree ^? element 5
Just 6
> tree ^? element 6
Just 7

Мы также можем получить доступ к последовательность из пакета контейнеры :

> import qualified Data.Sequence as Seq
> Seq.fromList [1,2,3,4] ^? element 3
Just 4

Мы можем получить доступ к стандартным индексированным массивам int из пакета vector , текст из стандартного пакета text , строки байтов для стандартного пакета bytestring и многие другие. другие стандартные структуры данных. Этот стандартный метод доступа может быть расширен на ваши структуры персональных данных, сделав их экземпляром класса типов Taversable , см. Более длинный список примеров Traversables в документации Lens. .


Вложенные структуры

Копаться во вложенных структурах просто с линзой взлом . Например, доступ к элементу в списке списков:

> [[1,2,3],[4,5,6]] ^? element 0 . element 1
Just 2
> [[1,2,3],[4,5,6]] ^? element 1 . element 2
Just 6

Эта композиция работает, даже когда вложенные структуры данных имеют разные типы. Например, если бы у меня был список деревьев:

> :{
 let
  tree = Node 1 [
       Node 2 []
     , Node 3 []
     ]
 :}
> putStrLn . drawTree . fmap show $ tree
1
|
+- 2
|
`- 3
> :{
 let 
  listOfTrees = [ tree
      , fmap (*2) tree -- All tree elements times 2
      , fmap (*3) tree -- All tree elements times 3
      ]            
 :}

> listOfTrees ^? element 1 . element 0
Just 2
> listOfTrees ^? element 1 . element 1
Just 4

Можно произвольно глубоко вкладывать произвольные типы, если они соответствуют требованию Traversable. Таким образом, доступ к списку деревьев последовательностей текста - это не проблема.


Изменение n-го элемента

Обычной операцией во многих языках является присвоение индексированной позиции в массиве. В python вы можете:

>>> a = [1,2,3,4,5]
>>> a[3] = 9
>>> a
[1, 2, 3, 9, 5]

Пакет lens предоставляет эту функциональность с помощью оператора (.~). Хотя в отличие от python исходный список не видоизменен, скорее возвращается новый список.

> let a = [1,2,3,4,5]
> a & element 3 .~ 9
[1,2,3,9,5]
> a
[1,2,3,4,5]

element 3 .~ 9 - это просто функция, а оператор (&), входящий в пакет lens , является просто приложением обратной функции. Вот оно с более распространенным приложением функций.

> (element 3 .~ 9) [1,2,3,4,5]
[1,2,3,9,5]

Назначение снова прекрасно работает с произвольным вложением Traversables.

> [[1,2,3],[4,5,6]] & element 0 . element 1 .~ 9
[[1,9,3],[4,5,6]]
57
Davorak

Прямой ответ уже был дан: используйте !!.

Однако новички часто имеют тенденцию злоупотреблять этим оператором, что дорого в Haskell (потому что вы работаете с одиночными связанными списками, а не с массивами). Есть несколько полезных методов, чтобы избежать этого, самый простой - использовать Zip. Если вы напишете Zip ["foo","bar","baz"] [0..], вы получите новый список с индексами, "привязанными" к каждому элементу в паре: [("foo",0),("bar",1),("baz",2)], что часто именно то, что вам нужно.

11
Landei

Стандартный тип данных списка Haskell forall t. [t] в реализации очень похож на канонический связанный список C и разделяет его по существу свойства. Связанные списки сильно отличаются от массивов. В частности, доступ по индексу - это O(n) linear-, а не O(1) операция с постоянным временем.

Если вам требуется частый произвольный доступ, рассмотрите стандарт Data.Array .

!! - небезопасная частично определенная функция, вызывающая сбой для индексов вне диапазона. Помните, что стандартная библиотека содержит некоторые такие частичные функции (head, last и т.д.). В целях безопасности используйте тип параметра Maybe или модуль Safe .

Пример достаточно эффективной и надежной общей (для индексов ≥ 0) функции индексации:

data Maybe a = Nothing | Just a

lookup :: Int -> [a] -> Maybe a
lookup _ []       = Nothing
lookup 0 (x : _)  = Just x
lookup i (_ : xs) = lookup (i - 1) xs

Работая со связанными списками, часто удобны порядковые номера:

nth :: Int -> [a] -> Maybe a
nth _ []       = Nothing
nth 1 (x : _)  = Just x
nth n (_ : xs) = nth (n - 1) xs
4
user6428287

Вы можете использовать !!, но если вы хотите сделать это рекурсивно, то ниже приведен один из способов сделать это:

dataAt :: Int -> [a] -> a
dataAt _ [] = error "Empty List!"
dataAt y (x:xs)  | y <= 0 = x
                 | otherwise = dataAt (y-1) xs
3
Abgo80