首页 文章

有什么区别 . (点)和$(美元符号)?

提问于
浏览
629

(.) 和美元符号 ($) 之间有什么区别?据我了解,它们都是不需要使用括号的语法糖 .

12 回答

  • 11

    $ 运算符用于避免括号 . 之后出现的任何内容都将优先于之前的任何内容 .

    例如,假设您有一行内容如下:

    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
    
  • 29

    它们有不同的类型和不同的定义:

    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
    

    换句话说,在 $ 的链中,除最后一个之外的所有部分都可以被 . 取代

  • 0

    另请注意, ($) 是专用于函数类型的标识函数 . 标识函数如下所示:

    id :: a -> a
    id x = x
    

    虽然 ($) 看起来像这样:

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

    请注意,我有意在类型签名中添加了额外的括号 .

    通常可以通过添加括号来消除 ($) 的使用(除非在一个部分中使用运算符) . 例如: f $ g x 变为 f (g x) .

    (.) 的使用通常稍微难以替换;它们通常需要lambda或引入显式函数参数 . 例如:

    f = g . h
    

    f x = (g . h) x
    

    f x = g (h x)
    

    希望这可以帮助!

  • 115

    ($) 允许将函数链接在一起而不添加括号来控制评估顺序:

    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"
    
  • 70

    简短而甜蜜的版本:

    • ($) 调用函数,该函数是其右手参数值的左侧参数 .

    • (.) 组成函数,它是函数的左手参数,它是右手参数 .

  • 58

    一个有用的应用程序,花了我一些时间从非常简短的描述中找出at learn you a haskell:从:

    f $ x = f x
    

    并且将包含中缀运算符的表达式的右侧括起来将其转换为前缀函数,可以将 ($ 3) (4+) 类似于 (++", world") "hello" .

    为什么有人会这样做?例如,对于函数列表 . 都:

    map (++", world") ["hello","goodbye"]`
    

    和:

    map ($ 3) [(4+),(3*)]
    

    map (\x -> x ++ ", world") ...map (\f -> f 3) ... 短 . 显然,后者的变体对大多数人来说更具可读性 .

  • 12

    ...或者您可以通过使用流水线来避免 .$ 构造:

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

    在您添加辅助函数之后:

    (|>) x y = y x
    
  • 11

    了解任何事物(任何功能)的好方法是记住一切都是功能!一般的口头禅有所帮助,但在特定情况下,如操作员,它有助于记住这个小技巧:

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

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

    请记住自由地使用 :t ,并将您的运算符包装在 () 中!

  • 1120

    我的规则很简单(我也是初学者):

    • 如果要传递参数(调用函数),请不要使用 .

    • 如果还没有参数,则不要使用 $ (撰写函数)

    那是

    show $ head [1, 2]
    

    但从不:

    show . head [1, 2]
    
  • 7

    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)
    

    我们可以通过阅读每个函数的源来看到这一点 .

    阅读来源

    这是source for (.)

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

    这是source($)

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

    当我们没有 .

  • 0

    我想一个简短的例子,你将使用 . 而不是 $ 将有助于澄清事情 .

    double x = x * 2
    triple x = x * 3
    times6 = double . triple
    
    :i times6
    times6 :: Num c => c -> c
    

    请注意, times6 是根据函数组合创建的函数 .

  • 172

    所有其他答案都很好 . 但是关于ghc如何处理$,有一个重要的可用性细节,ghc类型检查器允许更高级别/量化类型的instatiarion . 如果你看一下 $ id 的类型,你会发现它将采用一个函数,其参数本身就是一个多态函数 . 像这样的小事情与同等的沮丧操作员没有相同的灵活性 . (这实际上让我想知道$!是否值得同样的待遇)

相关问题