95

我试图了解点运算符在此 Haskell 代码中的作用:

sumEuler = sum . (map euler) . mkList

整个源代码如下。

我的理解

点运算符将两个函数sum以及结果map 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
4

6 回答 6

149

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

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

于 2009-03-10T17:07:51.480 回答
32

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

a . b

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

sumEuler = sum . (map euler) . mkList

与以下内容完全相同:

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

但希望更容易阅读。map euler周围有括号的原因是因为它更清楚地说明了由 3 个函数组成:summap euler和mkList - map euler是一个函数。

于 2009-03-10T17:10:05.343 回答
27

sum是 Haskell Prelude 中的一个函数,而不是sumEuler. 它有类型

Num a => [a] -> a

函数组合运算符. 具有类型

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

所以我们有

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

请注意,这Int确实是类型类的一个实例Num

于 2009-03-10T18:15:24.157 回答
12

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

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

所做的是它接受一个带有两个参数的函数,它应用参数欧拉。(地图欧拉)对吧?结果是一个新函数,它只接受一个参数。

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

于 2009-03-10T17:08:08.377 回答
7

Haskell 中的点运算符

我试图了解点运算符在此 Haskell 代码中的作用:

sumEuler = sum . (map euler) . mkList

简短的回答

没有点的等效代码,就是

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

或没有 lambda

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

因为点 (.) 表示函数组合。

更长的答案

euler首先,让我们简化to的部分应用map

map_euler = map euler
sumEuler = sum . map_euler . mkList

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

来源

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

(.)就是compose 运算符

撰写

在数学中,我们可以将函数 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),我们可以简单地写成 f ∘ g ∘ h。

盘旋回来

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

sumEuler = sum . map_euler . mkList

只是意味着这sumEuler是这些功能的未应用组合:

sumEuler = \x -> sum (map_euler (mkList x))
于 2018-01-28T04:06:22.467 回答
4

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

于 2009-03-10T17:09:09.217 回答