【问题标题】:How can I understand "(.) . (.)"?我如何理解“(.) . (.)”?
【发布时间】:2013-02-22 17:23:02
【问题描述】:

我相信我理解 fmap . fmap 的 Functors,但在函数方面,这几个月来一直让我头疼。

我看到您可以将(.) 的定义应用于(.) . (.),但我忘记了如何做到这一点。
当我自己尝试时,结果总是错误:

(.) f g = \x -> f (g x)
(.) (.) (.) = \x -> (.) ((.) x)
\x f -> (.) ((.) x) f
\x f y  -> (((.)(f y)) x)
\x f y g-> (((.)(f y) g) x)
\x f y g-> ((f (g y)) x)
\x f y g-> ((f (g y)) x):: t2 -> (t1 -> t2 -> t) -> t3 -> (t3 -> t1) -> t

如果“仅应用定义”是唯一的方法,那么有人怎么想出(.) . (.)
我必须缺少一些更深层次的理解或直觉。

【问题讨论】:

  • @Will Ness 你是对的.. :P
  • @WillNess:戴眼镜的猫头鹰组合器
  • @amindfv 就是这样..!!
  • 如果你理解fmap . fmap,那么,只需将fmaps 都专门化为(->) e 函子,你就完成了。 ;-)
  • @DanielWagner 是的,但我也缺少其他东西,我不确定是什么,但各种答案都有帮助。可悲的是我只能勾选一个答案:/

标签: haskell functional-programming pointfree


【解决方案1】:

想出(.) . (.) 实际上非常简单,它背后的直觉很难理解。

(.) 在将表达式重写为“管道”样式计算时让您走得很远(想想 shell 中的 |)。但是,一旦您尝试将一个接受多个参数的函数与一个只接受一个参数的函数组合在一起,使用起来就会变得很尴尬。例如,让我们定义concatMap

concatMap :: (a -> [b]) -> [a] -> [b]
concatMap f xs = concat (map f xs)

摆脱xs只是一个标准操作:

concatMap f = concat . map f

然而,摆脱f 没有“好”的方法。这是因为map 有两个参数,我们想在其最终结果上应用concat

您当然可以应用一些无意义的技巧,只需(.)

concatMap f = (.) concat (map f)
concatMap f = (.) concat . map $ f
concatMap = (.) concat . map
concatMap = (concat .) . map

但是,很遗憾,这段代码的可读性几乎没有了。相反,我们引入了一个新的组合子,它完全符合我们的需要:将第二个函数应用于第一个函数的最终结果

-- .: is fairly standard name for this combinator
(.:) :: (c -> d) -> (a -> b -> c) -> a -> b -> d
(f .: g) x y = f (g x y)

concatMap = concat .: map

好的,这就是动力。让我们开始点免费业务。

(.:) = \f g x y -> f (g x y)
     = \f g x y -> f ((g x) y)
     = \f g x y -> f . g x $ y
     = \f g x   -> f . g x

现在,有趣的部分来了。这是另一个在您遇到困难时通常会有所帮助的无点技巧:我们将. 重写为其前缀形式并尝试从那里继续。

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

至于直觉,你应该阅读这个very nice article。我将解释关于(.)的部分:

让我们再想想我们的组合器应该做什么:它应该将f应用于gresultresult(我一直在使用最终结果在之前的部分是故意的,这就是你完全应用时得到的结果 - 将类型变量与另一个函数类型模统一 - g 函数,result 这里只是应用程序g x 一些x)。

f 应用于g结果 对我们意味着什么?好吧,一旦我们将g 应用于某个值,我们将获取结果并将f 应用于它。听起来很熟悉:(.) 就是这样做的。

result :: (b -> c) -> ((a -> b) -> (a -> c))
result = (.)

现在,事实证明这些组合器的组合(我们的 of 词)只是一个函数组合,即:

(.:) = result . result -- the result of result

【讨论】:

  • 我真的很喜欢这个答案,它帮助了我。如果可以的话,我也会打勾。
【解决方案2】:

你也可以用你对fmap . fmap的理解。

如果你有两个Functors foobar,那么

fmap . fmap :: (a -> b)  ->  foo (bar a)    ->   foo (bar b)

fmap . fmap 接受一个函数并为两个Functors 的合成生成一个诱导函数。

现在,对于任何类型的 t(->) tFunctor,而 fmap 对应的 Functor(.)

所以(.) . (.)fmap . fmap 对于两个Functors 是(->) s(->) t 的情况,因此

(.) . (.) :: (a -> b) -> ((->) s) ((->) t a) -> ((->) s) ((->) t b)
          =  (a -> b) -> (s -> (t -> a))     -> (s -> (t -> b))
          =  (a -> b) -> (s ->  t -> a )     -> (s ->  t -> b )

它“组合”了一个函数 f :: a -> b 和一个有两个参数的函数 g :: s -> t -> a

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

该观点还清楚地表明,该模式以及如何扩展到接受更多参数的函数,

(.)             :: (a -> b) -> (s ->           a) -> (s ->           b)
(.) . (.)       :: (a -> b) -> (s -> t ->      a) -> (s -> t ->      b)
(.) . (.) . (.) :: (a -> b) -> (s -> t -> u -> a) -> (s -> t -> u -> b)

等等

