Suche…


Bang Patterns

Mit einem Knall ( ! ) Versehene Muster werden streng und nicht faul bewertet.

foo (!x, y) !z = [x, y, z] 

In diesem Beispiel werden x und z vor der Rückgabe der Liste in schwacher Normalform ausgewertet. Es entspricht:

foo (x, y) z = x `seq` z `seq` [x, y, z]

Bang-Muster werden mit der BangPatterns Haskell 2010 BangPatterns .

Normalformen

Dieses Beispiel bietet einen kurzen Überblick - eine ausführlichere Erläuterung von Normalformen und Beispielen finden Sie in dieser Frage .

Normalform reduziert

Die reduzierte Normalform (oder nur die Normalform, wenn der Kontext klar ist) eines Ausdrucks ist das Ergebnis der Bewertung aller reduzierbaren Unterausdrücke im angegebenen Ausdruck. Aufgrund der nicht strengen Semantik von Haskell (normalerweise als Faulheit bezeichnet ) kann ein Unterausdruck nicht reduziert werden, wenn er sich unter einem Binder befindet (dh einer Lambda-Abstraktion - \x -> .. ). Die Normalform eines Ausdrucks hat die Eigenschaft, dass der Ausdruck eindeutig ist, wenn er existiert.

Mit anderen Worten, es spielt keine Rolle (in Bezug auf die Bedeutungssemantik), in welcher Reihenfolge Sie die Teilausdrücke reduzieren. Der Schlüssel zum Schreiben performanter Haskell-Programme ist jedoch häufig die Sicherstellung, dass der richtige Ausdruck zum richtigen Zeitpunkt ausgewertet wird, dh das Verständnis der operativen Semantik.

Ein Ausdruck, dessen Normalform selbst ist, wird als Normalform bezeichnet .

Einige Ausdrücke, z. B. let x = 1:x in x , haben keine normale Form, sind aber dennoch produktiv. Der Beispielausdruck hat noch einen Wert , wenn man unendliche Werte zulässt, hier die Liste [1,1, ...] . Andere Ausdrücke wie let y = 1+y in y haben keinen Wert oder ihr Wert ist undefined .

Schwache Kopfnormalform

Die RNF entspricht der vollständigen Auswertung eines Ausdrucks - ebenso entspricht die schwache Kopfnormalform (WHNF) der Auswertung des Kopfes des Ausdrucks. Der Kopf eines Ausdrucks e wird vollständig ausgewertet, wenn e eine Anwendung Con e1 e2 .. en und Con ein Konstruktor ist. oder eine Abstraktion \x -> e1 ; oder eine Teilanwendung f e1 e2 .. en , wobei Teilanwendung bedeutet, dass f mehr als n Argumente annimmt (oder äquivalent der Typ von e ein Funktionstyp ist). In jedem Fall können die Unterausdrücke e1..en für den Ausdruck als WHNF ausgewertet oder nicht ausgewertet werden - sie können sogar undefined .

Die Evaluierungssemantik von Haskell kann anhand der WHNF beschrieben werden: Um einen Ausdruck e auszuwerten, wird er zuerst in WHNF ausgewertet und dann alle seine Unterausdrücke rekursiv von links nach rechts ausgewertet.

Die primitive seq Funktion wird verwendet, um einen Ausdruck in WHNF auszuwerten. seq xy ist denotional gleich y (der Wert von seq xy ist genau y ); ferner wird x zu WHNF ausgewertet, wenn y zu WHNF ausgewertet wird. Ein Ausdruck kann auch mit einem Bang-Muster (aktiviert durch die Erweiterung -XBangPatterns ) in WHNF ausgewertet werden, dessen Syntax wie folgt lautet:

f !x y = ... 

Dabei wird x zu WHNF ausgewertet, wenn f ausgewertet wird, während y nicht (unbedingt) ausgewertet wird. Ein Knallmuster kann auch in einem Konstruktor erscheinen, z

data X = Con A !B C .. N

In diesem Fall wird der Konstruktor Con im B Feld als streng bezeichnet, was bedeutet, dass das B Feld in WHNF ausgewertet wird, wenn der Konstruktor auf ausreichend Argumente (hier zwei) angewendet wird.

Faule Muster

Faule oder unwiderlegbare Muster (mit der Syntax ~pat ) sind Muster, die immer übereinstimmen, ohne den übereinstimmenden Wert zu betrachten. Dies bedeutet, dass faule Muster sogar den unteren Werten entsprechen. Nachfolgende Verwendungen von Variablen, die in Teilmustern eines nicht zu widerlegenden Musters gebunden sind, erzwingen jedoch das Auftreten des Musterabgleichs und wird nach unten bewertet, sofern der Abgleich nicht erfolgreich ist.

Die folgende Funktion ist in ihrem Argument faul:

f1 :: Either e Int -> Int
f1 ~(Right 1) = 42

und so bekommen wir

λ» f1 (Right 1)
42
λ» f1 (Right 2)
42
λ» f1 (Left "foo")
42
λ» f1 (error "oops!")
42
λ» f1 "oops!"
*** type mismatch ***

Die folgende Funktion wird mit einem Lazy-Pattern geschrieben, verwendet jedoch tatsächlich die Variable des Patterns, die die Übereinstimmung erzwingt, und schlägt daher für Left Argumente fehl:

f2 :: Either e Int -> Int
f2 ~(Right x) = x + 1

λ» f2 (Right 1)
2
λ» f2 (Right 2)
3
λ» f2 (Right (error "oops!"))
*** Exception: oops!
λ» f2 (Left "foo")
*** Exception: lazypat.hs:5:1-21: Irrefutable pattern failed for pattern (Right x)
λ» f2 (error "oops!")
*** Exception: oops! 

let Bindungen faul sein, verhalten Sie sich wie unwiderlegbare Muster:

act1 :: IO ()
act1 = do
    ss <- readLn
    let [s1, s2] = ss :: [String]
    putStrLn "Done"

act2 :: IO ()
act2 = do
    ss <- readLn
    let [s1, s2] = ss
    putStrLn s1

Hier act1 auf Eingaben, die in eine Liste von Strings act1 , während in act2 putStrLn s1 den Wert von s1 der den Musterabgleich für [s1, s2] erzwingt, sodass es nur für Listen mit genau zwei Strings funktioniert:

λ» act1
> ["foo"]
Done
λ» act2
> ["foo"]
*** readIO: no parse ***

Strikte Felder

In einer data eine Art mit einem Knall prefixing ( ! ) Macht das Feld ein strenges Feld. Wenn der Datenkonstruktor angewendet wird, werden diese Felder in der Normalform "Schwache Kopf" ausgewertet, sodass die Daten in den Feldern garantiert immer in der Normalform "Schwache Kopf" sind.

Strikte Felder können sowohl für Datensatz- als auch für Nicht-Datensatztypen verwendet werden:

data User = User
    { identifier :: !Int
    , firstName :: !Text
    , lastName :: !Text
    }

data T = MkT !Int !Int


Modified text is an extract of the original Stack Overflow Documentation
Lizenziert unter CC BY-SA 3.0
Nicht angeschlossen an Stack Overflow