Respostas:
Olha aqui , o operador usado é !!.
Ou seja, [1,2,3]!!1dá a você 2, já que as listas são indexadas a 0.
itemOf :: Int -> [a] -> Maybe a; x `itemOf` xs = let xslen = length xs in if ((abs x) > xslen) then Nothing else Just (xs !! (x `mod` xslen)). Observe que isso falhará catastroficamente em uma lista infinita.
!!é uma função parcial e, portanto, insegura. Dê uma olhada no comentário abaixo e use lens stackoverflow.com/a/23627631/2574719
Não estou dizendo que haja algo errado com sua pergunta ou com a resposta dada, mas talvez você gostaria de saber sobre a ferramenta maravilhosa que é o Hoogle para economizar tempo no futuro: Com o Hoogle, você pode pesquisar funções de biblioteca padrão que correspondem a uma determinada assinatura. Portanto, não sabendo nada sobre !!, no seu caso, você pode pesquisar "algo que pega uma Inte uma lista de qualquer coisa e retorna um único tal qualquer", a saber
Int -> [a] -> a
Lo e eis que , com !!como o primeiro resultado (embora a assinatura de tipo na verdade tem dois argumentos na ordem inversa em relação ao que buscamos). Legal, hein?
Além disso, se o seu código depende da indexação (em vez de consumir do início da lista), as listas podem na verdade não ser a estrutura de dados adequada. Para acesso baseado em índice O (1), existem alternativas mais eficientes, como matrizes ou vetores .
Uma alternativa ao uso (!!)é usar o
pacote de lentes e sua elementfunção e operadores associados. A
lente fornece uma interface uniforme para acessar uma ampla variedade de estruturas e estruturas aninhadas acima e além das listas. Abaixo, vou me concentrar em fornecer exemplos e ignorar tanto as assinaturas de tipo quanto a teoria por trás do
pacote de lentes . Se você quiser saber mais sobre a teoria, um bom lugar para começar é o arquivo leia-me no repositório github .
Na linha de comando:
$ 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
Para acessar uma lista com o operador infixo
> [1,2,3,4,5] ^? element 2 -- 0 based indexing
Just 3
Ao contrário de, (!!)isso não lançará uma exceção ao acessar um elemento fora dos limites e, em Nothingvez disso, retornará . Geralmente, é recomendável evitar funções parciais como (!!)ou, headpois elas têm mais casos extremos e são mais propensas a causar um erro de tempo de execução. Você pode ler um pouco mais sobre por que evitar funções parciais nesta página wiki .
> [1,2,3] !! 9
*** Exception: Prelude.(!!): index too large
> [1,2,3] ^? element 9
Nothing
Você pode forçar a técnica da lente a ser uma função parcial e lançar uma exceção quando estiver fora dos limites usando o (^?!)operador em vez do (^?)operador.
> [1,2,3] ^?! element 1
2
> [1,2,3] ^?! element 9
*** Exception: (^?!): empty Fold
No entanto, isso não se limita apenas a listas. Por exemplo, a mesma técnica funciona em árvores do pacote de contêineres padrão .
> 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
Agora podemos acessar os elementos da árvore em ordem de profundidade:
> 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
Também podemos acessar sequências do pacote de contêineres :
> import qualified Data.Sequence as Seq
> Seq.fromList [1,2,3,4] ^? element 3
Just 4
Podemos acessar os arrays indexados int padrão do pacote vetorial , texto do pacote de texto padrão , cadeias de bytes do pacote de cadeias de bytes padrão e muitas outras estruturas de dados padrão. Este método padrão de acesso pode ser estendido às suas estruturas de dados pessoais, tornando-as uma instância da typeclass Taversable ; consulte uma lista mais longa de exemplos de Traversables na documentação do Lens. .
Cavar em estruturas aninhadas é simples com o hackage da lente . Por exemplo, acessando um elemento em uma lista de listas:
> [[1,2,3],[4,5,6]] ^? element 0 . element 1
Just 2
> [[1,2,3],[4,5,6]] ^? element 1 . element 2
Just 6
Essa composição funciona mesmo quando as estruturas de dados aninhadas são de tipos diferentes. Por exemplo, se eu tivesse uma lista de árvores:
> :{
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
Você pode aninhar profundamente com tipos arbitrários, desde que atendam ao Traversablerequisito. Portanto, acessar uma lista de árvores de sequências de texto é fácil.
Uma operação comum em muitos idiomas é atribuir a uma posição indexada em uma matriz. Em python, você pode:
>>> a = [1,2,3,4,5]
>>> a[3] = 9
>>> a
[1, 2, 3, 9, 5]
O
pacote de lentes oferece essa funcionalidade com o (.~)operador. Embora ao contrário do python, a lista original não sofra mutação, mas uma nova lista é retornada.
> 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é apenas uma função e o (&)operador, parte do
pacote de lentes , é apenas a aplicação da função reversa. Aqui está a aplicação de função mais comum.
> (element 3 .~ 9) [1,2,3,4,5]
[1,2,3,9,5]
A atribuição novamente funciona perfeitamente bem com aninhamento arbitrário de Traversables.
> [[1,2,3],[4,5,6]] & element 0 . element 1 .~ 9
[[1,9,3],[4,5,6]]
Data.Traversablevez de reexportar em lens?
A resposta direta já foi dada: Use !!.
No entanto, os novatos geralmente tendem a usar esse operador em excesso, o que é caro em Haskell (porque você trabalha com listas de links simples, não com matrizes). Existem várias técnicas úteis para evitar isso, a mais fácil é usar o zip. Se você escrever zip ["foo","bar","baz"] [0..], obterá uma nova lista com os índices "anexados" a cada elemento em um par:, [("foo",0),("bar",1),("baz",2)]que geralmente é exatamente o que você precisa.
O tipo de dados de lista padrão de Haskell forall t. [t]na implementação se assemelha muito a uma lista vinculada C canônica e compartilha suas propriedades essenciais. Listas vinculadas são muito diferentes de arrays. Mais notavelmente, o acesso por índice é um O (n) linear-, em vez de uma O (1) operação de tempo constante.
Se você precisar de acesso aleatório frequente, considere o Data.Arraypadrão.
!!é uma função parcialmente definida insegura, provocando um travamento para índices fora do intervalo. Esteja ciente de que a biblioteca padrão contém algumas dessas funções parciais ( head, last, etc.). Por segurança, use um tipo de opção Maybeou o Safemódulo.
Exemplo de uma função de indexação total robusta e razoavelmente eficiente (para índices ≥ 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
Trabalhando com listas vinculadas, geralmente os ordinais são convenientes:
nth :: Int -> [a] -> Maybe a
nth _ [] = Nothing
nth 1 (x : _) = Just x
nth n (_ : xs) = nth (n - 1) xs
[1,2,3]!!6apresentará um erro de tempo de execução. Isso poderia ser facilmente evitado se!!tivesse o tipo[a] -> Int -> Maybe a. A própria razão de termos Haskell é evitar esses erros de tempo de execução!