【讨论】:

  • 所以我可以说foo (bar a) 是函数的函数?!啊:)
  • @FabianGerhardt foobar 这里是两个 type 变量。 foo(bar a) 表示“从s 到从ta 的函数”的类型。如果s 表示函数类型,它只会是函数的函数的类型,但这是无关的。
【解决方案3】:

当您介绍 y 时,您的解决方案会有所不同。应该是

\x f y -> ((.) ((.) x) f) y     :: (c -> d) -> (a -> b -> c) -> a -> b -> d
\x f y z -> ((.) ((.) x) f) y z :: (c -> d) -> (a -> b -> c) -> a -> b -> d
\x f y z -> ((.) x (f y)) z     :: (c -> d) -> (a -> b -> c) -> a -> b -> d
-- Or alternately:
\x f y z -> (x . f y) z         :: (c -> d) -> (a -> b -> c) -> a -> b -> d
\x f y z -> (x (f y z))         :: (c -> d) -> (a -> b -> c) -> a -> b -> d

与原始类型签名匹配:(.) . (.) :: (c -> d) -> (a -> b -> c) -> a -> b -> d

(在ghci中做扩展最简单,可以用:t expression检查每一步)

编辑:

更深层次的直觉是这样的:

(.) 简单定义为

\f g -> \x -> f (g x)

我们可以简化为

\f g x -> f (g x)

因此,当您为其提供两个参数时,它会被柯里化,并且仍然需要另一个参数来解决。 每次使用带有 2 个参数的 (.) 时,都会为另一个参数创建一个“需要”。

(.) . (.) 当然只是(.) (.) (.),所以让我们扩展它:

(\f0 g0 x0 -> f0 (g0 x0)) (\f1 g1 x1 -> f1 (g1 x1)) (\f2 g2 x2 -> f2 (g2 x2))

我们可以对f0g0 进行beta-reduce(但我们没有x0!):

\x0 -> (\f1 g1 x1 -> f1 (g1 x1)) ((\f2 g2 x2 -> f2 (g2 x2)) x0) 

用第二个表达式替换f1...

\x0 -> \g1 x1 -> ((\f2 g2 x2 -> f2 (g2 x2)) x0) (g1 x1) 

现在它“倒转”了! (f2 的 beta 减少):
这是一个有趣的步骤 - x0 被替换为 f2 - 这意味着 x 可能是数据,而不是一个函数。
就是 @987654343 @ 提供了额外参数的“需要”。

\x0 -> \g1 x1 -> (\g2 x2 -> x0 (g2 x2)) (g1 x1) 

这开始看起来很正常... 让我们最后一次进行 beta-reduce(g2):

\x0 -> \g1 x1 -> (\x2 -> x0 ((g1 x1) x2))

所以我们只剩下简单的了

\x0 g1 x1 x2 -> x0 ((g1 x1) x2)

,其中的参数很好地保持顺序。

【讨论】:

  • 真的,这要简单得多with the combinatory equations。真的。 :) 不是吗? (我从 Davie 那里得到这个,“Introduction to Functional Programming Systems Using Haskell”)。
  • @WillNess 这肯定更简单,但我发现所有论点都在飞来飞去更容易理解。不过,这看起来确实是一本好书!我以前没听说过。
  • 它有点过时了,但有它的“早期”魅力。不过,它没有 Monads,AFAIR。
【解决方案4】:

所以,这就是我进行稍微增量扩展时得到的结果

(.) f g   = \x -> f (g x)
(.) . g   = \x -> (.) (g x)
          = \x -> \y -> (.) (g x) y
          = \x -> \y -> \z -> (g x) (y z)
          = \x y z -> (g x) (y z)
(.) . (.) = \x y z -> ((.) x) (y z)
          = \x y z -> \k -> x (y z k)
          = \x y z k -> x (y z k)

根据 ghci 有正确的类型

Prelude> :t (.) . (.)
(.) . (.) :: (b -> c) -> (a -> a1 -> b) -> a -> a1 -> c
Prelude> :t \x y z k -> x (y z k)
\x y z k -> x (y z k)
  :: (t1 -> t) -> (t2 -> t3 -> t1) -> t2 -> t3 -> t
Prelude> 

虽然我不知道这个组合子的起源,但很可能是 开发用于组合逻辑,您严格使用组合器, 所以你不能使用更方便的 lambda 表达式来定义事物。可能有 一些与弄清楚这些事情有关的直觉,但我还没有找到。 如果你必须做的足够多,你很可能会发展出某种程度的直觉。

【讨论】:

    【解决方案5】:

    最简单的方法是写方程combinators-style,而不是 lambda 表达式:a b c = (\x -> ... body ...) 等价于 a b c x = ... body ...,反之亦然,前提是 x 不出现在 @ 中987654326@。所以,

    -- _B = (.)  
    
    _B f g x = f (g x)
    _B _B _B f g x y = _B (_B f) g x y
                     = (_B f) (g x) y
                     = _B f (g x) y
                     = f ((g x) y)
                     = f (g x y)
    

    如果给定f (g x y),您想将其转换为into a combinatory form(去掉所有括号和变量重复),您就会发现这一点。然后你应用与组合器的定义相对应的模式,希望向后追溯这个推导。不过,这要少得多机械/自动。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-05-23
      • 2013-09-15
      • 2013-09-19
      • 2021-08-15
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多