2014-05-10 25 views
5

Gibt es eine schnelle und einfache Möglichkeit, die Priorität und Assoziativität einer Funktion in GHCI zu erkennen?Wie erkennt man die Priorität und Assoziativität einer Funktion in GHCI?

Ich habe festgestellt, dass eine einfache Methode Bruteforce ist, einen Operator mit einem anderen zu kombinieren, bis Sie einen Precedence Parsing Error (PPE) erhalten. Um zum Beispiel den Vorrang/Assoziativität von !! zu entdecken, können wir 10 verschiedene Betreiber von infix n für 0 ≤ n < 10 definieren:

>>> let infix 0 %; (%) = ($) 
>>> undefined % undefined !! undefined 
*** Exception: Prelude.undefined 
>>> let infix 1 %; (%) = ($) 
>>> undefined % undefined !! undefined 
*** Exception: Prelude.undefined 
[...] 
>>> let infix 8 %; (%) = ($) 
>>> undefined % undefined !! undefined 
*** Exception: Prelude.undefined 
>>> let infix 9 %; (%) = ($) 
>>> undefined % undefined !! undefined 

<interactive>:21:1: 
    Precedence parsing error 
     cannot mix `%' [infix 9] and `!!' [infixl 9] in the same infix expression 

Der Fehler sagt Ihnen, was die predences und fixities sind. Da die 10 definierten Funktionen nicht assoziativ sind, gibt es keine Möglichkeit, eine PPE zu vermeiden, wenn die Rangstufen gleich sind. Der naive Ansatz wäre gewesen, 10 Funktionen jeder Assoziativität auszuprobieren, was zu einem WCET von 20 führt (denn wenn man auf der gleichen Assoziativität wie das Ziel anfängt, würde die 10 mit dieser Assoziativität bestehen, aber dann würde der Fehler passieren, wenn du erst einmal kommst der kth Vorrang der nächsten Assoziativität, wobei k der Vorrang der Zielfunktion ist).

Aber gibt es einen schnelleren Weg? I Denken Sie Sie können Bisektion im Durchschnitt Log (10) Schritte zu erreichen. Wenn das Ziel beispielsweise !!! ist, das als !! definiert ist, aber mit infixl 6, könnten wir einen Ausdruck (:[]) % "AB" !!! 1 machen, wobei % eine der gleichen 10 Dummy-Funktionen ist. Wenn der Vorrang von % zu niedrig ist, um ein PPE zu verursachen, liefert der Ausdruck "B" (weil der Ausdruck zu (:[]) % ("AB" !!! 1) analysiert), während, wenn der Vorrang zu hoch ist, (weil der Ausdruck zu ((:[]) % "AB") !!! 1 parsen wird). Beispiel:

>>> let infixl 6 !!!; (!!!) = (!!) 
>>> let infix 5 %; (%) = ($) 
>>> (:[]) % "AB" !!! 1 
"B" 
>>> --too low 
>>> let infix 7 %; (%) = ($) 
>>> (:[]) % "AB" !!! 1 
"*** Exception: Prelude.(!!): index too large 

>>> --too high 
>>> let infix 6 %; (%) = ($) 
>>> (:[]) % "AB" !!! 1 

<interactive>:10:1: 
    Precedence parsing error 
     cannot mix `%' [infix 6] and `!!!' [infixl 6] in the same infix expression 
>>> --got it 

Allerdings bin ich nicht sicher über diesen Ansatz.

  • Sie haben die Befindlichkeit, deren Anzahl zu halten nächsten zu erraten, und tun, um die Abteilung usw. in Ihrem Kopf (von Papier/Rechner würde wahrscheinlich langsamer sein als nur die 10 Schritte in der einfachen Methode zu tun)
  • Ist es völlig allgemein? Kannst du einen Testausdruck immer so aufbauen?
  • Ich bin mir nicht sicher, ob ich mit einem solchen Ausdruck immer schneller kommen kann als mit der einfachen Methode. In diesem Fall war es tatsächlich schneller, und ich kam tatsächlich mit der Halbierung Methode vor Ich kam mit der einfachen Methode
  • Wenn das vorherige Anliegen gilt, gibt es eine völlig allgemeine Möglichkeit, Ausdrücke davon automatisch zu generieren nett?

Kennt jemand eine alternative Methode? Ich kann mir vorstellen, dass es einen Weg geben könnte, dies in einem einzigen Schritt zu tun. Aber nach einer Stunde ist das Beste, was ich mir vorstellen kann, diese Halbierungsmethode.

Antwort

15

Ja. Sie können den Befehl :info verwenden.

Prelude> :info (+) 
class Num a where 
    (+) :: a -> a -> a 
    ... 
     -- Defined in `GHC.Num' 
infixl 6 + 

Prelude> :info (.) 
(.) :: (b -> c) -> (a -> b) -> a -> c -- Defined in `GHC.Base' 
infixr 9 . 

Prelude> :info ($) 
($) :: (a -> b) -> a -> b  -- Defined in `GHC.Base' 
infixr 0 $ 

Wenn es nicht sagen, es ist die Standardeinstellung von infixl 9.

+2

Sie können auch nur eingeben, z. B. ': i +'. –

15

@kqr ‚s Antwort ist natürlich am besten aus GHCi, aber ich werde beachten Sie nur, dass es ist ein schneller Weg, ähnlich dem, was Sie versucht haben, die ich manchmal mit lambdabot in IRC-Kanälen verwenden (was nicht unterstützt :info).

>>> (0$0 !!) 

Wegen des Abschnitts Anforderung, dass (expr `op`) nur verwendet werden, wenn expr `op` var identisch (expr) `op` var parst sowie $ rechts assoziativ mit der niedrigsten fixity ist, dass die Expression wird nie Parst unabhängig von der Unveränderlichkeit von !!, und gibt Ihnen die Festigkeit von !! in der Fehlermeldung.

+1

Hmm. Ich bin versucht, dies als Antwort zu akzeptieren, da es für alle Präzedenzfälle (selbst Standard) funktioniert, im Gegensatz zu ': info'. Es ist eine weitere Sache, die man sich merken muss, aber auch "Info". Plus ': info' sorgt dafür, dass Sie sich die voreingestellte Präzedenz/Assoziativität merken müssen. – Dog

+0

@Dog: Ja, die WCETs sind beide 1, aber Sie müssen sich doppelt so viele Informationen merken, um ': info:' zu verwenden. Allerdings würde ich behaupten, dass es sich gelohnt hat, statt dessen ": info" auswendig zu lernen und dass die Standardfixität "infixl 9" ist. Die letzte Tatsache ist erforderlich, wenn Sie Ihre eigene Haskell-Implementierung schreiben, also können Sie sie sich einfach einprägen. Dies führt zu einer amortisierten Einsparung, sobald Sie Haskell implementiert haben. Darüber hinaus ist ': info' nützlich für andere Zwecke, wie das Anzeigen der Instanzen einer Klasse oder das Finden, wo ein Wert definiert ist. –

+0

Richtig, auch wenn Sie etwas Code lesen, müssen Sie die Assoziativität/Präzedenz kennen, um zu wissen, was es tut, also ist das ein anderer Fall für die Verwendung von ': info'. – Dog