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

如何在没有助手方法的情况下实现这个foldl0函数?

  •  1
  • Cowsay  · 技术社区  · 8 年前

    我有以下代码:

    function foldr0(list, func) {
      if (list.length == 0) {
        return 0;
      } else {
        return func(list[0], foldr0(list.slice(1), func));
      }
    }
    
    function foldl0(list, func) {
      if (list.length == 0) {
        return 0;
      } else {
        return ?
      }
    }
    

    我知道实现递归很容易 foldl0 通过定义辅助方法使用迭代逻辑 iter(list, func, part_result) 并将结果存储为参数。但是如何实施呢 折叠0 没有助手方法就像 foldr0 的实现?


    注意:为了方便起见,我用Javascript编写了这个问题。请用 car cdr 谢谢

    3 回复  |  直到 8 年前
        1
  •  1
  •   Mulan    8 年前

    我们将研究每个泛型的计算过程 foldr foldl . 查看以下实现,比较如何 f 获取的结果 但是 福尔德尔 获取的结果 f

    const foldr = ( f , acc , xs ) =>
      isEmpty (xs)
        ? acc
        : f ( foldr ( f , acc , tail ( xs ) )
            , head ( xs )
            )
    
    const foldl = ( f , acc , xs ) =>
      isEmpty (xs)
        ? acc
        : foldl ( f
                , f ( acc , head ( xs ) )
                , tail ( xs )
                )
    

    让我们仔细看看现在的过程 ,你可以看到 acc ( 0 )在任何 f 曾经计算过

    // example call
    foldr ( f , 0 , [ 1 , 2 , 3 ] )
    
    // notice we can't run f yet, still waiting for foldr
    f ( foldr ( f , 0 , [ 2 , 3 ] )
      , 1
      )
    
    
    // now 2 f calls pending; still waiting on foldr
    f ( f ( foldr ( f , 0 , [ 3 ] )
          , 2
          )
      , 1
      )
    
    // now 3 f calls pending, still waiting on foldr
    f ( f ( f ( foldr ( f , 0 , [] )
              , 3
              )
          , 2
          )
      , 1
      )
    
    // now we can compute the inner most f, working our way out
    f ( f ( f ( 0
              , 3
              )
          , 2
          )
      , 1
      )
    
    // in other words, foldr traverses your input and creates a stack of f calls
    f ( f ( f ( 0 , 3 ) , 2 ) , 1 )
    

    的调用堆栈 福尔德尔 注意acc是如何立即使用的

    // pretend f = ( x , y ) => x + y
    foldl ( f 
          , 0
          , [ 1 , 2 , 3 ]
          ) 
    
    // this time f is called first, and the result becomes the next acc
    foldl ( f
          , f ( 0 , 1 ) // next acc = 1
          , [ 2 , 3 ]
          )
    
    // f is always computed before recurring
    foldl ( f
          , f ( 1 , 2 ) // next acc = 3
          , [ 3 ]
          )
    
    // notice how foldl stays nice and flat (foldl uses a tail call, foldr doesn't)
    foldl ( f
          , f ( 3 , 3 ) // next acc = 6
          , []
          )
    
    // when the input is empty, the acc is just returned
    foldl ( f
          , 6
          , [] // empty input
          )
    
    // result
    6
    

    那么我们为什么要研究这些泛型呢?重点是向大家展示计算过程是什么样子的。在过程的可视化中,您可以看到数据如何在程序中移动。

    在里面 福尔德 你可以看到 acc公司 立即向下传递到调用堆栈,因此您可以有效地删除 acc公司 参数和替换 acc公司 具有 0 这是你的 foldr0

    const foldr0 = ( f , xs ) =>
      isEmpty (xs)
        ? 0
        : f ( foldr0 ( f , tail ( xs ) )
            , head ( xs )
            )
    

    然而,事实并非如此 福尔德尔 acc在每个步骤中计算,需要计算 下一个 步骤,所以我们不能删除参数并替换为 0 就像我们对 . 相反,最简单(最智能)的实现变得

    const foldl0 = ( f , xs ) => 
      foldl ( f , 0 , xs )
    
    const foldr0 = ( f , xs ) =>
      foldr ( f , 0 , xs )
    


    tl:dr;

    JavaScript充满了各种各样的技巧,所以你可以通过作弊来通过考试,阻止自己学习任何东西!

    const isEmpty = xs =>
      xs.length === 0
      
    const head = ( [ x , ... xs ] ) =>
      x
      
    const tail = ( [ x , ... xs ] ) =>
      xs
    
    const foldl0 = ( f , xs , acc = 0 ) =>
      isEmpty (xs)
        ? acc
        : foldl0 ( f
                 , tail ( xs )
                 , f ( acc , head ( xs ) )
                 )
            
    const myFunc = ( x , y ) =>
      `( ${x} + ${y} )`
      
    console.log ( foldl0 ( myFunc , [ 1 , 2 , 3 ] ) ) 
    // ( ( ( 0 + 1 ) + 2 ) + 3 )
        2
  •  0
  •   Bergi    8 年前

    只需使用与中完全相同的方法 foldr0 但是分割阵列 to the other side :

    function foldl0(list, func) {
      if (list.length == 0) {
        return 0;
      } else {
        return func(list[list.length-1], foldr0(list.slice(0, -1), func));
    //                   ^^^^^^^^^^^^^                     ^^^^^
    //                       last                       without last
      }
    }
    

    当然,如果你有 foldl / foldr 具有用于初始累加器值的参数的函数。

        3
  •  0
  •   Redu    8 年前

    var foldl0 = ([x0,x1,...xs],f) => xs.length ? foldl0([f(x0,x1)].concat(xs),f)
                                                : f(x0,x1);
    
    console.log(foldl0([1,2,3,4], (x,y) => x + y));