Haskell中的点运算符:需要更多说明


86

我试图了解在此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

Answers:


138

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

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
小小的问题:第一个代码段实际上不是有效的Haskell。
SwiftsNamesake

2
@SwiftsNamesake对于那些不懂Haskell的人来说,您是说单等号在这里没有意义吗?(因此,代码段的格式应该为“ f (g x)= (f . g) x”?)还是其他格式?
user234461 '18

1
@ user234461是的,是的。你需要==,而不是如果你想有效的标准哈斯克尔。
SwiftsNamesake

上面的小片段只是金子。像其他答案一样,这里的答案是正确的,但是该片段只是直接在我的脑海中直接单击,因此无需阅读其余答案。
Tarick Welling

24

的。运算符组成功能。例如,

a . b

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

sumEuler = sum . (map euler) . mkList

与以下内容完全相同:

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

但希望更容易阅读。map euler周围有括号的原因是因为它使组成3个函数更加清晰:summap eulermkList - map euler是单个函数。


23

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类型类的实例。


11

的。运算符用于功能组合。就像数学一样,如果必须要函数f(x)和g(x)f。g变成f(g(x))。

map是一个内置函数,将函数应用于列表。通过将函数放在括号中,该函数将被视为参数。这个词是易变的。你应该查一下。

它的作用是接受带有两个参数的函数,并应用参数euler。(地图欧拉)对吗?结果是一个新函数,它仅接受一个参数。

总和。(地图欧拉)。mkList基本上是将所有内容组合在一起的一种理想方法。我必须说,我的Haskell有点生锈,但是也许您可以自己完成最后一个功能?


5

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

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

来源

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

因此(.)compose运算符

撰写

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

[f∘g] [x)

可以读为“由g组成的f”。

因此在Haskell中,可以写出f∘g或由g组成的f:

f . g

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

也就是说,由于(f f g)∘h等于f∘(g∘h),因此我们可以简单地写f∘g∘h。

回圈

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

sumEuler = sum . map_euler . mkList

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

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

4

点运算符将左侧的功能(sum)应用于右侧的功能的输出。在您的情况下,您要将多个函数链接在一起-您将的结果传递mkList(map euler),然后将的结果传递给sum该站点对其中几个概念进行了很好的介绍。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.