【问题标题】:Dot Operator in Haskell: need more explanationHaskell中的点运算符:需要更多解释
【发布时间】:2009-03-10 16:59:13
【问题描述】:

我试图了解点运算符在此 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

【问题讨论】:

    标签: haskell syntax function-composition


    【解决方案1】:

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

    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

    【讨论】:

    • 小问题:第一个代码 sn-p 实际上不是有效的 Haskell。
    • @SwiftsNamesake 对于我们这些不熟悉 Haskell 的人,你的意思是说单等号在这里没有意义吗? (所以 sn-p 的格式应该是“f (g x) = (f . g) x”?)还是别的什么?
    • @user234461 没错,是的。如果您想要有效的标准 Haskell,则需要 ==
    • 顶部的小sn-p简直就是金子。就像这里的其他答案是正确的一样,但是 sn-p 只是直接在我的脑海中直观地单击,这使得没有必要阅读其余的答案。
    【解决方案2】:

    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
    

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

    来自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,或者由 g 组成的 f,可以写成:

    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))
    

    【讨论】:

      【解决方案3】:

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

      a . b
      

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

      sumEuler = sum . (map euler) . mkList
      

      完全一样:

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

      但希望更容易阅读。 map euler 周围有括号的原因是因为它更清楚地表明有 3 个函数正在组合:summap eulermkList - map euler 是一个单独的函数。

      【讨论】:

        【解决方案4】:

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

        【讨论】:

        • sum :: Num a => [a ] -> a 这行是从哪里来的?
        【解决方案5】:

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

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

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

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

        【讨论】:

          【解决方案6】:

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

          【讨论】:

            猜你喜欢
            • 2016-11-22
            • 1970-01-01
            • 2017-11-09
            • 1970-01-01
            • 2014-09-08
            • 2019-01-28
            • 2017-05-31
            • 2011-02-19
            • 1970-01-01
            相关资源
            最近更新 更多