Persönlich kann ich nicht verstehen, wie ein At-Index-Accessor, der keinen Vielleicht-Typ zurückgibt, als idiomatischer Haskell akzeptabel ist. [1,2,3]!!6gibt 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!
Worldsayshi
9
Es ist ein Kompromiss. Das Symbol, das sie gewählt haben, ist wahrscheinlich das alarmierendste Symbol, das sie haben könnten. Ich denke, die Idee war, es für Randfälle zuzulassen, aber es als nicht idiomatisch hervorzuheben.
cdosborn
3
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.
DJVs
2
!!ist eine teilweise und damit unsichere Funktion. lensWerfen
goetzc
90
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 Intund 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 .
Hoogle ist absolut großartig. Jeder Haskell-Programmierer sollte es wissen. Es gibt eine Alternative namens Hayoo ( holumbus.fh-wedel.de/hayoo/hayoo.html ). Es sucht während der Eingabe, scheint aber nicht so clever zu sein wie Hoogle.
musiKk
61
Eine Alternative zur Verwendung (!!)besteht darin, das
Objektivpaket und seine elementFunktion 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 .
Zugriff auf Listen und andere Datentypen
Zugriff auf das Objektivpaket
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 Listen
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 Nothingstattdessen zurückgegeben. Es wird häufig empfohlen, Teilfunktionen wie (!!)oder zu vermeiden, headda 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 12>[1,2,3]^?! element 9*** Exception:(^?!): empty Fold
Arbeiten mit anderen Typen als Listen
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.
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
>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. .
Verschachtelte Strukturen
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 TraversableAnforderungen erfüllen. Der Zugriff auf eine Liste von Bäumen mit Textsequenzen ist also kein Problem.
Ändern des n-ten Elements
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 .~ 9ist 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 Traversables.
>[[1,2,3],[4,5,6]]& element 0. element 1.~9[[1,9,3],[4,5,6]]
Darf ich vorschlagen, auf zu verlinken Data.Traversableund nicht wieder zu exportieren lens?
dfeuer
@dfeuer - Ich habe einen Link zu Data.Traversable in base hinzugefügt. Ich habe auch den alten Link beibehalten und darauf hingewiesen, dass die Objektivdokumentation eine längere Liste von Beispiel-Traverables enthält. Danke für den Vorschlag.
Davorak
11
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.
Sie müssen dort auch vorsichtig mit Ihren Typen sein. Meistens möchten Sie nicht, dass die Indizes langsame Ganzzahlen und keine schnellen Maschinen-Ints sind. Abhängig davon, was genau Ihre Funktion tut und wie explizit Ihre Eingabe ist, kann Haskell den Typ von [0 ..] als [Integer] anstelle von [Int] ableiten.
Chrisdb
4
Sie können verwenden !!, aber wenn Sie es rekursiv tun möchten, ist unten eine Möglichkeit, dies zu tun:
dataAt :: Int ->[a]-> a
dataAt _[]= error "Empty List!"
dataAt y (x:xs)| y <=0= x
| otherwise = dataAt (y-1) xs
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.ArrayStandard.
!!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, lastusw.). Verwenden Sie aus Sicherheitsgründen einen Optionstyp Maybeoder das SafeModul.
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!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.!!
ist eine teilweise und damit unsichere Funktion.lens
WerfenIch 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 eineInt
und eine Liste von was auch immer nimmt und ein einzelnes solches zurückgibt, was auch immer", nämlichUnd 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 .
quelle
Eine Alternative zur Verwendung
(!!)
besteht darin, das Objektivpaket und seineelement
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 .Zugriff auf Listen und andere Datentypen
Zugriff auf das Objektivpaket
In der Kommandozeile:
Zugriff auf Listen
Zugriff auf eine Liste mit dem Infix-Operator
Im Gegensatz
(!!)
dazu wird beim Zugriff auf ein Element außerhalb der Grenzen keine Ausnahme ausgelöst undNothing
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 .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.Arbeiten mit anderen Typen als Listen
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.
Wir können jetzt in der Reihenfolge der Tiefe zuerst auf die Elemente des Baums zugreifen:
Wir können auch über das Containerpaket auf Sequenzen zugreifen :
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. .
Verschachtelte Strukturen
Das Eingraben in verschachtelte Strukturen ist mit dem Objektiv- Hackage einfach . Zum Beispiel Zugriff auf ein Element in einer Liste von Listen:
Diese Komposition funktioniert auch dann, wenn die verschachtelten Datenstrukturen unterschiedlichen Typs sind. Wenn ich zum Beispiel eine Liste von Bäumen hätte:
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.Ändern des n-ten Elements
In vielen Sprachen wird häufig eine indizierte Position in einem Array zugewiesen. In Python könnten Sie:
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.element 3 .~ 9
ist nur eine Funktion und der(&)
Bediener, Teil des Objektivpakets , ist nur eine Umkehrfunktionsanwendung. Hier ist es mit der allgemeineren Funktionsanwendung.Die Zuweisung funktioniert wieder einwandfrei mit willkürlicher Verschachtelung von
Traversable
s.quelle
Data.Traversable
und nicht wieder zu exportierenlens
?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.quelle
Sie können verwenden
!!
, aber wenn Sie es rekursiv tun möchten, ist unten eine Möglichkeit, dies zu tun:quelle
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 OptionstypMaybe
oder dasSafe
Modul.Beispiel einer einigermaßen effizienten, robusten Gesamtindexierungsfunktion (für Indizes ≥ 0):
Bei der Arbeit mit verknüpften Listen sind häufig Ordnungszahlen praktisch:
quelle