代码之家  ›  专栏  ›  技术社区  ›  user6445533

为什么函数组合在Javascript中是从右到左组成的?

  •  9
  • user6445533  · 技术社区  · 9 年前

    功能组成从右到左:

    const comp  = f => g => x => f(g(x));
    const inc = x => x + 1;
    const dec = x => x - 1;
    const sqr = x => x * x;
    let seq = comp(dec)(comp(sqr)(inc));
    
    seq(2); // 8
    

    seq(2) 被转换为 dec(sqr(inc(2))) 申请顺序是 inc(2)...sqr...dec 。因此,函数的调用顺序与传递给它们的顺序相反 comp 这对于Javascript程序员来说并不直观,因为他们习惯于方法链接,方法链接从左到右:

    o = {
      x: 2,
      inc() { return this.x + 1, this },
      dec() { return this.x - 1, this },
      sqr() { return this.x * this.x, this }
    }
    
    o.dec().sqr().inc(); // 2
    

    我认为这很令人困惑。下面是一个相反的组成:

    const flipped = f => g => x => g(f(x));
    let seql = flipped(dec)(flipped(sqr)(inc));
    
    seql(2); // 2
    

    函数组成从右向左有什么原因吗?

    2 回复  |  直到 4 年前
        1
  •  10
  •   Magne    4 年前

    回答原来的问题:为什么函数组合是从右到左组成的?

    1. 因此,它传统上是在数学中生成的
    2. comp(f)(g)(x) 具有与相同的顺序 f(g(x))
    3. 创建反向或正向合成很简单(参见示例)

    正向功能组成:

    const comp = f => g => x => f(g(x));
    const flip = f => x => y => f(y)(x);
    const flipped = flip(comp);
    
    const inc = a => a + 1;
    const sqr = b => b * b;
    
       comp(sqr)(inc)(2); // 9, since 2 is first put into inc then sqr
    flipped(sqr)(inc)(2); // 5, since 2 is first put into sqr then inc
    

    这种调用函数的方式称为 咖喱 ,其工作原理如下:

    // the original:
    comp(sqr)(inc)(2); // 9
    
    // is interpreted by JS as:
    ( ( ( comp(sqr) ) (inc) ) (2) ); // 9 still (yes, this actually executes!)
    
    // it is even clearer when we separate it into discrete steps:
    const compSqr = comp(sqr); // g => x => sqr(g(x))
    compSqr(inc)(2);   // 9 still
    const compSqrInc = compSqr(inc); // x => sqr(x + 1)
    compSqrInc(2);     // 9 still
    const compSqrInc2 = compSqrInc(2); // sqr(3)
    compSqrInc2;       // 9 still
    

    因此,函数是由JS解释器从左到右组合和解释的,而在执行时,它们的值从右到左流经每个函数。简而言之:先由外向内,然后由内向外。

    但是 flip 有一个限制,即翻转的构图不能与自身组合形成“更高阶构图”:

    const comp2 = comp(comp)(comp);
    const flipped2 = flipped(flipped)(flipped);
    const add = x => y => x + y;
    
       comp2(sqr)(add)(2)(3); // 25
    flipped2(sqr)(add)(2)(3); // "x => f(g(x))3" which is nonsense
    

    结论: 从右到左的顺序是传统的/传统的,但不是直观的。

        2
  •  3
  •   FPstudent    9 年前

    您的问题实际上是关于函数组合运算符定义中参数的顺序,而不是关于右关联或左关联。在数学中,我们通常写“f o g”(相当于您定义中的comp(f)(g))来表示接受x并返回f(g(x))的函数。因此,“f o(g o h)”和“(f o g)o h”是等价的,都表示将每个参数x映射到f(g(h(x))的函数。

    也就是说,我们有时写f;g(相当于代码中的comple(f)(g))表示将x映射到g(f(x))的函数。因此,两者(f;g);h和f;(g;h)表示x到h(g(f(x))的函数映射。

    参考文献: https://en.wikipedia.org/wiki/Function_composition#Alternative_notations

    推荐文章