違いは何ですか 。(ドット)と$(ドル記号)?


回答:


1226

$オペレータは、括弧を回避するためです。その後に現れるものは、それ以前に来るものよりも優先されます。

たとえば、次のような行があるとします。

putStrLn (show (1 + 1))

これらの括弧を削除したい場合は、次の行も同じことを行います。

putStrLn (show $ 1 + 1)
putStrLn $ show (1 + 1)
putStrLn $ show $ 1 + 1

.演算子の主な目的は、括弧を回避することではなく、関数をチェーンすることです。右側に表示されるものの出力を左側に表示されるものの入力に関連付けることができます。これにより、通常、括弧が少なくなりますが、動作が異なります。

同じ例に戻る:

putStrLn (show (1 + 1))
  1. (1 + 1)入力がないため、.演算子と一緒に使用できません。
  2. showを取り、Intを返すことができますString
  3. putStrLnを取り、Stringを返すことができますIO ()

あなたはこのように連鎖showすることができますputStrLn

(putStrLn . show) (1 + 1)

括弧が多すぎて好みに合わない場合は、$演算子で括弧を削除します。

putStrLn . show $ 1 + 1

54
実際には、+も関数であるため、 `putStrLnのように、それを接頭辞にしてから作成することもできませんでした。公演 。(+)1 1 `はっきりしているわけではありませんが、つまり...できますよね?
CodexArcanum 2010年

4
@CodexArcanumこの例では、のようなものputStrLn . show . (+1) $ 1は同等です。ほとんどの(すべての?)中置演算子は関数です。
マイケルスティール

79
なぜ誰もがのような使用法について言及しないのか疑問に思いますmap ($3)$つまり、私は主に括弧を避けるためにも使用しますが、それだけではありません。
キュービック

43
map ($3)タイプの関数ですNum a => [(a->b)] -> [b]。数値を取る関数のリストを取得し、それらすべてに3を適用して結果を収集します。
キュービック

21
他の演算子で$を使用する場合は注意が必要です。「x + f(y + z)」は「x + f $ y + z」と同じではありません。後者は実際には「(x + f)(y + z)」を意味するためです(つまり、xとfの合計は関数として扱われます)。
ポールジョンソン

186

それらは異なるタイプと異なる定義を持っています:

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のチェーンでは、最後のものを除くすべてを次のように置き換えることができます。.


1
x関数の場合はどうなりますか?.最後に使用できますか?
リッチー2017

3
@richizy実際xにこのコンテキストで適用している場合は、はい-しかし、「最終」のは以外のものに適用されますx。を適用しない場合は、値であることにx変わりはありませんx
GS-モニカに謝罪2017年

124

また、これ($)関数型に特化した識別関数です。識別関数は次のようになります。

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)

お役に立てれば!


「型シグネチャに余分な括弧を意図的に追加したことに注意してください。」私は混乱しています...なぜこれをするのですか?
Mateen Ulhaq

3
@MateenUlhaq($)のタイプは(a-> b)-> a-> bであり、(a-> b)->(a-> b)と同じですが、余分な括弧はここに追加されます明快さ。
Rudi

2
ああ、たぶん。私はそれを2つの引数の関数と考えていました...しかし、カリー化のため、関数を返す関数とまったく同じです。
Mateen Ulhaq

78

($) 評価の順序を制御する括弧を追加せずに関数を一緒にチェーンすることができます:

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"

3
stackoverflowに組み合わせ関数がある場合、私は前の2つの説明とこの回答の例を組み合わせた回答を好みます。
Chris.Q、2014年

59

短くて甘いバージョン:

  • ($) 右側の引数である値に対して、左側の引数である関数を呼び出します。
  • (.) 左側の引数である関数を、右側の引数である関数に合成します。

29

便利なアプリケーションの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) ...。明らかに、後者のバリアントはほとんどの人にとって読みやすくなります。


14
ところで、$3スペースなしで使用しないことをお勧めします。テンプレートHaskellが有効な場合、これはスプライスとして解析され$ 3ますが、常にあなたが言ったことを意味します。一般に、Haskellには、特定の演算子をそのように処理するためのスペースがあると主張することで、構文のビットを「盗む」傾向があるようです。
GS-2010年

1
:括弧が働いたかを把握するために私にしばらく時間がかかったen.wikibooks.org/wiki/Haskell/...
Casebash

18

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

そうしないとき。


12

...または、パイプラインを使用して.and $構造を回避できます。

third xs = xs |> tail |> tail |> head

それはヘルパー関数に追加した後です:

(|>) x y = y x

2
はい、|>はF#パイプライン演算子です。
user1721780

6
ここで注意すべきことの一つは、Haskellのことである$オペレータが実際に多くのF#のように動作します<|、それはないより|>:、通常、Haskellでは、あなたがこのような上記の関数を書きたいthird xs = head $ tail $ tail $ xsか、おそらくのようなthird = head . tail . tail、F#スタイルの構文でこのようなものになるだろうどの:let third = List.head << List.tail << List.tail
エレクトリックコーヒー

1
HaskellをF#のように見せるヘルパー関数を追加する理由 -1
vikingsteve

9
反転したもの$がすでに利用可能で、& hackage.haskell.org / package / base
pat

11

あらゆるもの(あらゆる機能)についてさらに学ぶための素晴らしい方法は、すべてが機能であることを覚えておくことです!その一般的なマントラは役立ちますが、オペレーターなどの特定のケースでは、この小さなトリックを覚えておくと役立ちます。

:t (.)
(.) :: (b -> c) -> (a -> b) -> a -> c

そして

:t ($)
($) :: (a -> b) -> a -> b

:t自由に使用し、演算子を()


11

私のルールは簡単です(私も初心者です):

  • .パラメータを渡す(関数を呼び出す)場合は使用しないでください。
  • $まだパラメーターがない場合は使用しない(関数を作成する)

あれは

show $ head [1, 2]

しかし決して:

show . head [1, 2]

3
優れたヒューリスティックですが、より多くの例を使用できます
Zoey Hewll 2017

0

私はあなたがどこで使用するのか.、そして$物事を明確にするのに役立たないだろうという短い例を考えます。

double x = x * 2
triple x = x * 3
times6 = double . triple

:i times6
times6 :: Num c => c -> c

times6機能組成物から作成される関数です。


0

他のすべての答えはかなり良いです。しかし、ghcが$をどのように扱うかについての重要なユーザビリティの詳細があります。これは、ghc型チェッカーがより高いランク/数量化された型のインスタンス化を可能にすることです。$ idたとえばの型を見ると、 引数自体が多態性関数である関数を取ることがわかります。そのような小さなことには、同等の動揺演算子では同じ柔軟性が与えられません。(これは実際に$!が同じ扱いに値するかどうか疑問に思います)

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.