【问题标题】:One-Dimensional Array Iteration Using Recursion使用递归的一维数组迭代
【发布时间】:2018-10-11 14:56:48
【问题描述】:

我正在尝试使用递归迭代一个简单的数组。对于这种特定情况,我正在尝试使用递归重新创建.map()(不使用.map()!。我目前只推送原始数组中的最后一个元素,但我想将所有元素都推送到数组中。

function recursiveMap (arr, func) {
    let newArr = [];
    if (arr.length === 1){
        newArr.push(func(arr));
    }
    else {
        newArr.push(...recursiveMap(arr.slice(1),func));
    }
    return newArr;
}

【问题讨论】:

    标签: javascript arrays recursion


    【解决方案1】:

    您需要在当前项上使用func,并将调用函数的结果传播到数组的其余部分:

    function recursiveMap(arr, func) {
      return arr.length ? [func(arr[0]), ...recursiveMap(arr.slice(1), func)] : [];
    }
    
    const arr = [1, 2, 3];
    
    const result = recursiveMap(arr, n => n * 2);
    
    console.log(result);

    【讨论】:

      【解决方案2】:

      您的基本情况似乎是错误的。您需要检查一个空数组:

      function recursiveMap (arr, func) {
          let newArr = [];
          if (arr.length === 0) {
              // do nothing
          } else {
              newArr.push(func(arr[0]));
              newArr.push(...recursiveMap(arr.slice(1),func));
          }
          return newArr;
      }
      

      当至少有一个元素时,您需要调用func(在第一项上)。

      【讨论】:

        【解决方案3】:

        通过递归,我发现将基本情况作为您在函数中检查的第一件事并缩短那里的执行时间是很有帮助的。 map 的基本情况是如果数组有 0 个项目,在这种情况下您将返回一个空数组。

        如果你之前没见过let [a, ...b] 是数组解构,a 成为第一个值,b 保存剩余的数组。你可以对 slice 做同样的事情。

        function recursiveMap(arr, func){
          if(arr.length == 0) return [];
          let [first, ...rest] = arr;
          return [func(first)].concat(recursiveMap(rest, func));
        }
        
        let test = [1,2,3,4,5,6,7];
        console.log(recursiveMap(test, (item) => item * 2));

        编辑

        回到您的示例,我看到您在 xD 之前显然已经看到了解构,抱歉。将其留在答案中,以供将来的答案读者使用。

        【讨论】:

        • s/next/first/ :-)
        【解决方案4】:

        以下是一些替代方案。每个recursiveMap

        • 不改变输入
        • 生成一个新数组作为输出
        • 在给出空输入时产生有效结果[]
        • 使用单一的纯函数式表达式

        解构赋值

        const identity = x =>
          x
        
        const recursiveMap = (f = identity, [ x, ...xs ]) =>
          x === undefined
            ? []
            : [ f (x), ...recursiveMap (f, xs) ]
            
        const square = (x = 0) =>
          x * x
          
        console.log (recursiveMap (square, [ 1, 2, 3, 4, 5 ]))
        // [ 1, 4, 9, 16, 25 ]

        数组切片

        const identity = x =>
          x
        
        const recursiveMap = (f = identity, xs = []) =>
          xs.length === 0
            ? []
            : [ f (xs[0]), ...recursiveMap (f, xs.slice (1)) ]
            
        const square = (x = 0) =>
          x * x
          
        console.log (recursiveMap (square, [ 1, 2, 3, 4, 5 ]))
        // [ 1, 4, 9, 16, 25 ]
          

        带有默认赋值的附加参数——创建更少的中间值

        const identity = x =>
          x
        
        const recursiveMap = (f = identity, xs = [], i = 0) =>
          i >= xs.length
            ? []
            : [ f (xs[i]) ] .concat (recursiveMap (f, xs, i + 1))
            
        const square = (x = 0) =>
          x * x
          
        console.log (recursiveMap (square, [ 1, 2, 3, 4, 5 ]))
        // [ 1, 4, 9, 16, 25 ]

        尾递归(可爱)

        const identity = x =>
          x
          
        const prepend = x => xs =>
          [ x ] .concat (xs)
          
        const compose = (f, g) =>
          x => f (g (x))
        
        const recursiveMap = (f = identity, [ x, ...xs ], then = identity) =>
          x === undefined
            ? then ([])
            : recursiveMap
                ( f
                , xs
                , compose (then, prepend (f (x)))
                )
        
        const square = (x = 0) =>
          x * x
        
        console.log (recursiveMap (square, [ 1, 2, 3, 4, 5 ]))
        // [ 1, 4, 9, 16, 25 ]
        // => undefined
        
        recursiveMap (square, [ 1, 2, 3, 4, 5 ], console.log)
        // [ 1, 4, 9, 16, 25 ]
        // => undefined
        
        recursiveMap (square, [ 1, 2, 3, 4, 5 ])
        // => [ 1, 4, 9, 16, 25 ]

        源自尾递归foldl - 注意foldl 选择了上面使用的类似技术:具有默认分配的附加参数。

        const identity = x =>
          x
        
        const foldl = (f = identity, acc = null, xs = [], i = 0) =>
          i >= xs.length
            ? acc
            : foldl
                ( f
                , f (acc, xs[i])
                , xs
                , i + 1
                )
        
        const recursiveMap = (f = identity, xs = []) =>
          foldl
            ( (acc, x) => acc .concat ([ f (x) ])
            , []
            , xs
            )
            
        const square = (x = 0) =>
          x * x
          
        console.log (recursiveMap (square, [ 1, 2, 3, 4, 5 ]))
        // [ 1, 4, 9, 16, 25 ]

        【讨论】:

          【解决方案5】:

          您可以通过对收集的值使用第三个参数来采取另一种方法。

          function recursiveMap(array, fn, result = []) {
              if (!array.length) {
                  return result;
              }
              result.push(fn(array[0]));
              return recursiveMap(array.slice(1), fn, result);
          }
          
          console.log(recursiveMap([1, 2, 3, 4, 5], x => x << 1));
          console.log(recursiveMap([], x => x << 1));

          【讨论】:

          • 当您在每一步仍然使用数组扩展语法时,谈论优化(尤其是仍未实现的尾调用)有点毫无意义......
          • 你是否也陷入了空数组的无限递归?
          • @Bergi,也许它来得更快,比所有用户都知道这种技术。反正。它现在也适用于空数组。
          【解决方案6】:

          欢迎来到 Stack Overflow。您可以将结果传递给自身,如下例所示:

          function recursiveMap (arr, func,result=[]) {
            if (arr.length === 0){
                return result;
            }
            return recursiveMap(
                arr.slice(1),
                func,
                result.concat([func(arr[0])])
              );
          }
          console.log(recursiveMap([1,2,3,4],x=>(x===3)?['hello','world']:x+2));

          或者在你的函数中定义一个递归函数:

          function recursiveMap (arr, func) {
            const recur = (arr, func,result=[])=>
              (arr.length === 0)
                ? result
                : recur(
                  arr.slice(1),
                  func,
                  result.concat([func(arr[0])])
                );
            return recur(arr,func,[])
          }
          console.log(recursiveMap([1,2,3,4],x=>(x===3)?['hello','world']:x+2));

          【讨论】:

          • func返回数组时使用concat追加不起作用
          • @Bergi 是的,应该使用concat([value]),除非你想变平。我更改了代码。
          【解决方案7】:

          在再次调用函数之前添加newArr.push(func(arr[0]));

          function recursiveMap (arr, func) {
              let newArr = [];
              if (arr.length === 1){
                  newArr.push(func(arr));
              }
              else {
                  newArr.push(func(arr[0]));
                  newArr.push(...recursiveMap(arr.slice(1),func));
              }
              return newArr;
          }
          
          console.log(recursiveMap([1,2,3], function(a){return +a+2}))

          相同但修改了错误的答案

          function recursiveMap (arr, func) {
              let newArr = [];
              if(arr.length){
                 newArr.push(func(arr[0]));
                 if(arr.length > 1){
                    newArr.push(...recursiveMap(arr.slice(1),func));
                }
              }
              return newArr;
          }
          
          console.log(recursiveMap([1,2,3], function(a){return a+2}))

          【讨论】:

          • 这不适用于空数组。并且在最后一个元素上做了一些奇怪的事情——你真的不需要在你的回调中使用+a
          • @Bergi 是的,我知道,但我不想修改 OP 代码,只需给他他正在寻找的答案,这样他对代码的理解就不会改变。
          猜你喜欢
          • 2020-03-21
          • 2013-11-11
          • 2011-04-19
          • 2012-02-25
          • 2014-05-13
          • 1970-01-01
          • 2014-10-18
          • 2018-09-20
          • 2019-02-20
          相关资源
          最近更新 更多