Haskell中的点运算符:需要更多解释
-
08-07-2019 - |
题
我试图理解点运算符在这个Haskell代码中的作用:
sumEuler = sum . (map euler) . mkList
整个源代码如下。
我的理解
点运算符将两个函数sum
和map euler
的结果以及mkList
的结果作为输入。
但是,(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
解决方案
简单地说,.
是函数组合,就像在数学中一样:
f (g x) = (f . g) x
在您的情况下,您正在创建一个新功能,sumEuler
也可以像这样定义:
sumEuler x = sum (map euler (mkList x))
示例中的样式称为<!> quot; point-free <!> quot; style - 省略函数的参数。在许多情况下,这使得代码更清晰。 (第一次看到它时可能很难理解,但是你会在一段时间后习惯它。这是一种常见的Haskell习语。)
如果您仍然感到困惑,可能有助于将f
与UNIX管道相关联。如果g
的输出变为h
的输入,其输出变为f < x | g | h
的输入,则在命令行上写入|
,如h . g . f $ x
。在Haskell中,map (\x -> x * 2 + 10) [1..10]
的工作方式类似于UNIX (+10) . (*2) <$> [1..10]
,但是<!>“;向后<!>; - (+10) . (*2) $ 10
。我发现这种符号在处理列表时非常有用。而不是像<=>那样的一些笨重的构造,你可以写<=>。 (并且,如果您只想将该函数应用于单个值;它是<=>。一致!)
Haskell wiki有一篇很好的文章,其中包含更多细节: http://www.haskell.org/ haskellwiki / Pointfree
其他提示
。运算符组成函数。例如,
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 是一个单一的功能。
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
请注意Int
是Num
的实例。
。运算符用于函数组合。就像数学一样,如果你需要函数f(x)和g(x)f。 g变为f(g(x))。
map是一个内置函数,它将函数应用于列表。通过将函数放在括号中,函数被视为参数。一个术语是 currying 。你应该看一下。
它有两个参数需要一个函数,它应用参数euler。 (地图欧拉)对吗?结果是一个新函数,它只需要一个参数。
总结。 (地图euler)。 mkList基本上是将所有这些放在一起的奇特方式。我必须说,我的Haskell有点生疏,但也许你可以把最后一个功能放在一起吗?点运算符将左侧的函数(sum
)应用于右侧函数的输出。在您的情况下,您将多个函数链接在一起 - 您将mkList
的结果传递给(map euler)
,然后将结果传递给<=>。
本网站对几个概念有很好的介绍。
Haskell中的点运算符
我试图理解点运算符在这个Haskell代码中的作用:
sumEuler = sum . (map euler) . mkList
简短回答
没有点的等效代码,只是
sumEuler = \x -> sum ((map euler) (mkList x))
或没有lambda
sumEuler x = sum ((map euler) (mkList x))
因为点(。)表示功能组成。
更长的答案
首先,让我们简化部分应用euler
到map
:
map_euler = map euler
sumEuler = sum . map_euler . mkList
现在我们只是有点。这些点表示什么?
来自来源:
(.) :: (b -> c) -> (a -> b) -> a -> c (.) f g = \x -> f (g x)
因此(.)
是撰写运营商。
撰写
在数学中,我们可以写出函数f(x)和g(x)的组合,即f(g(x)),作为
(f <!>#8728; g)(x)
可以读取<!>“; f由g <!>组成;
所以在Haskell中,f <!>#8728; g,或由g组成的f,可以写成:
f . g
组合是关联的,这意味着用组合运算符写的f(g(h(x)))可以省略括号而没有任何歧义。
即,因为(f <!>#8728; g)<!>#8728; h相当于f <!>#8728; (g <!>#8728; h),我们可以简单地写f <!>#8728; g <!>#8728;小时。
盘旋回来
回到我们之前的简化,这个:
sumEuler = sum . map_euler . mkList
只是意味着sumEuler
是这些函数的未应用组合:
sumEuler = \x -> sum (map_euler (mkList x))