【问题标题】:Whats going on with the dot (.) in Haskell?Haskell 中的点 (.) 是怎么回事?
【发布时间】:2014-12-04 19:57:26
【问题描述】:

我无法理解函数组合运算符在 Haskell 中的工作原理。我在简单的情况下理解它,您只使用带有一个参数的函数,但我不明白当您在多个函数上使用它时会发生什么,每个函数都带有多个参数。

考虑这个函数:

f1 = (*) . (+) 2 $ 1

这个函数所做的只是它接受一个数字并将其乘以三,因此它与以下内容相同:

f2 = (*) $ (+) 2 1

f3 = (*) 3

我完全了解 f2 中发生了什么,但我不明白 f1 中发生了什么,这使得两者相同。 (.) 接受两个函数和一个值,每个函数只能接受一个参数,因此在这种情况下,必须同时部分应用 (*) 和 (+) 才能使事情正常工作。我不明白它们适用于什么以及如何通过函数链发送。

在 f2 中,(+) 首先应用于两个函数,产生一个部分应用于 (*) 的值,从而创建一个函数。在 f1 中,不可能将 (+) 分配给两个值,因为 (.) 需要一个函数作为输入,但它们是相同的。这个我不明白。

我希望您能理解我在理解方面遇到的问题。提前致谢!

【问题讨论】:

  • 加入俱乐部!有些人真的很擅长理解这些奇怪的函数组合。我们中的一些人没有那么多。从长远来看,我没有发现它是一个大障碍,所以我不会太担心这个......
  • 我其实不在乎那么多,我的教授在乎 ;)

标签: haskell function-composition


【解决方案1】:

(.) 只是 Haskell 中的另一个函数:

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

顺便说一句,fg 都可能期望多个参数。这意味着

(f . g) = (.) f g = (\x -> f (g x))         -- !!

这是关键:Haskell 方程定义左侧的任何(最后一个)参数都可以转到右侧,在那里它成为(第一个)lambda 参数。

所以,

f1 = (*) . (+) 2 $ 1
   = ((*) . ((+) 2)) 1            -- this is how it is really read
   = (\x -> (*) ( ((+) 2) x )) 1
   =        (*) ( ((+) 2) 1 )
   =        (*) (  (+) 2  1 )
   =        (*) 3

真的没有什么秘密。

【讨论】:

    【解决方案2】:

    查看运算符优先级可能会有所帮助:

    f1 = ((*) . ((+) 2)) $ 1
    

    换句话说,它是(2+)(*) - 即\x -> (*) ((2+) x) - 在1 上,使其成为(*) ((2+) 1),即(*) (2 + 1),即(*) 3,即@987654329 @。

    【讨论】:

      【解决方案3】:

      正如@minitech 指出的,首先要了解操作的顺序,即:

      f1 = ((*) . ((+) 2)) $ 1
      

      一般来说,对于二元运算fg,这个相等是真的:

      ((f . (g x)) $ y) z = f (g x y) z
      

      我们只是使用 compose 运算符的定义:(f . g) a = f (g a)

       (f . (g x)) $ y = (f . (g x)) y = f (g x y)
      

      等等:

       ((f . (g x)) $ y) z = f (g x y) z
      

      现在替换 f = (*)g = (+)x = 2y = 1

      【讨论】:

        【解决方案4】:

        在操作顺序方面,Haskell 通常是:括号、函数应用程序、运算符、特殊形式。所以fact 8 + case h (f 3 + g 1) of Nothing -> 2 会解析为:

        (+)
            fact
                8
            case
                h
                    (+)
                        f
                            3
                        g
                            1
                    Nothing
                    2
        

        fg 先发生。就多个函数而言,f g h 隐含地为 (f g) h——这被称为“左结合”定律,但我喜欢称其为“贪婪 nom 定律”:一个函数消耗(“noms”)任何东西单一的东西就在它面前,没有等待,很满意。 (嗯——它可能会产生一个接受另一个参数的函数,当然......)

        您的两个函数的解析树是:

        f1 = (*) . (+) 2 $ 1
        ($)
            (.)
                (*)
                (+)
                    2
            1
        
        f2 = (*) $ (+) 2 1
        ($)
            (*)
            (+)
                2
                1
        

        我们当然知道a $ b = a b$的定义,a . b = \x -> a (b x).的定义。从中我们看到了深刻的认同:

        (a . b) c == a $ b c
        

        $ 函数可以被认为是“懒惰的名义”,我是说a 将把表达式的整个其余部分作为它的参数。)

        换句话说,函数应用程序将. 转换为$。这实际上就是您理解上述表达式所需的全部内容:

        (*) . (+) 2 $ 1
        --> ((*) . (+) 2) 1  [use explicit () instead of the precedence of $]
        --> (*) $ (+) 2 1  [your expression]
        

        直观地说,上面的(*) . (2 +) 表示“将(2 +) 的输出输入(*) 的输入。如果将4 输入到结果中,首先发生的是它计算@ 987654342@,输出6,然后输入(*),生成(6 *)

        【讨论】:

          猜你喜欢
          • 2022-01-23
          • 2018-08-17
          • 2011-07-21
          • 1970-01-01
          • 1970-01-01
          • 2010-09-28
          • 1970-01-01
          • 2017-10-20
          • 2013-07-06
          相关资源
          最近更新 更多