【问题标题】:Recursion - Sum Nested Array递归 - 求和嵌套数组
【发布时间】:2018-07-25 06:50:05
【问题描述】:

我正在尝试在不使用循环的情况下对嵌套数组 [1,2,[3,4],[],[5]] 求和,但我看不出我目前所拥有的有什么问题。

function sumItems(array) {
  let sum = 0;
  array.forEach((item) => {
    if (Array.isArray(item)) {
      sumItems(item);
    } else {
      sum += item;
    }
  });
  return sum;
}

【问题讨论】:

  • 您没有存储嵌套数组sumItems(item) 的总和。检查多米尼克的答案
  • “我正在尝试对嵌套数组 [1,2,[3,4],[],[5]] 求和而不使用循环”上次我检查 forEach 是一个循环.
  • @gforce301 从技术上讲,forEach 是一种使用循环的方法。

标签: javascript recursion


【解决方案1】:

试试

 function sumItems(array) {

  let sum = 0;
  array.forEach((item) => {
    if(Array.isArray(item)) {
     sum += sumItems(item);
    } else {
    sum += item;
    }
  })
  return sum;
}

【讨论】:

    【解决方案2】:

    这是一个不使用循环的版本:

    function f(arr, i){
      if (i == arr.length)
        return 0;
    	
      if (Array.isArray(arr[i]))
        return f(arr[i], 0) + f(arr, i + 1);
    	  
      return arr[i] + f(arr, i + 1);
    }
    
    console.log(f([1,2,[3,4],[],[5]], 0));

    【讨论】:

      【解决方案3】:

      递归是一种功能遗产

      递归是一个源自函数式风格的概念。将它与命令式风格混合起来会给新程序员带来很多痛苦和困惑。

      To design a recursive function,我们确定了 baseinductive 情况。

      • 基本情况 - 要求和的项目列表为空;即,itemEmpty。返回0
      • 归纳案例 1 - 项目列表为空;即,必须至少有一个item。如果项目是列表,则返回其总和加上项目的rest 的总和
      • 归纳案例 2 - 至少有一个item 不是数组。返回此项目加上项目的rest 的总和

      const Empty =
        Symbol ()
      
      const sumDeep = ([ item = Empty, ...rest ] = []) =>
        item === Empty
          ? 0
          : Array.isArray (item)
            ? sumDeep (item) + sumDeep (rest)
            : item + sumDeep (rest)
      
      console.log
        ( sumDeep ([ [ 1, 2 ], [ 3, 4 ], [ 5, [ 6, [] ] ] ]) // 21
        , sumDeep ([ 1, 2, 3, 4, 5, 6 ])                     // 21
        , sumDeep ([])                                       // 0
        , sumDeep ()                                         // 0
        )

      由于此实施,所有痛苦和痛苦都从程序中消失了。我们不关心局部状态变量、变量重新分配或像forEach 这样的副作用,也不使用函数调用的返回值。


      递归注意

      还有一个尾递归版本,可以制作为stack-safe。在这里,我们添加了一个参数cont 来表示我们的延续,它有效地允许我们在不增加堆栈的情况下对+ 操作的顺序进行排序 - bold

      的变化
      常量标识 = x =>
        x
      
      const sumDeep = ([ item = Empty, ...rest ] = [], cont = identity) =>
        项目 === 空
          ? 续(0
          : Array.isArray (项目)
            ? sumDeep(项目,a =>
               sumDeep(休息,b =>
                 续 (a + b)))
            : sumDeep (休息, a =>
                续(项目+ a)

      用法相同

      console.log
        ( sumDeep ([ [ 1, 2 ], [ 3, 4 ], [ 5, [ 6, [] ] ] ]) // 21
        , sumDeep ([ 1, 2, 3, 4, 5, 6 ])                     // 21
        , sumDeep ([])                                       // 0
        , sumDeep ()                                         // 0
        )
      

      性能提升

      正如@גלעד ברקן 指出的,上面使用的数组解构语法(例如...rest)会创建输入数组的副本。如他/她的回答所示,可以使用索引参数来避免创建副本。这种变化显示了索引技术也可以以尾递归的方式使用

      const identity = x =>
        x
      
      const sumDeep = (items = [], i = 0, cont = identity) =>
        i >= items.length
          ? cont (0)
          : Array.isArray (items [i])
            ? sumDeep (items [i], 0, a =>
                sumDeep (items, i + 1, b =>
                  cont (a + b)))
            : sumDeep (items, i + 1, a => 
                cont (items [i] + a))
      
      console.log
        ( sumDeep ([ [ 1, 2 ], [ 3, 4 ], [ 5, [ 6, [] ] ] ]) // 21
        , sumDeep ([ 1, 2, 3, 4, 5, 6 ])                     // 21
        , sumDeep ([])                                       // 0
        , sumDeep ()                                         // 0
        )

      【讨论】:

      • ...rest 进行复制,这是低效的内存使用,尽管实际上它可能不是问题。
      • @גלעדברקן 这是正确的,但副本不会保留在内存中。您使用索引的方法是一个很好的解决方法:)
      【解决方案4】:

      您可以为Array#reduce 定义一个回调,它检查一个项目是否是一个数组,并再次为该数组使用这个函数。

      function add(s, v) {
          return Array.isArray(v)
              ? v.reduce(add, s)
              : s + v;
      }
      
      var array = [1, 2, [3, 4], [], [5]];
      
      console.log(array.reduce(add, 0));

      【讨论】:

        【解决方案5】:

        你可以这样做;

        var sumNested = ([a,...as]) => (as.length && sumNested(as)) + (Array.isArray(a) ? sumNested(a) : a || 0);
        
        console.log(sumNested([1,2,3,[4,[5,[6]]],7,[]]));

        函数参数指定 [a,…as] 意味着当函数被提供一个嵌套数组时,如 [1,2,3,[4,[5,[6]]],7,[]] 然后 a 分配给头部 1as 分配给尾部初始数组是[2,3,[4,[5,[6]]],7,[]]。其余的应该很容易理解。

        【讨论】:

          【解决方案6】:
          function arraySum (array) {
            if (array.length > 0) {
              return arraySum(array[0]) + arraySum(array.slice(1));
            }
            if (array.length === 0) {
              return 0;
            } else {
              return array;
            }
          };
          

          【讨论】:

            【解决方案7】:

            这与其他一些解决方案类似,但对某些人来说可能更容易阅读:

             function Sum(arr) {
               if (!arr.length) return 0;
               if (Array.isArray(arr[0])) return Sum(arr[0]) + Sum(arr.slice(1));
               return arr[0] + Sum(arr.slice(1));
             }
            
             console.log(Sum([[1],2,[3,[4,[5,[6,[7,[8,9,10],11,[12]]]]]]])) // 78
            

            【讨论】:

              猜你喜欢
              • 2021-05-06
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2015-06-12
              • 2023-04-02
              • 2020-07-15
              • 2010-10-23
              相关资源
              最近更新 更多