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.showmoże wziąć Inti zwrócić a String.putStrLnmoże wziąć Stringi zwrócić IO ().Możesz połączyć, showaby putStrLnpolubić 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) $ 1był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ć.
xbyła funkcja? Czy możesz użyć go .jako ostatecznego?
xw tym kontekście, to tak - ale wtedy „ostateczny” dotyczyłby czegoś innego niż x. Jeśli nie aplikujesz x, nie jest niczym innym jak xbyciem 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 xstaje 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.
$3bez przestrzeni. Jeśli szablon Haskell jest włączony, zostanie on przeanalizowany jako splot, a $ 3zawsze 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 gdo 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 fpierwszy):
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 gargumenty) 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 $ xsa 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ć :tswobodnie 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 times6jest 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 $ idprzykł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)