Jaka jest różnica między kropką (.)
a znakiem dolara ($)
?
Jak rozumiem, oba są cukrem syntaktycznym, ponieważ nie muszą używać nawiasów.
Jaka jest różnica między kropką (.)
a znakiem dolara ($)
?
Jak rozumiem, oba są cukrem syntaktycznym, ponieważ nie muszą używać nawiasów.
Odpowiedzi:
$
Operatora dla uniknięcia nawiasów. Wszystko, co pojawi się po nim, będzie miało pierwszeństwo przed wszystkim, co nastąpi wcześniej.
Załóżmy na przykład, że masz wiersz o treści:
putStrLn (show (1 + 1))
Jeśli chcesz się pozbyć tych nawiasów, dowolny z poniższych wierszy również zrobiłby to samo:
putStrLn (show $ 1 + 1)
putStrLn $ show (1 + 1)
putStrLn $ show $ 1 + 1
Głównym celem .
operatora nie jest unikanie nawiasów, ale połączenie funkcji. Pozwala powiązać wyjście tego, co pojawia się po prawej stronie, z wejściem tego, co pojawia się po lewej stronie. Powoduje to zwykle mniej nawiasów, ale działa inaczej.
Wracając do tego samego przykładu:
putStrLn (show (1 + 1))
(1 + 1)
nie ma danych wejściowych i dlatego nie można go używać z .
operatorem.show
może wziąć Int
i zwrócić a String
.putStrLn
może wziąć String
i zwrócić IO ()
.Możesz połączyć, show
aby putStrLn
polubić to:
(putStrLn . show) (1 + 1)
Jeśli jest to zbyt wiele nawiasów na twój gust, pozbądź się ich z $
operatorem:
putStrLn . show $ 1 + 1
putStrLn . show . (+1) $ 1
byłoby równoważne. Masz rację, ponieważ większość operatorów (wszystkie?) Są funkcjami.
map ($3)
. Chodzi mi głównie o to, $
aby unikać nawiasów, ale to nie tak, że po to są.
map ($3)
jest funkcją typu Num a => [(a->b)] -> [b]
. Pobiera listę funkcji z liczbą, stosuje 3 do wszystkich i zbiera wyniki.
Mają różne typy i różne definicje:
infixr 9 .
(.) :: (b -> c) -> (a -> b) -> (a -> c)
(f . g) x = f (g x)
infixr 0 $
($) :: (a -> b) -> a -> b
f $ x = f x
($)
ma zastąpić normalną aplikację funkcji, ale ma inny priorytet, aby uniknąć nawiasów. (.)
służy do połączenia dwóch funkcji w celu utworzenia nowej funkcji.
W niektórych przypadkach są one wymienne, ale ogólnie nie jest to prawdą. Typowy przykład, w którym się znajdują, to:
f $ g $ h $ x
==>
f . g . h $ x
Innymi słowy w łańcuchu $
s, wszystkie oprócz ostatniego można zastąpić.
x
była funkcja? Czy możesz użyć go .
jako ostatecznego?
x
w tym kontekście, to tak - ale wtedy „ostateczny” dotyczyłby czegoś innego niż x
. Jeśli nie aplikujesz x
, nie jest niczym innym jak x
byciem wartością.
Zauważ też, że ($)
jest to funkcja tożsamości wyspecjalizowana w typach funkcji . Funkcja tożsamości wygląda następująco:
id :: a -> a
id x = x
Chociaż ($)
wygląda tak:
($) :: (a -> b) -> (a -> b)
($) = id
Zauważ, że celowo dodałem dodatkowe nawiasy w podpisie typu.
Zastosowania ($)
można zwykle wyeliminować przez dodanie nawiasów (chyba że w sekcji użyto operatora). Np .: f $ g x
staje się f (g x)
.
Zastosowania (.)
są często nieco trudniejsze do zastąpienia; zazwyczaj potrzebują lambda lub wprowadzenia jawnego parametru funkcji. Na przykład:
f = g . h
staje się
f x = (g . h) x
staje się
f x = g (h x)
Mam nadzieję że to pomoże!
($)
umożliwia łączenie funkcji bez dodawania nawiasów w celu sterowania kolejnością oceny:
Prelude> head (tail "asdf")
's'
Prelude> head $ tail "asdf"
's'
Operator tworzenia (.)
tworzy nową funkcję bez podawania argumentów:
Prelude> let second x = head $ tail x
Prelude> second "asdf"
's'
Prelude> let second = head . tail
Prelude> second "asdf"
's'
Powyższy przykład jest prawdopodobnie ilustracyjny, ale tak naprawdę nie pokazuje wygody używania kompozycji. Oto kolejna analogia:
Prelude> let third x = head $ tail $ tail x
Prelude> map third ["asdf", "qwer", "1234"]
"de3"
Jeśli użyjemy trzeciego tylko raz, możemy uniknąć nazwania go za pomocą lambda:
Prelude> map (\x -> head $ tail $ tail x) ["asdf", "qwer", "1234"]
"de3"
Wreszcie, kompozycja pozwala nam uniknąć lambda:
Prelude> map (head . tail . tail) ["asdf", "qwer", "1234"]
"de3"
Jedna aplikacja, która jest przydatna i zajęła mi trochę czasu, aby zrozumieć bardzo krótki opis na stronie hashell : Od:
f $ x = f x
a nawiasowanie prawej strony wyrażenia zawierającego operator infix przekształca go w funkcję prefiksu, którą można napisać ($ 3) (4+)
analogicznie (++", world") "hello"
.
Dlaczego ktoś miałby to robić? Na przykład dla list funkcji. Obie:
map (++", world") ["hello","goodbye"]`
i:
map ($ 3) [(4+),(3*)]
są krótsze niż map (\x -> x ++ ", world") ...
lub map (\f -> f 3) ...
. Oczywiście te ostatnie warianty byłyby bardziej czytelne dla większości ludzi.
$3
bez przestrzeni. Jeśli szablon Haskell jest włączony, zostanie on przeanalizowany jako splot, a $ 3
zawsze oznacza to, co powiedziałeś. Ogólnie rzecz biorąc, wydaje się, że w Haskell istnieje tendencja do „kradzieży” fragmentów składni przez naleganie, aby niektórzy operatorzy mieli wokół siebie spacje, aby byli traktowani jako tacy.
Haskell: różnica między
.
(kropka) a$
(znak dolara)Jaka jest różnica między kropką
(.)
a znakiem dolara($)
? Jak rozumiem, oba są cukrem syntaktycznym, ponieważ nie muszą używać nawiasów.
Są one nie cukrem syntaktycznym, ponieważ nie muszą używać nawiasów - są funkcjami, - naprawione, dlatego możemy nazwać je operatorami.
(.)
i kiedy z niego korzystać.(.)
to funkcja tworzenia. Więc
result = (f . g) x
jest równoznaczne z budowaniem funkcji, która przekazuje wynik argumentu przekazanego g
do f
.
h = \x -> f (g x)
result = h x
Posługiwać się (.)
gdy nie masz dostępnych argumentów do przekazania funkcji, które chcesz skomponować.
($)
i kiedy z nich korzystać($)
jest funkcją stosującą asocjację z niskim priorytetem wiązania. Więc najpierw oblicza tylko rzeczy po prawej stronie. A zatem,
result = f $ g x
jest taki sam jak ten, proceduralnie (co ma znaczenie, ponieważ Haskell jest oceniany leniwie, zacznie oceniać jako f
pierwszy):
h = f
g_x = g x
result = h g_x
lub bardziej zwięźle:
result = f (g x)
Użyj, ($)
gdy masz wszystkie zmienne do oceny przed zastosowaniem poprzedniej funkcji do wyniku.
Możemy to zobaczyć, czytając źródło dla każdej funkcji.
Oto źródło dla (.)
:
-- | Function composition.
{-# INLINE (.) #-}
-- Make sure it has TWO args only on the left, so that it inlines
-- when applied to two functions, even if there is no final argument
(.) :: (b -> c) -> (a -> b) -> a -> c
(.) f g = \x -> f (g x)
A oto źródło dla ($)
:
-- | Application operator. This operator is redundant, since ordinary
-- application @(f x)@ means the same as @(f '$' x)@. However, '$' has
-- low, right-associative binding precedence, so it sometimes allows
-- parentheses to be omitted; for example:
--
-- > f $ g $ h x = f (g (h x))
--
-- It is also useful in higher-order situations, such as @'map' ('$' 0) xs@,
-- or @'Data.List.zipWith' ('$') fs xs@.
{-# INLINE ($) #-}
($) :: (a -> b) -> a -> b
f $ x = f x
Użyj kompozycji, gdy nie musisz od razu oceniać funkcji. Może chcesz przekazać funkcję wynikającą ze składu do innej funkcji.
Użyj aplikacji, gdy podajesz wszystkie argumenty do pełnej oceny.
W naszym przykładzie byłoby to semantycznie lepsze
f $ g x
kiedy mamy x
(a raczej g
argumenty) i wykonujemy:
f . g
kiedy my nie.
... lub możesz uniknąć .
i $
konstrukcji, używając rurociągów :
third xs = xs |> tail |> tail |> head
To po dodaniu funkcji pomocnika:
(|>) x y = y x
$
operator faktycznie działa bardziej jak F # 's <|
niż to robi |>
, zazwyczaj w Haskell chcesz napisać powyższą funkcję tak: third xs = head $ tail $ tail $ xs
a może nawet jak third = head . tail . tail
, co w F # -Style składnia byłoby coś takiego:let third = List.head << List.tail << List.tail
Świetnym sposobem, aby dowiedzieć się więcej o czymkolwiek (dowolnej funkcji) jest zapamiętanie, że wszystko jest funkcją! Ta ogólna mantra pomaga, ale w szczególnych przypadkach, takich jak operatorzy, pomaga zapamiętać tę małą sztuczkę:
:t (.)
(.) :: (b -> c) -> (a -> b) -> a -> c
i
:t ($)
($) :: (a -> b) -> a -> b
Pamiętaj tylko, aby używać :t
swobodnie i owijać swoich operatorów ()
!
Moja zasada jest prosta (ja też jestem początkujący):
.
jeśli chcesz przekazać parametr (wywołać funkcję), i$
jeśli nie ma jeszcze parametru (utwórz funkcję)To jest
show $ head [1, 2]
ale nigdy:
show . head [1, 2]
Myślę, że krótki przykład tego, gdzie byś użył .
i nie $
pomógłby wyjaśnić rzeczy.
double x = x * 2
triple x = x * 3
times6 = double . triple
:i times6
times6 :: Num c => c -> c
Zauważ, że times6
jest to funkcja utworzona z kompozycji funkcji.
Wszystkie pozostałe odpowiedzi są całkiem dobre. Ale jest ważny szczegół użyteczności dotyczący tego, jak ghc traktuje $, że moduł sprawdzania typu ghc pozwala na instatiar z typami wyższego rzędu / ilościowymi. Jeśli spojrzysz na $ id
przykład, zobaczysz, że przyjmie on funkcję, której argument sam w sobie jest funkcją polimorficzną. Takie małe rzeczy nie mają tej samej elastyczności z równorzędnym operatorem zdenerwowanym. (To sprawia, że zastanawiam się, czy $! Zasługuje na to samo traktowanie, czy nie)