Haskell中的点运算符:需要更多解释

时间:2009-03-10 16:59:13

标签: haskell syntax function-composition

我试图理解点运算符在这个Haskell代码中的作用:

sumEuler = sum . (map euler) . mkList

整个源代码如下。

我的理解

点运算符将两个函数summap euler的结果以及mkList的结果作为输入。

但是,sum不是函数,它是函数的参数,对吧?那么这里发生了什么?

另外,(map euler)在做什么?

代码

mkList :: Int -> [Int]
mkList n = [1..n-1]

euler :: Int -> Int
euler n = length (filter (relprime n) (mkList n))

sumEuler :: Int -> Int
sumEuler = sum . (map euler) . mkList

6 个答案:

答案 0 :(得分:122)

简单地说,.是函数组合,就像在数学中一样:

f (g x) = (f . g) x

在您的情况下,您正在创建一个新功能,sumEuler也可以像这样定义:

sumEuler x = sum (map euler (mkList x))

示例中的样式称为“无点”样式 - 省略了函数的参数。在许多情况下,这使得代码更清晰。 (第一次看到它时可能很难理解,但过了一段时间你会习惯它。这是一种常见的Haskell习语。)

如果您仍然感到困惑,将.与UNIX管道相关联可能会有所帮助。如果f的输出变为g的输入,其输出变为h的输入,则您可以在命令行中将其写为f < x | g | h。在Haskell中,.的工作方式类似于UNIX |,但“向后” - h . g . f $ x。我发现这种符号在处理列表时非常有用。你可以写map (\x -> x * 2 + 10) [1..10]而不是像(+10) . (*2) <$> [1..10]那样笨拙的构造。 (并且,如果您只想将该函数应用于单个值;它是(+10) . (*2) $ 10。一致!)

Haskell wiki有一篇很好的文章,其中包含更多细节:http://www.haskell.org/haskellwiki/Pointfree

答案 1 :(得分:22)

。运算符组成函数。例如,

a . b

其中 a b 是函数是一个新的函数,它在其参数上运行b,然后在这些结果上运行。你的代码

sumEuler = sum . (map euler) . mkList

与:

完全相同
sumEuler myArgument = sum (map euler (mkList myArgument))

但希望更容易阅读。围绕 map euler 出现问题的原因是因为它更清楚地表明有3个函数被组成: sum map euler mkList - map euler 是一个单一的功能。

答案 2 :(得分:21)

sum是Haskell Prelude中的函数,而不是sumEuler的参数。它的类型为

Num a => [a] -> a

函数组合运算符.的类型为

(b -> c) -> (a -> b) -> a -> c

所以我们有

sum                        :: Num a => [a] -> a
map                        :: (a -> b) -> [a] -> [b]
euler                      :: Int -> Int
mkList                     :: Int -> [Int]
(map euler)                :: [Int] -> [Int]
(map euler) . mkList       :: Int -> [Int]
sum . (map euler) . mkList :: Int -> Int

请注意,IntNum的实例。

答案 3 :(得分:11)

。运算符用于函数组合。就像数学一样,如果你需要函数f(x)和g(x)f。 g变为f(g(x))。

map是一个内置函数,它将函数应用于列表。通过将函数放在括号中,函数被视为参数。这个术语是currying。你应该看一下。

它有两个参数需要一个函数,它应用参数euler。 (地图欧拉)对吗?结果是一个新函数,它只需要一个参数。

总结。 (地图euler)。 mkList基本上是将所有这些放在一起的奇特方式。我必须说,我的Haskell有点生疏,但也许你可以把最后一个功能放在一起吗?

答案 4 :(得分:4)

点运算符将左侧的函数(sum)应用于右侧函数的输出。在您的情况下,您将多个函数链接在一起 - 您将mkList的结果传递给(map euler),然后将结果传递给sumThis site对几个概念有很好的介绍。

答案 5 :(得分:1)

  

Haskell中的点运算符

     

我试图理解点运算符在这个Haskell代码中的作用:

sumEuler = sum . (map euler) . mkList

简短回答

没有点的等效代码,只是

sumEuler = \x -> sum ((map euler) (mkList x))

或没有lambda

sumEuler x = sum ((map euler) (mkList x))

因为点(。)表示功能组成。

更长的答案

首先,让我们简化eulermap的部分应用:

map_euler = map euler
sumEuler = sum . map_euler . mkList

现在我们只是有点。这些点表示什么?

来自the source

(.)    :: (b -> c) -> (a -> b) -> a -> c
(.) f g = \x -> f (g x)

因此(.)compose operator

撰写

在数学中,我们可以写出函数f(x)和g(x)的组合,即f(g(x)),作为

  

(f g)(x)

可以读作“f由g组成”。

所以在Haskell中,f g或f由g组成,可以写成:

f . g

组合是关联的,这意味着用组合运算符写的f(g(h(x)))可以省略括号而没有任何歧义。

也就是说,因为(f∘g)∘h相当于f∘(g∘h),我们可以简单地写ffg∘h。

盘旋回来

回到我们之前的简化,这个:

sumEuler = sum . map_euler . mkList

只是意味着sumEuler是这些函数的未应用组合:

sumEuler = \x -> sum (map_euler (mkList x))