810 Stimmen

Was ist der Unterschied zwischen . (Punkt) und $ (Dollarzeichen)?

Was ist der Unterschied zwischen dem Punkt (.) und dem Dollarzeichen ($)?

Wie ich es sehe, sind sie beide syntaktischer Zucker, um Klammern nicht verwenden zu müssen.

37voto

Christoph Punkte 1907

Eine Anwendung, die nützlich ist und die mir einige Zeit gekostet hat, um sie aus der sehr kurzen Beschreibung bei Learn You a Haskell zu verstehen: Da

f $ x = f x

und wenn man den rechten Teil eines Ausdrucks, der einen Infix-Operator enthält, in Klammern setzt, wird er zu einer Präfixfunktion, kann man ($ 3) (4 +) schreiben, analog zu (++ ", world") "hello".

Warum sollte das jemand tun? Zum Beispiel für Listen von Funktionen. Beides:

map (++ ", world") ["hello", "goodbye"]
map ($ 3) [(4 +), (3 *)]

sind kürzer als

map (\x -> x ++ ", world") ["hello", "goodbye"]
map (\f -> f 3) [(4 +), (3 *)]

Offensichtlich wären die letzteren Varianten für die meisten Menschen lesbarer.

17 Stimmen

Übrigens würde ich davon abraten, $3 ohne Leerzeichen zu verwenden. Wenn Template Haskell aktiviert ist, wird dies als Splice analysiert, während $ 3 immer das bedeutet, was du gesagt hast. Im Allgemeinen scheint es in Haskell einen Trend zu geben, dass bestimmte Operatoren durch das Bestehen darauf, dass um sie herum Leerzeichen stehen, "gestohlen" werden sollen.

1 Stimmen

Ich brauchte eine Weile, um herauszufinden, wie die Klammern funktionieren: en.wikibooks.org/wiki/Haskell/…

0 Stimmen

Normalerweise sehen wir auf eine großartige Frage wie diese eine wirklich großartige Antwort. Allerdings haben wir mehrere fantastische Antworten, und jede beleuchtet den Punkt weiter und bietet noch einen Aspekt, der beim Verständnis hilft. Ich liebe es!

37voto

Haskell: Unterschied zwischen . (Punkt) und $ (Dollarzeichen)

Was ist der Unterschied zwischen dem Punkt (.) und dem Dollarzeichen ($)? So wie ich es verstehe, sind sie nur Zucker für die Syntax, um keine Klammern verwenden zu müssen.

Sie sind nicht nur Zucker für die Syntax, um keine Klammern verwenden zu müssen - sie sind Funktionen, - infix, daher können wir sie Operatoren nennen.

Komponieren, (.) und wann es zu verwenden ist.

(.) ist die Kompositions-Funktion. Also

result = (f . g) x

ist dasselbe wie eine Funktion zu erstellen, die das Ergebnis ihres Arguments, das an g übergeben wurde, an f weitergibt.

h = \x -> f (g x)
result = h x

Verwende (.), wenn du die Argumente nicht verfügbar hast, um sie an die Funktionen zu übergeben, die du komponieren möchtest.

Rechts-assoziative Anwendung, ($) und wann es zu verwenden ist

($) ist eine rechts-assoziative Anwendungs-Funktion mit niedriger Bindungspräzedenz. Es berechnet also einfach die Dinge rechts von ihm zuerst. Daher ist

result = f $ g x

prozedural dasselbe wie dies (was wichtig ist, da Haskell faul ausgewertet wird, beginnt es zuerst f auszuwerten):

h = f
g_x = g x
result = h g_x

oder kürzer ausgedrückt:

result = f (g x)

Verwende ($), wenn du alle Variablen hast, die du auswerten möchtest, bevor du die vorherige Funktion auf das Ergebnis anwendest.

Wir können dies erkennen, indem wir den Quellcode jeder Funktion lesen.

Den Quellcode lesen

Hier ist der Quellcode für (.):

