点(.)和美元符号($)的区别是什么?
根据我的理解,它们都是不需要使用括号的语法糖。
点(.)和美元符号($)的区别是什么?
根据我的理解,它们都是不需要使用括号的语法糖。
当前回答
我的规则很简单(我也是初学者):
不要使用。如果要传递参数(调用函数),和 如果没有参数,不要使用$(合成一个函数)
这是
show $ head [1, 2]
但从来没有:
show . head [1, 2]
其他回答
它们有不同的类型和不同的定义:
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链中,除了最后一个,其他的都可以被。
$操作符用于避免括号。在它之后出现的任何东西都会优先于在它之前出现的任何东西。
例如,假设你有一行是这样写的:
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()。
你可以这样链式显示strln:
(putStrLn . show) (1 + 1)
如果括号太多,可以用$操作符去掉:
putStrLn . show $ 1 + 1
其他答案都很好。但是关于ghc如何处理$有一个重要的可用性细节,ghc类型检查器允许用更高等级/量化的类型进行初始化。例如,如果你看$ id类型你会发现它将接受一个参数本身是多态函数的函数。类似这样的小事情在等效的打乱操作符中没有相同的灵活性。(这实际上让我怀疑$!是否值得同样的待遇)
一个很有用的应用程序,我花了一些时间从Learn You a Haskell非常简短的描述中弄清楚:Since
f $ x = f x
在包含中缀运算符的表达式的右边加圆括号将其转换为前缀函数,可以将($ 3)(4 +)写成类似于(++ ",world")“你好”。
为什么会有人这么做?例如,对于函数列表。两个:
map (++ ", world") ["hello", "goodbye"]
map ($ 3) [(4 +), (3 *)]
比
map (\x -> x ++ ", world") ["hello", "goodbye"]
map (\f -> f 3) [(4 +), (3 *)]
显然,后一种变体对大多数人来说更具可读性。
($)允许函数链接在一起,而不需要添加括号来控制求值顺序:
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"
如果我们只使用一次third,我们可以通过使用lambda来避免命名:
Prelude> map (\x -> head $ tail $ tail x) ["asdf", "qwer", "1234"]
"de3"
最后,复合让我们避免lambda:
Prelude> map (head . tail . tail) ["asdf", "qwer", "1234"]
"de3"