如何使用Lodash流程了解咖喱和功能组成?

时间:2017-02-09 07:35:08

标签: functional-programming lodash

2<c<5

我开始写跟踪2认为跟踪不起作用,因为“如何挖掘可能知道n或x的任何东西?”。

但是跟踪确实有效,我不明白它如何能够“注入”来自流量的x进入点击呼叫。任何解释将不胜感激:)

1 个答案:

答案 0 :(得分:15)

Silver Spoon评估

我们首先要跟踪

的评估
addSquare(3, 1) // ...

好的,这里是

= flow([add, trace('after add'), square]) (3, 1)
        add(3,1)
        4
             trace('after add') (4)
             tap(x => console.log(`== ${ 'after add' }:  ${ x }`)) (4)
             curry((interceptor, n) => { interceptor(n); return n; }) (x => console.log(`== ${ 'after add' }:  ${ x }`)) (4)
             (x => console.log(`== ${ 'after add' }:  ${ x }`)) (4); return 4;
             console.log(`== ${ 'after add' }:  ${ 4 }`); return 4;
~log effect~ "== after add: 4"; return 4
             4
                                 square(4)
                                 4 * 4
                                 16
= 16

所以基本&#34;技巧&#34;你很难看到trace('after add')返回一个等待最后一个参数的函数。这是因为trace是一个双参数函数 curried

<强>无用

我无法表达flow函数的无用和误解

function flow(funcs) {
  const length = funcs ? funcs.length : 0
  let index = length
  while (index--) {
    if (typeof funcs[index] != 'function') {
      throw new TypeError('Expected a function')
    }
  }
  return function(...args) {
    let index = 0
    let result = length ? funcs[index].apply(this, args) : args[0]
    while (++index < length) {
      result = funcs[index].call(this, result)
    }
    return result
  }
}

当然,&#34;工作&#34; 因为它被描述起作用,但它允许你创建可怕的,脆弱的代码。

  • 循环通过所有提供的函数来键入检查它们
  • 再次循环所有提供的功能以应用它们
  • 由于某种原因,允许第一个功能(以及第一个功能)具有接受1个或更多参数的特殊行为传入
  • 所有非首发函数最多只接受1个参数
  • 如果使用空流,则除了第一个输入参数之外的所有参数都将被丢弃

非常奇怪的f&#39;合同,如果你问我。你应该问:

  • 我们为什么要循环两次?
  • 为什么第一个函数会出现特殊异常?
  • 我以牺牲这种复杂性为代价获得了什么?

经典功能组合

两个函数fg的组合 - 允许数据看似从状态A直接传送到状态C。当然,状态B仍然在幕后发生,但事实上我们可以从认知负荷中消除这一点是一个巨大的礼物。

function composition

因为

,构图和曲线一起玩得很好
  1. 函数组合最适合一元(单参数)函数
  2. curried函数接受每个应用程序1个参数
  3. 让我们现在重写您的代码

    &#13;
    &#13;
    const add = a => b => a + b
    
    const square = n => n * n;
    
    const comp = f => g => x => f(g(x))
    
    const comp2 = comp (comp) (comp)
    
    const addSquare = comp2 (square) (add)
    
    console.log(addSquare(3)(1)) // 16
    &#13;
    &#13;
    &#13;

    &#34;嘿,你欺骗了我! comp2根本不容易理解!&#34; - 我很抱歉。但这是因为这个功能从一开始就注定失败了。为什么?

    因为组合最适合一元功能!我们尝试使用一元函数add编写二进制函数square

    为了更好地说明经典构图及其简单性,让我们使用 一元函数来查看序列。

    &#13;
    &#13;
    const mult = x => y => x * y
    
    const square = n => n * n;
    
    const tap = f => x => (f(x), x)
    
    const trace = str => tap (x => console.log(`== ${str}: ${x}`))
    
    const flow = ([f,...fs]) => x =>
      f === undefined ? x : flow (fs) (f(x))
    
    const tripleSquare = flow([mult(3), trace('triple'), square])
    
    console.log(tripleSquare(2))
    // == "triple: 6"
    // => 36
    &#13;
    &#13;
    &#13;

    哦,顺便说一句,我们也用一行代码重新实现了flow

    再次被逮捕

    好的,所以您可能已经注意到32参数是在不同的地方传递的。你会认为你再次被骗了。

    const tripleSquare = flow([mult(3), trace('triple'), square])
    
    console.log(tripleSquare(2)) //=> 36

    但事实是:只要在函数组合中引入单个非一元函数,您就可以重构代码。可读性立即急剧下降。如果它会损害可读性,那么试图保持代码无点是毫无意义的。

    让我们说我们必须保留原始addSquare函数的两个参数......看起来会是什么样?

    &#13;
    &#13;
    const add = x => y => x + y
    
    const square = n => n * n;
    
    const tap = f => x => (f(x), x)
    
    const trace = str => tap (x => console.log(`== ${str}: ${x}`))
    
    const flow = ([f,...fs]) => x =>
      f === undefined ? x : flow (fs) (f(x))
    
    const addSquare = (x,y) => flow([add(x), trace('add'), square]) (y)
    
    console.log(addSquare(3,1))
    // == "add: 4"
    // => 16
    &#13;
    &#13;
    &#13;

    好的,所以我们必须将addSquare定义为此

    const addSquare = (x,y) => flow([add(x), trace('add'), square]) (y)

    它当然不像lodash版本那样聪明,但是显式如何组合这些术语并且实际上是复杂性。

    事实上,这里的7行代码实现了整个程序,而不是单独实现lodash flow函数。

    大惊小怪,为什么

    您计划中的所有内容都是权衡。我不愿意看到初学者在应该简单的事情上挣扎。与使这些事情变得如此复杂的图书馆合作非常令人沮丧 - 甚至不让我开始使用Lodash的curry实施(包括 复杂createWrap

    我的2美分:如果你刚刚开始使用这些东西,那么图书馆就是一把大锤。他们有自己做出的每一个选择的理由,但要知道每个人都需要权衡利弊。所有这些复杂性并非完全没有根据,但作为初学者,你并不需要关注它。切断基本功能并从那里开始工作。

    <强>咖喱

    自从我提到curry以来,这里有3行代码几乎取代了Lodash咖喱的任何实际用途。

    如果您之后将这些用于更复杂的咖喱实施交易,请确保您知道自己从交易中获得了什么 - 否则您只需承担更多的开销而几乎没有收益。

    // for binary (2-arity) functions
    const curry2 = f => x => y => f(x,y)
    
    // for ternary (3-arity) functions
    const curry3 = f => x => y => z => f(x,y,z)
    
    // for arbitrary arity
    const partial = (f, ...xs) => (...ys) => f(...xs, ...ys)
    

    两种类型的功能组合

    我还应该提一点:经典功能组合应用从右到左的功能。因为有些人发现难以阅读/推理,所以像flowpipe这样的从左到右的函数作曲家出现在流行的库中

    rtl vs ltr function composition

    • 从左到右的作曲家flow,恰如其分地命名,因为当你尝试追踪时,你的眼睛会以意大利面形状流动通过程序移动的数据。 (LOL)

    • 从右到左作曲家,composer,会让你觉得自己最初是在倒退,但经过一些练习,它开始感觉很自然。它不会受到意大利面形状数据追踪的影响。