Risposte:
Guarda qui , l'operatore utilizzato è !!
.
Cioè [1,2,3]!!1
ti dà 2
, poiché le liste sono indicizzate 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))
. Nota, questo fallirà catastroficamente su un elenco infinito.
!!
è una funzione parziale e quindi non sicura. Date un'occhiata al commento qui sotto e l'uso lens
stackoverflow.com/a/23627631/2574719
Non sto dicendo che ci sia qualcosa di sbagliato nella tua domanda o nella risposta data, ma forse ti piacerebbe conoscere il meraviglioso strumento che è Hoogle per risparmiare tempo in futuro: con Hoogle, puoi cercare le funzioni di libreria standard che corrispondono a una data firma. Quindi, non sapendo nulla in merito !!
, nel tuo caso potresti cercare "qualcosa che prende un Int
e un elenco di qualsiasi cosa e restituisce una singola cosa del genere", vale a dire
Int -> [a] -> a
Ed ecco , con !!
come primo risultato (sebbene la firma del tipo abbia effettivamente i due argomenti al contrario rispetto a quello che abbiamo cercato). Perfetto, eh?
Inoltre, se il codice si basa sull'indicizzazione (invece di consumare dall'inizio dell'elenco), gli elenchi potrebbero in realtà non essere la struttura dati corretta. Per l'accesso basato su indice O (1) sono disponibili alternative più efficienti, come gli array o vettori .
Un'alternativa all'uso (!!)
è usare il
pacchetto lenti e la sua element
funzione e gli operatori associati. L'
obiettivo fornisce un'interfaccia uniforme per accedere a un'ampia varietà di strutture e strutture nidificate al di sopra e al di là degli elenchi. Di seguito mi concentrerò sulla fornitura di esempi e sorvolerò sia sulle firme del tipo che sulla teoria alla base del
pacchetto dell'obiettivo . Se vuoi saperne di più sulla teoria, un buon punto di partenza è il file readme in repository github .
Alla riga di 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
Per accedere a una lista con l'operatore infisso
> [1,2,3,4,5] ^? element 2 -- 0 based indexing
Just 3
A differenza di (!!)
questo non genererà un'eccezione quando si accede a un elemento fuori dai limiti e restituirà Nothing
invece. Si consiglia spesso di evitare funzioni parziali come (!!)
o head
poiché hanno più casi d'angolo e hanno maggiori probabilità di causare un errore di runtime. Puoi leggere un po 'di più sul perché evitare le funzioni parziali in questa pagina wiki .
> [1,2,3] !! 9
*** Exception: Prelude.(!!): index too large
> [1,2,3] ^? element 9
Nothing
È possibile forzare la tecnica dell'obiettivo in modo che sia una funzione parziale e generare un'eccezione quando fuori dai limiti utilizzando l' (^?!)
operatore invece (^?)
dell'operatore.
> [1,2,3] ^?! element 1
2
> [1,2,3] ^?! element 9
*** Exception: (^?!): empty Fold
Tuttavia, questo non è limitato solo agli elenchi. Ad esempio, la stessa tecnica funziona sugli alberi del pacchetto contenitori standard .
> 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
Ora possiamo accedere agli elementi dell'albero in profondità al primo ordine:
> 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
Possiamo anche accesso sequenze dei contenitori pacchetto:
> import qualified Data.Sequence as Seq
> Seq.fromList [1,2,3,4] ^? element 3
Just 4
Possiamo accedere agli array indicizzati int standard dal pacchetto vettoriale , al testo dal pacchetto di testo standard , a bytestrings dal pacchetto standard bytestring ea molte altre strutture dati standard. Questo metodo di accesso standard può essere esteso alle strutture dei dati personali rendendole un'istanza della classe di tipo Taversable , vedere un elenco più lungo di Traversables di esempio nella documentazione di Lens. .
Scavare in strutture annidate è semplice con l' hackage dell'obiettivo . Ad esempio l'accesso a un elemento in un elenco di elenchi:
> [[1,2,3],[4,5,6]] ^? element 0 . element 1
Just 2
> [[1,2,3],[4,5,6]] ^? element 1 . element 2
Just 6
Questa composizione funziona anche quando le strutture dati nidificate sono di tipi diversi. Quindi, ad esempio, se avessi un elenco di alberi:
> :{
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
È possibile nidificare arbitrariamente in profondità con tipi arbitrari purché soddisfino i Traversable
requisiti. Quindi accedere a un elenco di alberi di sequenze di testo non è un problema.
Un'operazione comune in molte lingue consiste nell'assegnare a una posizione indicizzata in un array. In Python potresti:
>>> a = [1,2,3,4,5]
>>> a[3] = 9
>>> a
[1, 2, 3, 9, 5]
Il
pacchetto di lenti offre questa funzionalità (.~)
all'operatore. Sebbene a differenza di Python l'elenco originale non sia stato modificato, viene invece restituito un nuovo elenco.
> 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
è solo una funzione e l' (&)
operatore, parte del
pacchetto dell'obiettivo , è solo un'applicazione di funzione inversa. Eccolo con l'applicazione delle funzioni più comune.
> (element 3 .~ 9) [1,2,3,4,5]
[1,2,3,9,5]
L'assegnazione funziona di nuovo perfettamente con l'annidamento arbitrario di Traversable
s.
> [[1,2,3],[4,5,6]] & element 0 . element 1 .~ 9
[[1,9,3],[4,5,6]]
Data.Traversable
piuttosto che riesportare in lens
?
La risposta diretta era già stata data: Usa !!
.
Tuttavia, i neofiti spesso tendono a fare un uso eccessivo di questo operatore, che è costoso in Haskell (perché lavori su elenchi concatenati singoli, non su array). Esistono diverse tecniche utili per evitarlo, la più semplice è usare zip. Se scrivi zip ["foo","bar","baz"] [0..]
, ottieni una nuova lista con gli indici "attaccati" ad ogni elemento in una coppia:, [("foo",0),("bar",1),("baz",2)]
che spesso è esattamente quello che ti serve.
Tipo di dati elenco standard di Haskell forall t. [t]
nell'implementazione assomiglia molto a un elenco collegato C canonico e ne condivide essenzialmente le proprietà. Gli elenchi collegati sono molto diversi dagli array. In particolare, l'accesso per indice è un'operazione lineare O (n), invece di un'operazione a tempo costante O (1).
Se hai bisogno di un accesso casuale frequente, considera lo Data.Array
standard.
!!
è una funzione parzialmente definita non sicura, che provoca un arresto anomalo per indici fuori intervallo. Essere consapevoli del fatto che la libreria standard contiene alcune tali funzioni parziali ( head
, last
, ecc). Per sicurezza, utilizzare un tipo di opzione Maybe
o il fileSafe
modulo.
Esempio di una funzione di indicizzazione del totale (per indici ≥ 0) ragionevolmente efficiente e robusta:
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
Lavorando con elenchi collegati, spesso gli ordinali sono convenienti:
nth :: Int -> [a] -> Maybe a
nth _ [] = Nothing
nth 1 (x : _) = Just x
nth n (_ : xs) = nth (n - 1) xs
[1,2,3]!!6
ti darà un errore di runtime. Potrebbe essere facilmente evitato se!!
avesse il tipo[a] -> Int -> Maybe a
. Il vero motivo per cui abbiamo Haskell è evitare tali errori di runtime!