簡體   English   中英

之間有什么區別。 (點)和 $(美元符號)?

[英]What is the difference between . (dot) and $ (dollar sign)?

(.)和美元符號($)之間有什么區別?

據我了解,它們都是不需要使用括號的語法糖。

$運算符用於避免括號。 在它之后出現的任何東西都將優先於它之前出現的任何東西。

例如,假設您有一行內容如下:

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 ()

您可以像這樣將showputStrLn

(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

($)旨在替換普通函數應用程序,但優先級不同以幫助避免括號。 (.)用於將兩個函數組合在一起形成一個新函數。

在某些情況下,它們是可以互換的,但通常情況並非如此。 它們所在的典型示例是:

f $ g $ h $ x

==>

f . g . h $ x

換句話說,在$ s 鏈中,除最后一個之外的所有內容都可以替換為.

另請注意, ($)專門用於函數類型的標識函數 身份函數如下所示:

id :: a -> a
id x = x

雖然($)看起來像這樣:

($) :: (a -> b) -> (a -> b)
($) = id

請注意,我有意在類型簽名中添加了額外的括號。

通常可以通過添加括號來消除($)使用(除非在節中使用了運算符)。 例如: f $ gx變成f (gx)

(.)的使用通常更難替換; 它們通常需要 lambda 或引入顯式函數參數。 例如:

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"

如果我們只使用第三次,我們可以避免使用 lambda 來命名它:

Prelude> map (\x -> head $ tail $ tail x) ["asdf", "qwer", "1234"]
"de3"

最后,組合讓我們避免了 lambda:

Prelude> map (head . tail . tail) ["asdf", "qwer", "1234"]
"de3"

簡短而甜蜜的版本:

  • ($)在作為其右側參數的值上調用作為其左側參數的函數。
  • (.)在作為其右側參數的函數上組合作為其左側參數的函數。

一個有用的應用程序,我花了一些時間從Learn You a Haskell的非常簡短的描述找出來:因為

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: 之間的區別. (點)和$ (美元符號)

(.)和美元符號($)之間有什么區別? 據我了解,它們都是不需要使用括號的語法糖。

他們不是為沒有需要使用括號語法糖-他們是功能- infixed,因此,我們可以稱他們為運營商。

Compose、 (.)以及何時使用它。

(.)是組合函數。 所以

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

結論

當您不需要立即評估函數時使用組合。 也許您想將組合產生的函數傳遞給另一個函數。

當您提供所有參數以進行全面評估時,請使用應用程序。

所以對於我們的例子,在語義上最好這樣做

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 ,並將您的運算符包裹在()

$ 最重要的部分是它具有最低的運算符優先級。

如果你輸入信息,你會看到:

λ> :info ($)
($) :: (a -> b) -> a -> b
    -- Defined in ‘GHC.Base’
infixr 0 $

這告訴我們它是一個具有最低優先級的具有右結合性的中綴運算符。 普通函數應用程序是左關聯的並且具有最高優先級 (10)。 所以 $ 是相反的。

那么我們在普通函數應用程序或 using () 不起作用的地方使用它。

因此,例如,這有效:

λ> head . sort $ "example"
λ> e

但這不會:

λ> head . sort "example"

因為 。 具有比 sort 低的優先級,並且 (sort "example") 的類型是 [Char]

λ> :type (sort "example")
(sort "example") :: [Char]

但 。 需要兩個函數,由於 sort 和 的操作順序,沒有一個很好的簡短方法可以做到這一點。

我想一個簡短的例子來說明你會在哪里使用. 而不是$將有助於澄清事情。

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

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

請注意, times6是從函數組合創建的函數。

所有其他答案都很好。 但是有一個關於 ghc 如何處理 $ 的重要可用性細節,即 ghc 類型檢查器允許使用更高等級/量化類型的 instatiarion。 例如,如果您查看$ id的類型,您會發現它將采用一個函數,其參數本身就是一個多態函數。 類似的小事情沒有與等效的翻轉運算符相同的靈活性。 (這實際上讓我想知道 $! 是否應該得到同樣的待遇)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM