回答:
$
オペレータは、括弧を回避するためです。その後に現れるものは、それ以前に来るものよりも優先されます。
たとえば、次のような行があるとします。
putStrLn (show (1 + 1))
これらの括弧を削除したい場合は、次の行も同じことを行います。
putStrLn (show $ 1 + 1)
putStrLn $ show (1 + 1)
putStrLn $ show $ 1 + 1
.
演算子の主な目的は、括弧を回避することではなく、関数をチェーンすることです。右側に表示されるものの出力を左側に表示されるものの入力に関連付けることができます。これにより、通常、括弧が少なくなりますが、動作が異なります。
同じ例に戻る:
putStrLn (show (1 + 1))
(1 + 1)
入力がないため、.
演算子と一緒に使用できません。show
を取り、Int
を返すことができますString
。putStrLn
を取り、String
を返すことができますIO ()
。あなたはこのように連鎖show
することができますputStrLn
:
(putStrLn . show) (1 + 1)
括弧が多すぎて好みに合わない場合は、$
演算子で括弧を削除します。
putStrLn . show $ 1 + 1
putStrLn . show . (+1) $ 1
は同等です。ほとんどの(すべての?)中置演算子は関数です。
map ($3)
。$
つまり、私は主に括弧を避けるためにも使用しますが、それだけではありません。
map ($3)
タイプの関数ですNum a => [(a->b)] -> [b]
。数値を取る関数のリストを取得し、それらすべてに3を適用して結果を収集します。
それらは異なるタイプと異なる定義を持っています:
infixr 9 .
(.) :: (b -> c) -> (a -> b) -> (a -> c)
(f . g) x = f (g x)
infixr 0 $
($) :: (a -> b) -> a -> b
f $ x = f x
($)
は通常の関数アプリケーションを置き換えることを目的としていますが、かっこを回避するために優先順位が異なります。(.)
2つの関数を組み合わせて新しい関数を作成するためのものです。
場合によっては交換可能ですが、これは一般的には当てはまりません。それらの典型的な例は次のとおりです。
f $ g $ h $ x
==>
f . g . h $ x
つまり、$
sのチェーンでは、最後のものを除くすべてを次のように置き換えることができます。.
x
関数の場合はどうなりますか?.
最後に使用できますか?
x
にこのコンテキストで適用している場合は、はい-しかし、「最終」のは以外のものに適用されますx
。を適用しない場合は、値であることにx
変わりはありませんx
。
また、これ($)
は関数型に特化した識別関数です。識別関数は次のようになります。
id :: a -> a
id x = x
一方で($)
次のようになります。
($) :: (a -> b) -> (a -> b)
($) = id
型シグネチャに余分な括弧を意図的に追加したことに注意してください。
の使用は、($)
通常、括弧を追加することで削除できます(演算子がセクションで使用されている場合を除く)。例:にf $ g x
なりf (g x)
ます。
の使用は(.)
、多くの場合、置き換えるのが少し難しいです。通常、ラムダまたは明示的な関数パラメーターの導入が必要です。例えば:
f = g . h
なる
f x = (g . h) x
なる
f x = g (h x)
お役に立てれば!
($)
評価の順序を制御する括弧を追加せずに関数を一緒にチェーンすることができます:
Prelude> head (tail "asdf")
's'
Prelude> head $ tail "asdf"
's'
構成演算子(.)
は、引数を指定せずに新しい関数を作成します。
Prelude> let second x = head $ tail x
Prelude> second "asdf"
's'
Prelude> let second = head . tail
Prelude> second "asdf"
's'
上記の例は間違いなく説明的なものですが、コンポジションを使用することの利便性を実際に示しているわけではありません。これは別のアナロジーです:
Prelude> let third x = head $ tail $ tail x
Prelude> map third ["asdf", "qwer", "1234"]
"de3"
3番目を1度だけ使用する場合は、ラムダを使用して名前を付けるのを回避できます。
Prelude> map (\x -> head $ tail $ tail x) ["asdf", "qwer", "1234"]
"de3"
最後に、合成によりラムダを回避できます。
Prelude> map (head . tail . tail) ["asdf", "qwer", "1234"]
"de3"
便利なアプリケーションの1つで、hashellを学ぶための非常に短い説明から理解するのに少し時間がかかりました。
f $ x = f x
中置演算子を含む式の右辺を括弧で囲むと、前置関数に変換さ($ 3) (4+)
れ(++", world") "hello"
ます。
なぜ誰もがこれをするのでしょうか?関数のリストなど。どちらも:
map (++", world") ["hello","goodbye"]`
そして:
map ($ 3) [(4+),(3*)]
map (\x -> x ++ ", world") ...
またはより短いmap (\f -> f 3) ...
。明らかに、後者のバリアントはほとんどの人にとって読みやすくなります。
$3
スペースなしで使用しないことをお勧めします。テンプレートHaskellが有効な場合、これはスプライスとして解析され$ 3
ますが、常にあなたが言ったことを意味します。一般に、Haskellには、特定の演算子をそのように処理するためのスペースがあると主張することで、構文のビットを「盗む」傾向があるようです。
Haskell:(
.
ドット)と$
(ドル記号)の違いドット
(.)
とドル記号の違いは何($)
ですか?私が理解しているように、それらは両方とも括弧を使用する必要がないため、構文上の砂糖です。
括弧を使用する必要がないため、これらは構文糖ではありません。これらは関数であり、固定されているため、演算子と呼ぶことができます。
(.)
使用するタイミング。(.)
作成機能です。そう
result = (f . g) x
渡された引数の結果渡す関数の構築と同じであるg
のではf
。
h = \x -> f (g x)
result = h x
(.)
作成する関数に渡すための引数がない場合に使用します。
($)
それを使用するタイミング($)
結合の優先順位が低い、右結合の適用関数です。したがって、最初にその右側にあるものを計算するだけです。したがって、
result = f $ g x
手続き的にこれと同じです(Haskellは遅延評価されるので重要です、f
最初に評価されます):
h = f
g_x = g x
result = h g_x
より簡潔に:
result = f (g x)
($)
上記の関数を結果に適用する前に、評価するすべての変数がある場合に使用します。
これは、各関数のソースを読むことで確認できます。
ここにソースがあり(.)
ます:
-- | 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)
そしてここにソースがあり($)
ます:
-- | 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
関数をすぐに評価する必要がない場合は、構成を使用します。たぶんあなたは、合成から生じる関数を別の関数に渡したいでしょう。
完全な評価のためにすべての引数を提供する場合は、applicationを使用してください。
したがって、この例では、意味的には
f $ g x
我々は持っている時にx
(というか、g
の引数)、および実行します。
f . g
そうしないとき。
...または、パイプラインを使用して.
and $
構造を回避できます。
third xs = xs |> tail |> tail |> head
それはヘルパー関数に追加した後です:
(|>) x y = y x
$
オペレータが実際に多くのF#のように動作します<|
、それはないより|>
:、通常、Haskellでは、あなたがこのような上記の関数を書きたいthird xs = head $ tail $ tail $ xs
か、おそらくのようなthird = head . tail . tail
、F#スタイルの構文でこのようなものになるだろうどの:let third = List.head << List.tail << List.tail
私のルールは簡単です(私も初心者です):
.
パラメータを渡す(関数を呼び出す)場合は使用しないでください。$
まだパラメーターがない場合は使用しない(関数を作成する)あれは
show $ head [1, 2]
しかし決して:
show . head [1, 2]
他のすべての答えはかなり良いです。しかし、ghcが$をどのように扱うかについての重要なユーザビリティの詳細があります。これは、ghc型チェッカーがより高いランク/数量化された型のインスタンス化を可能にすることです。$ id
たとえばの型を見ると、 引数自体が多態性関数である関数を取ることがわかります。そのような小さなことには、同等の動揺演算子では同じ柔軟性が与えられません。(これは実際に$!が同じ扱いに値するかどうか疑問に思います)