在52 {40 this讲话中讨论了以下幻灯片。
我不太明白这个定义。
为什么定义中需要x
?
为什么不需要输出?
例如,为什么定义不是(f . g) x -> y = f (g x) -> y
或类似的东西?
如果我将其视为重写规则,是否更容易理解此定义?每当表达式求值程序遇到左侧的模式时,它应该将其重写到右侧吗?
重写规则解释是理解这个定义的唯一正确方法吗? (这种解释是这个定义对我有意义的唯一方法,但我甚至不确定这是否是解释这个定义的正确方法。)我不得不承认我是一个初学者Haskell中。
编辑:
或者这个定义只是lambda表达式的糖?
或者lambda表达式是重写规则的糖?
答案 0 :(得分:7)
作文可以用几种等效的方式书写。
(.) = \f g -> \x -> f (g x)
(.) = \f g x -> f (g x)
f . g = \x -> f (g x)
最后一个例子说"两个函数的组合给出了一个函数,这样......"
更多等效表达式:
(.) f g = \x -> f (g x)
(.) f g x = f (g x)
(f . g) x = f (g x)
答案 1 :(得分:4)
也许中缀符号让你感到困惑?让我们看一下编写该定义的另一种方式:
(.) :: (b -> c) -> (a -> b) -> a -> c
(.) f g x = f (g x) -- definition 1
因此,我们可以将(.)
视为一个函数,它接受三个参数f
(一个函数),g
(另一个函数)和x
(一个值) )。然后它返回f ( g x)
。要调用此函数,我们可以编写如下表达式:
(.) head tail "test"
将返回'e'
。但是,名称以某些特殊字符开头的函数(如.
)可以使用 infix 样式,如下所示:
(head . tail) "test"
现在,另一种定义.
的方法是这样的:
(f . g) x = f (g x) -- definition 2
这与"定义1"相同,但符号可能看起来有点奇怪,功能" name" .
出现在第一个参数之后!但这只是中缀符号的另一个例子。
现在,让我们看一下Prelude
中实际定义此函数的方式:
(.) :: (b -> c) -> (a -> b) -> a -> c
(.) f g = \x -> f (g x)
这相当于定义1和2,但使用了您可能不熟悉的语法, lambda表示法。右侧引入了一个带有单个参数x
的未命名函数,并将该函数定义为f (g x)
。所以整个事情说明函数(.) f g
被定义为未命名的函数!你可能想知道为什么有人会以这种奇怪的方式写这个。当你使用中缀符号和lambdas一段时间时,它会更有意义。
答案 2 :(得分:2)
->
,即说明函数作为参数使用的类型以及函数的结果类型。说明:
f :: ...
类似于“功能f
类型...
(a -> b)
表示类型“带有类型a
的参数并返回类型为b
的参数的函数”(a -> b) -> (b->c) -> (a->c)
表示“一个函数,它接受a->b
类型的函数和类型b->c
的函数,并返回类型a->c
的函数”(这是一个简化的配方。请参阅下面的注释)第二行是f.g
的定义。它就像在数学中定义函数一样。在那里,您可以通过说明任何给定参数h
h(x)
的结果来定义函数x
(例如,您可以编写h(x)=x²
)。您已阅读
(f . g) x = f (g x)
作为
(f . g)(x) := f(g(x))
应理解为:“任何给定f . g
的函数x
的结果应为f(g(x))
”
结论: ->
就像数学中的箭头一样,您可能会在f : R -> R
和=
这样的术语中看到:=
(f(x):=x²
表示在数学中,f(x)
被定义为x²
)
注意: (a -> b) -> (b->c) -> (a->c)
的实际类型(如@Ben Voigt所述):“函数采用类型a-> b的函数并返回映射函数类型b-> c的函数在a-> c“类型的ta函数上。 @jhegedus:如果您需要解释,请在评论中注明。
答案 3 :(得分:2)
这不是(。)的源代码,虽然它很接近。这是实际来源:
-- | 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)
(f.g)是一个函数。源使用lambda形式作为该函数。 lambda表单必须为其每个参数提供本地绑定。在这种情况下,只有一个参数,它在本地绑定到名称“x”。这就是为什么必须提到'x'(类型'a')的原因。
由于它标记为INLINE,它将在优化器传递期间有效地重写代码。 (IIRC,这是在desugaring(转换为Core)之后和转换为STG之前。)
Lambda不是糖,它们是基础。让/什么是lambdas的糖。
lambdas的函数定义几乎糖,但优化器(在GHC中最少)使用定义中的arity来确定何时/如何内联函数。类型“(b - > c) - >(a - > b) - > a - > c”可以被认为是从0到3的任何arity,并且可以用任何这些arities定义
不必要的括号可用于强烈暗示你想要使用的arity,尽管幻灯片从我见过的惯例向后倾斜。例如,在“a - > c”周围添加括号以得到“(b - > c) - >(a - > b) - >(a - > c)”;该类型通常被认为是二进制函数类型。幻灯片使用该类型,但随后使用三元定义。