Respuestas:
Mire aquí , el operador utilizado es !!.
Es decir , [1,2,3]!!1te da 2, ya que las listas están indexadas en 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)). Tenga en cuenta que esto fallará catastróficamente en una lista infinita.
!!es una función parcial y por lo tanto insegura. Eche un vistazo al comentario a continuación y use lens stackoverflow.com/a/23627631/2574719
No estoy diciendo que haya nada malo con su pregunta o la respuesta dada, pero tal vez le gustaría saber acerca de la maravillosa herramienta que es Hoogle para ahorrar tiempo en el futuro: con Hoogle, puede buscar funciones de biblioteca estándar que coinciden con una firma determinada. Por lo tanto, sin saber nada sobre !!, en su caso, podría buscar "algo que tome una Inty una lista de lo que sea y devuelva un solo lo que sea", es decir
Int -> [a] -> a
He aquí , con !!como primer resultado (aunque la firma de tipo en realidad tiene los dos argumentos al revés en comparación con lo que buscamos). Limpio, ¿eh?
Además, si su código se basa en la indexación (en lugar de consumir desde el principio de la lista), es posible que las listas no tengan la estructura de datos adecuada. Para el acceso basado en índices O (1) hay alternativas más eficientes, como matrices o vectores .
Una alternativa al uso (!!)es usar el
paquete de lentes y su elementfunción y operadores asociados. La
lente proporciona una interfaz uniforme para acceder a una amplia variedad de estructuras y estructuras anidadas por encima y más allá de las listas. A continuación, me centraré en proporcionar ejemplos y pasaré por alto tanto las firmas de tipos como la teoría detrás del
paquete de lentes . Si desea saber más sobre la teoría, un buen lugar para comenzar es el archivo Léame en el repositorio de github .
En la línea 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 acceder a una lista con el operador infijo
> [1,2,3,4,5] ^? element 2 -- 0 based indexing
Just 3
A diferencia de (!!)esto, esto no generará una excepción al acceder a un elemento fuera de los límites y en su Nothinglugar regresará . A menudo se recomienda evitar funciones parciales como (!!)o, headya que tienen más casos de esquina y es más probable que causen un error de tiempo de ejecución. Puede leer un poco más sobre por qué evitar funciones parciales en esta página wiki .
> [1,2,3] !! 9
*** Exception: Prelude.(!!): index too large
> [1,2,3] ^? element 9
Nothing
Puede forzar la técnica de la lente para que sea una función parcial y lanzar una excepción cuando esté fuera de los límites utilizando el (^?!)operador en lugar del (^?)operador.
> [1,2,3] ^?! element 1
2
> [1,2,3] ^?! element 9
*** Exception: (^?!): empty Fold
Sin embargo, esto no se limita a las listas. Por ejemplo, la misma técnica funciona en árboles del paquete de contenedores estándar .
> 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
Ahora podemos acceder a los elementos del árbol en orden de profundidad:
> 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
También podemos acceder a secuencias desde el paquete de contenedores :
> import qualified Data.Sequence as Seq
> Seq.fromList [1,2,3,4] ^? element 3
Just 4
Podemos acceder a las matrices indexadas int estándar desde el paquete de vectores , el texto del paquete de texto estándar , las cadenas de bytes del paquete de cadenas de bytes estándar y muchas otras estructuras de datos estándar. Este método estándar de acceso puede extenderse a sus estructuras de datos personales convirtiéndolas en una instancia de la clase de tipos Taversable , consulte una lista más larga de ejemplos de Traversables en la documentación de Lens. .
Profundizar en estructuras anidadas es simple con el truco de la lente . Por ejemplo, acceder a un elemento en una 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
Esta composición funciona incluso cuando las estructuras de datos anidadas son de diferentes tipos. Entonces, por ejemplo, si tuviera una lista de árboles:
> :{
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
Puede anidar arbitrariamente profundamente con tipos arbitrarios siempre que cumplan con el Traversablerequisito. Así que acceder a una lista de árboles de secuencias de texto es fácil.
Una operación común en muchos idiomas es asignar a una posición indexada en una matriz. En Python podrías:
>>> a = [1,2,3,4,5]
>>> a[3] = 9
>>> a
[1, 2, 3, 9, 5]
El
paquete de lentes brinda esta funcionalidad con el (.~)operador. Aunque a diferencia de Python, la lista original no está mutada, sino que se devuelve una nueva lista.
> let a = [1,2,3,4,5]
> a & element 3 .~ 9
[1,2,3,9,5]
> a
[1,2,3,4,5]
element 3 .~ 9es solo una función y el (&)operador, parte del
paquete de lentes , es solo una aplicación de función inversa. Aquí está con la aplicación de función más común.
> (element 3 .~ 9) [1,2,3,4,5]
[1,2,3,9,5]
La asignación nuevamente funciona perfectamente bien con anidamientos arbitrarios de Traversables.
> [[1,2,3],[4,5,6]] & element 0 . element 1 .~ 9
[[1,9,3],[4,5,6]]
Data.Traversablelugar de la reexportación en lens?
Ya se dio la respuesta directa: Use !!.
Sin embargo, los novatos a menudo tienden a abusar de este operador, que es caro en Haskell (porque trabaja en listas vinculadas individuales, no en matrices). Existen varias técnicas útiles para evitar esto, la más sencilla es usar zip. Si escribe zip ["foo","bar","baz"] [0..], obtiene una nueva lista con los índices "adjuntos" a cada elemento de un par:, [("foo",0),("bar",1),("baz",2)]que a menudo es exactamente lo que necesita.
Tipo de datos de lista estándar de Haskell forall t. [t] en implementación se parece mucho a una lista enlazada de C canónica y comparte sus propiedades esenciales. Las listas enlazadas son muy diferentes de las matrices. En particular, el acceso por índice es una operación de tiempo constante O (n) lineal, en lugar de O (1).
Si necesita un acceso aleatorio frecuente, considere el Data.Arrayestándar.
!!es una función insegura parcialmente definida, que provoca un bloqueo de los índices fuera de rango. Tenga en cuenta que la biblioteca estándar contiene algunas de estas funciones parciales ( head, last, etc.). Por seguridad, utilice un tipo de opción Maybeo elSafe módulo.
Ejemplo de una función de indexación total robusta y razonablemente 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
Al trabajar con listas vinculadas, a menudo los ordinales son convenientes:
nth :: Int -> [a] -> Maybe a
nth _ [] = Nothing
nth 1 (x : _) = Just x
nth n (_ : xs) = nth (n - 1) xs
[1,2,3]!!6le dará un error de tiempo de ejecución. Podría evitarse muy fácilmente si!!tuviera el tipo[a] -> Int -> Maybe a. ¡La verdadera razón por la que tenemos Haskell es para evitar tales errores de tiempo de ejecución!