【问题标题】:Write f in pointfree-style?用无点样式写 f?
【发布时间】:2016-08-03 12:23:55
【问题描述】:

说我有函数

g :: a -> b, h :: a -> c 

f :: b -> c -> d. 

能不能写出函数

 f' :: a -> a -> d 

给出
f' x y = f (g x) (h y) 

无点风格?

可以写函数

f' a -> d, f' x = f (g x) (h x) 

通过设置进入无点风格

f' = (f <$> g) <*> h  

但我不知道如何处理更一般的情况。

【问题讨论】:

    标签: haskell pointfree


    【解决方案1】:

    我们有:

    k x y = (f (g x)) (h y)
    

    我们希望以无点风格写k

    传递给k 的第一个参数是x。我们需要用x 做什么?好吧,首先我们需要调用g,然后调用f,然后做一些花哨的事情将它应用到(h y)

    k = fancy . f . g
    

    这是什么fancy?嗯:

    k x y = (fancy . f . g) x y
          = fancy (f (g x)) y
          = f (g x) (h y)
    

    所以我们想要fancy z y = z (h y)。 Eta-reducing,我们得到fancy z = z . h,或fancy = (. h)

    k = (. h) . f . g
    

    一种更自然的思考方式可能是

                                 ┌───┐           ┌───┐
                            x ───│ g │─── g x ───│   │
                          /      └───┘           │   │
                   (x, y)                        │ f │─── f (g x) (h y)
                          \      ┌───┐           │   │
                            y ───│ h │─── h y ───│   │
                                 └───┘           └───┘
    
                          └──────────────────────────────┘
                                          k
    

    输入Control.Arrow

    k = curry ((g *** h) >>> uncurry f)
    

    【讨论】:

    • 这与我自己倾向于遵循的程序非常不同。看到新的方式总是令人耳目一新。
    【解决方案2】:

    看看online converter

    它转换了

    f' x y = f (g x) (h y) 
    

    进入

    f' = (. h) . f . g
    

    随着变换的流动

    f' = id (fix (const (flip ((.) . f . g) h))) 
    f' = fix (const (flip ((.) . f . g) h)) 
    f' = fix (const ((. h) . f . g)) 
    f' = (. h) . f . g
    

    【讨论】:

    • 你/它是如何从 f' x y = f (g x) (h y) 变为 f' = id (fix (const (flip ((.) . f . g) h))) 的?
    • 我不是这方面的专家。看起来库有一组预定义的转换,并在可能的转换空间中执行搜索。通过以下简化看起来它具有一些冗余功能。例如第一步可能是flip ((.) . f . g) h,没有id(fix(const
    【解决方案3】:

    这比(. h) . f. g 稍长,但更容易理解。

    首先,稍微重写f' 以获取一个元组而不是两个参数。 (换句话说,我们将取消您原来的f'。)

    f' (x, y) = f (g x) (h y)
    

    您可以使用 fstsnd 将元组分开,而不是对其进行模式匹配:

    f' t = f (g (fst t)) (h (snd t))
    

    使用函数组合,上面变成了

    f' t = f ((g . fst) t) ((h . snd) t)
    

    哪个,嘿,看起来很像你可以使用 applicative 样式制作的版本:

    f' = let g' = g . fst
             h' = h . snd
         in (f <$> g') <*> h'
    

    剩下的唯一问题是f' :: (a, a) -&gt; d。您可以通过显式柯里化来解决此问题:

    f' :: a -> a -> d
    f' = let g' = g . fst
             h' = h . snd
         in curry $ (f <$> g') <*> h'
    

    (顺便说一下,这与 Lynn 添加的 Control.Arrow 解决方案非常相似。)

    【讨论】:

    • 更容易关注(. h) . f . g?呃……我不这么认为。将 Applicative (a-&gt;) 实例与元组一起使用会给您带来两全其美:神秘的数据流语义和笨拙的柯里化。
    • 我发现(.) 的部分应用比Applicative (a -&gt;) 更难理解。用户已经表示理解(或至少知道)Applicative,所以我以此为基础。问题是如何编写函数的无点版本;我自己,我不会打扰并坚持完全可以理解的f x y = f (g x) (h y)
    【解决方案4】:

    使用应用于(.) 函数组合运算符的“运算符部分的三个规则”,

    (.) f g  =  (f . g)  =  (f .) g  =  (. g) f   -- the argument goes into the free slot
    --       1           2           3
    

    这可以通过几个简单的机械步骤推导出来:

    k x y =  (f (g x)) (h y)                      -- a (b c) === (a . b) c
          =  (f (g x) . h) y
          =  (. h)  (f (g x)) y
          =  (. h)  ((f . g)  x) y
          = ((. h) . (f . g)) x  y
    

    最后,(.) 是关联的,因此内部括号可能会被删除。

    一般的过程是努力达到可以执行 eta-reduction 的情况,即如果它们的顺序相同并且在任何括号之外,我们可以摆脱参数:

    k x y = (......) y
    =>
    k x   = (......)
    

    Latherrinse,重复。


    另一个技巧是将两个参数变成一个参数,反之亦然,用等式

    curry f x y = f (x,y)     
    

    所以,你的

    f (g x) (h y) = (f.g) x (h y)                      -- by B-combinator rule
                  = (f.g.fst) (x,y) ((h.snd) (x,y))
                  = (f.g.fst <*> h.snd) (x,y)          -- by S-combinator rule
                  = curry (f.g.fst <*> h.snd) x y
    

    这与answer by @chepner 相同,但更简洁。

    所以,你看,你的(f.g &lt;*&gt; h) x1 就变成了(f.g.fst &lt;*&gt; h.snd) (x,y)。相同的区别。


    1(因为对于函数,(&lt;$&gt;) = (.)

    【讨论】:

      【解决方案5】:

      Control.Compose

      (g ~> h ~> id) f
      

      Data.Function.Meld

      f $* g $$ h *$ id
      

      Data.Function.Tacit

      lurryA @N2 (f <$> (g <$> _1) <*> (h <$> _2))
      lurryA @N5 (_1 <*> (_2 <*> _4) <*> (_3 <*> _5)) f g h
      

      相关文章

      【讨论】:

        猜你喜欢
        • 2019-11-17
        • 2018-06-23
        • 2010-10-30
        • 2023-04-09
        • 2013-05-19
        • 2018-06-16
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多