javascript函数中reduce之后for循环的无法解释的行为

gpnt7bae  于 2021-09-23  发布在  Java
关注(0)|答案(2)|浏览(400)

问题:
函数内部的for循环返回一个无法解释的结果(第一张图片,第150行以黄色突出显示)
与粘贴在函数外部的循环副本相同,返回预期结果(第2张图片,第164行以紫色突出显示)
我不是在寻找关于解决算法问题的方法的反馈——只是试图理解上面的内容
背景:
石头剪刀算法问题,根据作为参数传入的回合数,返回表示手的可能排列的嵌套数组数组
函数是递归的,以“n”轮作为参数,作为默认参数
第136-142行:由于可能的总置换数为3^n,因此它采用默认参数并将每个元素复制3次(newoutput)
第146-149行:对于通过newoutput的循环,将“rock”添加到第一个,将“paper”添加到第二个,将“剪刀”添加到第三个嵌套数组,依此类推
问题
第136行中元素的重复会影响146处for循环的结果,但为什么?这是无法解释的,因为执行线程已经移到了第146行。
第一张照片
第二张照片
代码如下:

function rockPaperScissors(num, output = [[]]) {

  // when num is 0, return output
  if (num === 0) return output;
  const moves = ['rock', 'paper', 'scissors']

  // take output parameter, and duplicate each of the existing nested array 3 times 
  const newOutput = output.reduce((acc, curr) => {
    let i = 1;
    while (i <= 3) {
      acc.push(curr);
      i++
    }
    return acc;
  }, [])

  // iterate through newOutput, push to each nested array rock first, then paper, then scissors, so on 
  for (let i = 0; i < newOutput.length; i++) {
    newOutput[i].push(moves[i%3]);
  }

  return rockPaperScissors(num - 1, newOutput);
}

rockPaperScissors(1)
// should return [[rock], [paper], [scissors]]
// instead, returns [[rock, paper, scissors], [rock, paper, scissors], [rock, paper, scissors]]
zf9nrax1

zf9nrax11#

这是因为您将同一元素(对象)的输出[0]n次推入累加器,创建了n个克隆,这些克隆随后成为 newOutput . 由于它们代表同一个示例,在其中一个示例上执行的每个操作都会对其他示例产生副作用。

如何解决?

您可以通过使用一个扩展操作符来解决这个问题,该操作符将数组的值复制到一个新的数组中。

...
      acc.push([...curr]);
...

一些好的读物:https://javascript.info/object-copy

js81xvg6

js81xvg62#

这是因为:

while (i <= 3) {
    acc.push(curr);   // these are not copies, it's all the same array
    i++
  }

这不是创建 curr 数组,它只会推送相同的引用3次。所有这3个元素都引用(或指向)相同的底层数组。你所拥有的是这样的:

[  ref_0,   ref_1,   ref_3  ]

      |         |        |
      |         |        |
       `---------`--------`--------------> []

当您尝试通过其中一个引用修改指向的数组的内容时,其他所有引用都会“看到”更改,因为它都是相同的数组:

[  ref_0,   ref_1,   ref_3  ]

      |         |        |
      |         |        |
       `---------`--------`--------------> ['rock', 'paper', 'scissors']

下面的可运行代码段演示了这一点。
请注意,您可以用其他内容替换每个引用;只有在修改它们指向的数组的内容时,问题才会变得明显。换句话说,您可以这样做:

newOutput[0] = "something else"

  [  ref_0,   ref_1,   ref_3  ]

      |         |        |
      |         |        |
      |          `--------`--------------> ['rock', 'paper', 'scissors']
      | 
       `---------> "something else"

如果需要副本,则必须使用spread运算符显式创建副本。假设内部对象为 output 始终是一个数组,您可以按如下方式复制它:

acc.push([...curr])

如果您没有令人信服的理由使用reduce,您可以这样做:

// [...Array(3).keys()]  produces the range [0, 1, 2]
// Use that with .map to create your copies.

const newOutput = [...Array(3).keys()].map(i => [...output[0]]);
const output = [['Array Content']];

const newOutput = output.reduce((acc, curr) => {
  let i = 1;
  while (i <= 3) {
    acc.push(curr);   // these are not copies, it's all the same array
    i++
  }
  return acc;
}, []);

displayInDiv('output-div-1', newOutput);
// shows: [["Array Content"],["Array Content"],["Array Content"]]

// Since all elements refer to the same underlying array, 
// modifying its contents through one of them affects all of them
output[0][0] = 'Modified Content';

displayInDiv('output-div-2', newOutput);
// shows: [["Modified Content"],["Modified Content"],["Modified Content"]]

displayInDiv('output-div-3', newOutput[0] === newOutput[1]);
displayInDiv('output-div-4', newOutput[1] === newOutput[2]);
// shows true in both cases
// it means that it's all the same object

function displayInDiv(divId, objectToShow) {
  const div = document.getElementById(divId);
  div.innerText = JSON.stringify(objectToShow);
}
div {
  margin-top: 4px;
}

.description {
  margin-top: 16px;
}

.output {
  color: gray;
  font-family: 'Courier New', monospace;
}
<div class="description">Result of reduce: </div>
<div id="output-div-1" class="output"></div>

<div class="description">After modification of array content: </div>
<div id="output-div-2" class="output"></div>

<div class="description">newOutput[0] === newOutput[1]: </div>
<div id="output-div-3" class="output"></div>

<div class="description">newOutput[1] === newOutput[2]: </div>
<div id="output-div-4" class="output"></div>

相关问题