-- | Funktionskomposition.
{-# INLINE (.) #-}
-- Stelle sicher, dass es nur ZWEI Argumente links hat, sodass es eingebaut wird,
-- wenn es auf zwei Funktionen angewendet wird, selbst wenn es kein Endargument gibt
(.)    :: (b -> c) -> (a -> b) -> a -> c
(.) f g = \x -> f (g x)

Und hier ist der Quellcode für ($):

-- | Anwendungsoperator. Dieser Operator ist überflüssig, da
-- normale Anwendung @(f x)@ dasselbe bedeutet wie @(f '$' x)@. Jedoch hat '$'
-- eine niedrige, rechts-assoziative Bindungspräzedenz, sodass es manchmal Klammern weglassen ermöglicht; zum Beispiel:
--
-- >     f $ g $ h x  =  f (g (h x))
--
-- Er ist auch nützlich in Situationen höherer Ordnung, wie bei @'map' ('$' 0) xs@,
-- oder @'Data.List.zipWith' ('$') fs xs@.
{-# INLINE ($) #-}
($)                     :: (a -> b) -> a -> b
f $ x                   =  f x

Fazit

Verwende Komposition, wenn du die Funktion nicht sofort auswerten musst. Vielleicht möchtest du die aus der Komposition resultierende Funktion an eine andere Funktion weitergeben.

Verwende Anwendung, wenn du alle Argumente für eine vollständige Auswertung bereitstellst.

Also wäre es semantisch bevorzugt, in unserem Beispiel

f $ g x

zu machen, wenn wir x haben (bzw. die Argumente von g), und

f . g

wenn wir es nicht haben.

1 Stimmen

Unter all den großartigen Antworten denke ich, dass diese vielleicht als erste gelesen werden sollte - sie bietet die genaueste und am besten verstandene Erklärung. Und dennoch liefern die anderen Antworten weiterführende Informationen.

13voto

user1721780 Punkte 339

... oder Sie könnten die .- und $-Konstruktionen vermeiden, indem Sie Pipelining verwenden:

third xs = xs |> tail |> tail |> head

Das ist, nachdem Sie die Hilfsfunktion hinzugefügt haben:

(|>) x y = y x

2 Stimmen

Ja, |> ist der F# Pipeline-Betreiber.

6 Stimmen

Eine Sache, die hier zu beachten ist, besteht darin, dass der Operator $ in Haskell tatsächlich eher wie <| in F# als wie |> funktioniert. Normalerweise würde man in Haskell die obige Funktion so schreiben: third xs = head $ tail $ tail $ xs oder vielleicht sogar wie third = head . tail . tail, was in F#-ähnlicher Syntax etwa so aussehen würde: let third = List.head << List.tail << List.tail

1 Stimmen

Warum sollte man eine Hilfsfunktion hinzufügen, um Haskell wie F# aussehen zu lassen? -1

13voto

halacsy Punkte 195

Meine Regel ist einfach (ich bin auch Anfänger):

  • Verwenden Sie . nicht, wenn Sie den Parameter übergeben möchten (die Funktion aufrufen), und
  • Verwenden Sie $ nicht, wenn noch kein Parameter vorhanden ist (eine Funktion zusammenstellen)

Das ist

show $ head [1, 2]

aber niemals:

show . head [1, 2]

6 Stimmen

Gute Heuristik, könnte jedoch mehr Beispiele verwenden

12voto

lol Punkte 3805

Ein großartiger Weg, um mehr über irgendetwas (jede Funktion) zu erfahren, besteht darin, sich daran zu erinnern, dass alles eine Funktion ist! Dieses allgemeine Mantra hilft, aber in spezifischen Fällen wie Operatoren hilft es, sich diesen kleinen Trick zu merken:

:t (.)
(.) :: (b -> c) -> (a -> b) -> a -> c

und

:t ($)
($) :: (a -> b) -> a -> b

Vergiss nicht, :t großzügig zu verwenden und deine Operatoren in () zu verpacken!

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X