点(.)
和美元符号($)
什么区别?
据我了解,它们都是不需要使用括号的语法糖。
#1楼
$
运算符用于避免括号。 之后出现的所有内容将优先于之前出现的所有内容。
例如,假设您有一行显示:
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
#2楼
...或者您可以避免使用.
和$
构造使用管道 :
third xs = xs |> tail |> tail |> head
那是在您添加了辅助函数之后:
(|>) x y = y x
#3楼
我的规则很简单(我也是初学者):
- 不要使用
.
如果要传递参数(调用函数),并且 - 如果没有参数,不要使用
$
(组成一个函数)
那是
show $ head [1, 2]
但永远不会:
show . head [1, 2]
#4楼
($)
允许将函数链接在一起,而无需添加括号来控制评估顺序:
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"
#5楼
一个有用的应用程序让我花了一些时间从简短的说明中了解到学习haskell :
f $ x = f x
并用括号将包含infix运算符的表达式的右侧转换为前缀函数,可以编写($ 3) (4+)
类似于(++", world") "hello"
。
为什么有人会这样做? 例如,用于功能列表。 都:
map (++", world") ["hello","goodbye"]`
和:
map ($ 3) [(4+),(3*)]
比map (\\x -> x ++ ", world") ...
或map (\\f -> f 3) ...
短。 显然,对于大多数人而言,后一种变体将更具可读性。
#6楼
了解任何事物(任何功能)的一种好方法是记住一切都是一个功能! 一般的口头禅可以帮助您,但是在像运算符这样的特定情况下,记住这一小技巧将有帮助:
:t (.)
(.) :: (b -> c) -> (a -> b) -> a -> c
和
:t ($)
($) :: (a -> b) -> a -> b
只要记住要使用:t
自由,并将运算符包含在()
!
#7楼
我认为您将在哪里使用一个简短示例.
而不是$
可以帮助澄清问题。
double x = x * 2
triple x = x * 3
times6 = double . triple
:i times6
times6 :: Num c => c -> c
请注意, times6
是从函数组合创建的函数。
#8楼
Haskell:之间的区别
.
(点)和$
(美元符号)点
(.)
和美元符号($)
什么区别? 据我了解,它们都是不需要使用括号的语法糖。
他们不是语法糖不需要使用括号-它们是功能- infixed,因此,我们可以称他们为运营商。
撰写(.)
以及何时使用它。
(.)
是撰写功能。 所以
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
当我们不这样做时。
#9楼
所有其他答案都很好。 但是,有一个重要的可用性细节涉及ghc如何处理$,ghc类型检查器允许具有较高等级/量化类型的错误。 例如,如果您查看$ id
的类型,您会发现它将使用一个函数,其参数本身就是一个多态函数。 像这样的小事情在使用等效的翻转运算符时不会具有相同的灵活性。 (这实际上使我怀疑$!是否应得到相同的待遇)
#10楼
它们具有不同的类型和不同的定义:
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
换句话说,在$
的链中,除最后一个以外的所有链都可以替换为.
#11楼
另请注意, ($)
是专用于函数类型的标识函数 。 身份函数如下所示:
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)
希望这可以帮助!
#12楼
简短而甜美的版本:
-
($)
调用函数,该函数是它的左手参数,值是它的右手参数。 -
(.)
在作为右手参数的函数上组成作为左手参数的函数。