(.)
ドットとドル記号の違いは何($)
ですか?
私が理解しているように、これらは両方とも括弧を使用する必要がないための構文糖衣です。
(.)
ドットとドル記号の違いは何($)
ですか?
私が理解しているように、これらは両方とも括弧を使用する必要がないための構文糖衣です。
$
演算子は括弧を避けるためのものです。後に表示されるものはすべて、前に表示されるものよりも優先されます。
たとえば、次のような行があるとします。
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
言い換えれば、$
sのチェーンでは、最後のものを除くすべてを次のように置き換えることができます。.
また、関数型に特化した恒等関数($)
であることに注意してください。恒等関数は次のようになります。
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"
短くて甘いバージョン:
($)
右側の引数である値に対して、左側の引数である関数を呼び出します。(.)
左側の引数である関数を、右側の引数である関数で構成します。Learn You a Haskellの非常に短い説明から理解するのに便利で時間がかかったアプリケーションの1つ:以来
f $ x = f x
中置演算子を含む式の右辺を括弧で囲むと、それが接頭関数に変換され、に($ 3) (4 +)
類似したものを書くことができます(++ ", world") "hello"
。
なぜ誰かがこれをするのでしょうか?たとえば、関数のリストの場合。両方:
map (++ ", world") ["hello", "goodbye"]
map ($ 3) [(4 +), (3 *)]
より短い
map (\x -> x ++ ", world") ["hello", "goodbye"]
map (\f -> f 3) [(4 +), (3 *)]
明らかに、後者の変種はほとんどの人にとってより読みやすいでしょう。
.
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
関数をすぐに評価する必要がない場合は、compositionを使用します。合成の結果である関数を別の関数に渡したい場合があります。
完全な評価のためにすべての引数を提供する場合は、アプリケーションを使用してください。
したがって、この例では、意味的には次のようにすることが望ましいでしょう。
f $ g x
私たちがx
(というよりは、g
の引数を)持っているとき、そして次のことを行います:
f . g
そうしないとき。
...または、パイプライン.
を使用してと$
構造を回避できます:
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
でください、そしてあなたのオペレーターを()
!
他のすべての答えはかなり良いです。しかし、ghcが$をどのように扱うかについての重要なユーザビリティの詳細があります。それは、ghcタイプチェッカーがより高いランク/定量化されたタイプでのインスタンス化を可能にすることです。たとえば、のタイプを見ると、 $ id
引数自体がポリモーフィック関数である関数を取得することがわかります。そのような小さなことは、同等の動揺演算子で同じ柔軟性を与えられていません。(これは実際に$!が同じ扱いに値するかどうか疑問に思います)
$に関する最も重要な部分は、演算子の優先順位が最も低いことです。
情報を入力すると、次のように表示されます。
λ> :info ($)
($) :: (a -> b) -> a -> b
-- Defined in ‘GHC.Base’
infixr 0 $
これは、優先順位が可能な限り低い右結合性を持つ中置演算子であることを示しています。通常の関数適用は左結合であり、最高の優先順位があります(10)。したがって、$は逆です。
したがって、通常の関数アプリケーションまたは()の使用が機能しない場合に使用します。
したがって、たとえば、これは機能します。
λ> head . sort $ "example"
λ> e
しかし、これはしません:
λ> head . sort "example"
なぜなら 。sortよりも優先順位が低く、(sort "example")のタイプは[Char]です。
λ> :type (sort "example")
(sort "example") :: [Char]
だが 。2つの関数が必要ですが、sortと。の演算の順序が原因で、これを行うための簡単な方法はありません。
.
使用する場所と使用しない場所の簡単な例は、$
物事を明確にするのに役立つと思います。
double x = x * 2
triple x = x * 3
times6 = double . triple
:i times6
times6 :: Num c => c -> c
times6
これは、関数の合成から作成される関数であることに注意してください。