Antworten:
Schauen Sie hier , der verwendete Operator ist !!
.
Dh [1,2,3]!!1
gibt Ihnen 2
, da Listen 0-indiziert sind.
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))
. Beachten Sie, dass dies bei einer unendlichen Liste katastrophal fehlschlägt.
Ich sage nicht, dass mit Ihrer Frage oder der gegebenen Antwort etwas nicht stimmt, aber vielleicht möchten Sie etwas über das wunderbare Tool von Hoogle wissen, mit dem Sie sich in Zukunft Zeit sparen können: Mit Hoogle können Sie nach Standardbibliotheksfunktionen suchen die einer bestimmten Unterschrift entsprechen. Wenn Sie also nichts darüber wissen !!
, könnten Sie in Ihrem Fall nach "etwas suchen, das eine Int
und eine Liste von was auch immer nimmt und ein einzelnes solches zurückgibt, was auch immer", nämlich
Int -> [a] -> a
Und siehe da , mit !!
als erstem Ergebnis (obwohl die Typensignatur tatsächlich die beiden Argumente im Vergleich zu dem, wonach wir gesucht haben, umgekehrt hat). Ordentlich, was?
Wenn Ihr Code auf der Indizierung beruht (anstatt von der Vorderseite der Liste zu konsumieren), sind Listen möglicherweise tatsächlich nicht die richtige Datenstruktur. Für den O (1) -indexbasierten Zugriff gibt es effizientere Alternativen wie Arrays oder Vektoren .
Eine Alternative zur Verwendung (!!)
besteht darin, das
Objektivpaket und seine element
Funktion sowie die zugehörigen Bediener zu verwenden. Das
Objektiv bietet eine einheitliche Schnittstelle für den Zugriff auf eine Vielzahl von Strukturen und verschachtelten Strukturen über Listen hinaus. Im Folgenden werde ich mich auf die Bereitstellung von Beispielen konzentrieren und sowohl die Typensignaturen als auch die Theorie hinter dem Objektivpaket beschönigen
. Wenn Sie mehr über die Theorie erfahren möchten, ist die Readme-Datei im Github-Repo ein guter Ausgangspunkt .
In der Kommandozeile:
$ 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
Zugriff auf eine Liste mit dem Infix-Operator
> [1,2,3,4,5] ^? element 2 -- 0 based indexing
Just 3
Im Gegensatz (!!)
dazu wird beim Zugriff auf ein Element außerhalb der Grenzen keine Ausnahme ausgelöst und Nothing
stattdessen zurückgegeben. Es wird häufig empfohlen, Teilfunktionen wie (!!)
oder zu vermeiden, head
da sie mehr Eckfälle aufweisen und mit größerer Wahrscheinlichkeit einen Laufzeitfehler verursachen. Auf dieser Wiki-Seite erfahren Sie mehr darüber, warum Sie Teilfunktionen vermeiden sollten .
> [1,2,3] !! 9
*** Exception: Prelude.(!!): index too large
> [1,2,3] ^? element 9
Nothing
Sie können die Linsentechnik als Teilfunktion erzwingen und eine Ausnahme auslösen, wenn sie außerhalb der Grenzen liegt, indem Sie den (^?!)
Operator anstelle des (^?)
Operators verwenden.
> [1,2,3] ^?! element 1
2
> [1,2,3] ^?! element 9
*** Exception: (^?!): empty Fold
Dies ist jedoch nicht nur auf Listen beschränkt. Zum Beispiel funktioniert auf die gleiche Technik , die auf Bäumen aus dem Standard - Container - Paket.
> 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
Wir können jetzt in der Reihenfolge der Tiefe zuerst auf die Elemente des Baums zugreifen:
> 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
Wir können auch über das Containerpaket auf Sequenzen zugreifen :
> import qualified Data.Sequence as Seq
> Seq.fromList [1,2,3,4] ^? element 3
Just 4
Wir können den Standard - int indizierten Arrays aus dem Zugriff auf Vektor - Paket, Text aus dem Standard - Text - Paket, fro bytestrings das Standard bytestring Paket und viele anderen Standard - Datenstrukturen. Diese Standardzugriffsmethode kann auf Ihre persönlichen Datenstrukturen erweitert werden, indem Sie sie zu einer Instanz der Typklasse Taversable machen . Eine längere Liste von Beispiel- Traversables finden Sie in der Lens-Dokumentation. .
Das Eingraben in verschachtelte Strukturen ist mit dem Objektiv- Hackage einfach . Zum Beispiel Zugriff auf ein Element in einer Liste von Listen:
> [[1,2,3],[4,5,6]] ^? element 0 . element 1
Just 2
> [[1,2,3],[4,5,6]] ^? element 1 . element 2
Just 6
Diese Komposition funktioniert auch dann, wenn die verschachtelten Datenstrukturen unterschiedlichen Typs sind. Wenn ich zum Beispiel eine Liste von Bäumen hätte:
> :{
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
Sie können beliebig tief mit beliebigen Typen verschachteln, solange diese die Traversable
Anforderungen erfüllen. Der Zugriff auf eine Liste von Bäumen mit Textsequenzen ist also kein Problem.
In vielen Sprachen wird häufig eine indizierte Position in einem Array zugewiesen. In Python könnten Sie:
>>> a = [1,2,3,4,5]
>>> a[3] = 9
>>> a
[1, 2, 3, 9, 5]
Das
Objektivpaket bietet diese Funktionalität für den (.~)
Bediener. Obwohl im Gegensatz zu Python die ursprüngliche Liste nicht mutiert ist, wird eine neue Liste zurückgegeben.
> 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
ist nur eine Funktion und der (&)
Bediener, Teil des
Objektivpakets , ist nur eine Umkehrfunktionsanwendung. Hier ist es mit der allgemeineren Funktionsanwendung.
> (element 3 .~ 9) [1,2,3,4,5]
[1,2,3,9,5]
Die Zuweisung funktioniert wieder einwandfrei mit willkürlicher Verschachtelung von Traversable
s.
> [[1,2,3],[4,5,6]] & element 0 . element 1 .~ 9
[[1,9,3],[4,5,6]]
Data.Traversable
und nicht wieder zu exportieren lens
?
Die klare Antwort wurde bereits gegeben: Verwenden !!
.
Neulinge neigen jedoch häufig dazu, diesen Operator zu überbeanspruchen, was in Haskell teuer ist (weil Sie an einzelnen verknüpften Listen arbeiten, nicht an Arrays). Es gibt verschiedene nützliche Techniken, um dies zu vermeiden. Die einfachste ist die Verwendung von Zip. Wenn Sie schreiben zip ["foo","bar","baz"] [0..]
, erhalten Sie eine neue Liste mit den Indizes, die an jedes Element in einem Paar "angehängt" sind. Dies [("foo",0),("bar",1),("baz",2)]
ist häufig genau das, was Sie benötigen.
Der Standard-Listendatentyp von Haskell forall t. [t]
in der Implementierung ähnelt stark einer kanonischen C-verknüpften Liste und teilt seine wesentlichen Eigenschaften. Verknüpfte Listen unterscheiden sich stark von Arrays. Insbesondere ist der Zugriff per Index eine O (n) -Linear- anstelle einer O (1) -Operation mit konstanter Zeit.
Wenn Sie häufigen Direktzugriff benötigen, beachten Sie den Data.Array
Standard.
!!
ist eine unsichere, teilweise definierte Funktion, die bei Indizes außerhalb des Bereichs zu einem Absturz führt. Beachten Sie, dass die Standard - Bibliothek einige solche Teilfunktionen enthält ( head
, last
usw.). Verwenden Sie aus Sicherheitsgründen einen Optionstyp Maybe
oder das Safe
Modul.
Beispiel einer einigermaßen effizienten, robusten Gesamtindexierungsfunktion (für Indizes ≥ 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
Bei der Arbeit mit verknüpften Listen sind häufig Ordnungszahlen praktisch:
nth :: Int -> [a] -> Maybe a
nth _ [] = Nothing
nth 1 (x : _) = Just x
nth n (_ : xs) = nth (n - 1) xs
[1,2,3]!!6
gibt Ihnen einen Laufzeitfehler. Es könnte sehr leicht vermieden werden, wenn!!
der Typ hätte[a] -> Int -> Maybe a
. Der Grund, warum wir Haskell haben, ist, solche Laufzeitfehler zu vermeiden!