代码之家  ›  专栏  ›  技术社区  ›  Harsha Venkataramu

重组库中的组合方法

  •  1
  • Harsha Venkataramu  · 技术社区  · 7 年前

    compose @acdlite在重组库中的函数,用于为高阶组件组合边界条件,这就是组合函数的外观

    const compose = (...funcs) => funcs.reduce((a, b) => (...args) => a(b(...args)), arg => arg);

    然而,我试过了 Eric-Elliott 这是一种单行方法,从 https://medium.com/javascript-scene/reduce-composing-software-fe22f0c39a1d

    const compose = (...fns) => x => fns.reduceRight((v, f) => f(v), x);

    我试着在我的react组件中使用这两种变体,

    const ListWithConditionalRendering = compose(
      withLoadingIndicator,
      withDataNull,
      withListEmpty
    )(Users);
    

    他们两个看起来都很好。我无法理解上述功能的工作方式是否有任何不同,如果有,它们是什么。

    3 回复  |  直到 7 年前
        1
  •  3
  •   Patrick Roberts Benjamin Gruenbaum    7 年前

    对于非常利基的场景,有一些差异可能有助于了解。

    reduce() 当它被合成而不是被调用时。相反,第二种方法返回一个作用域函数,该函数调用 reduceRight() 打电话

    第一个方法接受数组中最后一个函数的多个参数,而第二个方法只接受一个参数:

    const compose1 = (...funcs) => funcs.reduce((a, b) => (...args) => a(b(...args)), arg => arg);
    const compose2 = (...fns) => x => fns.reduceRight((v, f) => f(v), x);
    
    const f = s => (...args) => (console.log('function', s, 'length', args.length), args);
    
    compose1(f(1), f(2), f(3))(1, 2, 3);
    compose2(f(4), f(5), f(6))(1, 2, 3);

    const compose1 = (...funcs) => funcs.reduce((a, b) => (...args) => a(b(...args)), arg => arg);
    const compose2 = (...fns) => x => fns.reduceRight((v, f) => f(v), x);
    
    const f = v => v;
    
    try {
      compose1.apply(null, Array.from({ length: 1e5 }, () => f))();
      console.log('1 is safe');
    } catch (e) {
      console.log('1 failed');
    }
    
    try {
      compose2.apply(null, Array.from({ length: 1e5 }, () => f))();
      console.log('2 is safe');
    } catch (e) {
      console.log('2 failed');
    }

    ...fns arguments 也在堆栈上分配。

        2
  •  2
  •   user10675354 user10675354    7 年前

    如果您对reduce组合实际构建的结构感兴趣,可以将其可视化如下:

    /* original:
    const compose = (...funcs) =>
    funcs.reduce((a, b) => (...args) => a(b(...args)), arg => arg);
    */
    
    const compose = (...funcs) =>
      funcs.reduce((a, b) => `((...args) => ${a}(${b}(...args)))`, $_("id"));
    
    const $_ = name =>
      `${name}`;
    
    const id = x => x;
    const inc = x => x + 1;
    const sqr = x => x * x;
    const neg = x => -x;
    
    const computation = compose($_("inc"), $_("sqr"), $_("neg"));
    
    console.log(computation);
    
    /* yields:
    ((...args) => ((...args) => ((...args) =>
      id(inc(...args))) (sqr(...args))) (neg(...args)))
    */
    
    console.log(eval(computation) (2)); // 5 (= id(inc(sqr(neg(2))))

    这是怎么回事?我替换了内部函数 (...args) => a(b(...args)) arg => arg $_ 辅助函数。然后我将模板字符串包装在括号中,以便 String 代表 IIFE . 最后但并非最不重要的是我通过了 $_ 具有专有名称的帮助器函数 compose .

    $_ 这有点奇怪,但是可视化未应用/部分应用的函数确实很有帮助。

    从计算结构可以看出,reduce组合构建了匿名函数的嵌套结构,rest/spread操作分散在代码中。

    可视化和解释部分应用的函数是困难的。我们可以通过省略内部匿名函数来简化它:

    const compose = (...funcs) =>
      funcs.reduce($xy("reducer"), $_("id"));
    
    const $_ = name =>
      `${name}`;
    
    const $xy = name => (x, y) =>
      `${name}(${x}, ${y})`;
    
    const id = x => x;
    const inc = x => x + 1;
    const sqr = x => x * x;
    const neg = x => -x;
    
    console.log(
      compose($_("inc"), $_("sqr"), $_("neg"))
      // reducer(reducer(reducer(id, inc), sqr), neg)
    );

    我们可以通过实际运行合成来进一步简化:

    const compose = (...funcs) =>
      funcs.reduce((a, b) => (...args) => a(b(...args)), $x("id"));
    
    const $x = name => x =>
      `${name}(${x})`;
    
    console.log(
      compose($x("inc"), $x("sqr"), $x("neg")) (2) // id(inc(sqr(neg(2))))
    );

    我相信,像这样复杂计算的可视化是正确理解它们并更好地理解嵌套/递归计算结构的强大技术。

        3
  •  0
  •   Mulan    7 年前

    实施展示和讲述?好的-

    const identity = x =>
      x
    
    const compose = (f = identity, ...fs) => x =>
      f === identity
        ? x
        : compose (...fs) (f (x))
        
    const add1 = x =>
      x + 1
      
    console .log
      ( compose () (0)                   // 0
      , compose (add1) (0)               // 1
      , compose (add1, add1) (0)         // 2
      , compose (add1, add1, add1) (0)   // 3
      )

    compose 在线。。。

    const ListWithConditionalRendering = compose(
      withLoadingIndicator,
      withDataNull,
      withListEmpty
    )(Users);
    

    你可以做一种“正向合成”函数,在这里参数是第一位的-

    const $ = x => k =>
      $ (k (x))
      
    const add1 = x =>
      x + 1
      
    const double = x =>
      x * 2
    
    $ (0) (add1) (console.log)
    // 1
    
    $ (2) (double) (double) (double) (console.log)
    // 16
    
    $ (2) (double) (add1) (double) (console.log)
    // 10

    $ 当您可以维护-

    $ (value) (pureFunc) (pureFunc) (pureFunc) (...) (effect)
    

    在上面 将值放入某种“管道”,但无法获取该值 出来 . 只要稍加调整,我们就能写出非常灵活的变量表达式。下面,我们使用 $

    const $ = x => k =>
      k === $
        ? x
        : $ (k (x))
    
    const double = x =>
      x * 2
    
    const a =
      $ (2) (double) ($)
      
    const b =
      $ (3) (double) (double) (double) ($)
    
    console .log (a, b)
    // 4 24

    这个可变接口使您能够编写类似于梦寐以求的表达式 |> 运算符可以在其他更面向函数的语言中找到-

    value
      |> pureFunc
      |> pureFunc
      |> ...
      |> pureFunc
    
    5 |> add1
      |> double
      |> double
      // 24
    

    $ ,也就是说-

    $ (value) (pureFunc) (pureFunc) (...) (pureFunc) ($)
    
    $ (5) (add1) (double) (double) ($) // 24
    

    该技术还可以很好地与curried函数相结合-

    const $ = x => k =>
      $ (k (x))
    
    const add = x => y =>
      x + y
      
    const mult = x => y =>
      x * y
      
    $ (1) (add (2)) (mult (3)) (console.log)
    // 9

    const $ = x => k =>
      $ (k (x))
    
    const flatMap = f => xs =>
      xs .flatMap (f)
      
    const join = y => xs =>
      xs .join (y)
      
    const twice = x =>
      [ x, x ]
    
    $ ('mississippi')
      (([...chars]) => chars)
      (flatMap (twice))
      (join (''))
      (console.log)
      // 'mmiissssiissssiippppii'