ドット(.)
とドル記号($)
の違いは何ですか?私が理解しているように、それらはどちらも括弧を使用する必要がないためのシンタックスシュガーです。
$
演算子は括弧を避けるためのものです。それ以降に現れるものはすべて、前に来るものよりも優先されます。
たとえば、次のような行があるとしましょう。
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
それらは異なる型と異なる定義を持ちます。
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
言い換えれば、$
のチェーンの中で、最後のものを除くすべてを.
で置き換えることができます。
($)
は関数型に特化した恒等関数です。識別関数は次のようになります。
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"
一度だけ三度目を使うのなら、ラムダを使うことでそれを命名することを避けることができます。
Prelude> map (\x -> head $ tail $ tail x) ["asdf", "qwer", "1234"]
"de3"
最後に、合成によってラムダを避けることができます。
Prelude> map (head . tail . tail) ["asdf", "qwer", "1234"]
"de3"
短くて甘いバージョン:
($)
は、その右側の引数である値に対して、その左側の引数である関数を呼び出します。(.)
は、その右側の引数である関数の上に、その左側の引数である関数を構成します。便利で、非常に短い説明 からハスケル を理解するのに時間がかかったアプリケーション。
f $ x = f x
そして、中置演算子を含む式の右辺を括弧でくくると、それは前置関数に変換されます。($ 3) (4+)
と同様に(++", world") "hello"
を書くことができます。
なぜだれでもこれをしますか。例えば、関数のリスト用です。両方:
map (++", world") ["hello","goodbye"]`
そして:
map ($ 3) [(4+),(3*)]
map (\x -> x ++ ", world") ...
またはmap (\f -> f 3) ...
より短いです。明らかに、後者の変種はほとんどの人にとって読みやすくなるでしょう。
...または、パイプライン処理を使用して、.
および$
構造を回避することもできます。
third xs = xs |> tail |> tail |> head
これは、ヘルパー関数に追加した後です。
(|>) x y = y x
私のルールは簡単です(私も初心者です):
.
を使用しないでください。$
を使用しません(関数を作成します)あれは
show $ head [1, 2]
しかし決して:
show . head [1, 2]
何か(どんな機能でも)についてもっと知るための素晴らしい方法は、すべてが機能であることを覚えておくことです!その一般的な考え方は助けになりますが、演算子のような特定の場合には、この小さなトリックを思い出すのに役立ちます:
:t (.)
(.) :: (b -> c) -> (a -> b) -> a -> c
そして
:t ($)
($) :: (a -> b) -> a -> b
:t
を自由に使って、演算子を()
で囲むのを忘れないでください。
Haskell:
.
(ドット)と$
(ドル記号)の違いドット
(.)
とドル記号($)
の違いは何ですか?私が理解しているように、それらはどちらも括弧を使用する必要がないためのシンタックスシュガーです。
NOT括弧を使用する必要がないという意味での構文シュガーです。これらは関数です。
(.)
、そしていつ使用するか。(.)
は作成機能です。そう
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
そうでないとき。
他のすべての答えはかなり良いです。しかし、ghcがどのように$を扱うかについての重要なユーザビリティの詳細があります、それはghc型チェッカーがより高いランク/数量化された型でのインスタンス化を可能にするということです。たとえば$ id
の型を見ると、引数がそれ自体が多相関数である関数を取ることになるでしょう。そのような小さなことには、同等の動揺演算子で同じ柔軟性が与えられるわけではありません。 (これは実際には$!が同じ扱いに値するかどうか疑問に思います)
.
ではなく$
を使用する場所の簡単な例が、説明を明確にするのに役立つと思います。
double x = x * 2
triple x = x * 3
times6 = double . triple
:i times6
times6 :: Num c => c -> c
times6
は、関数合成から作成された関数です。