问题:
- For函数内部的循环返回一个无法解释的结果(第一张图片,在第150行用黄色突出显示(
- 与粘贴在函数外部的循环副本相同,返回预期结果(第二张图片,第164行用紫色突出显示(
- 我不是在寻找关于算法问题方法的反馈,只是试图理解以上内容
上下文:
- 岩石剪刀算法问题,根据作为参数传入的回合数,返回一个嵌套数组数组,表示可能的手的排列
- 函数是递归的,以"n"个循环作为参数,[[]]作为默认参数
- 第136-142行:因为可能的总排列数是3^n,所以它采用默认参数并将每个元素复制3次(newOutput(
- 第146-149行:对于循环通过newOutput,在第一个数组中添加"rock",在第二个数组中增加"paper",在在第三个嵌套数组中加"scapper",依此类推
问题
行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]]
之所以会发生这种情况,是因为您将SAME元素(Object(输出[0]n次推入累加器,创建它的n克隆,该克隆后来成为newOutput
。由于这些表示相同的实例,在其中一个实例上执行的每个操作都会对其他实例产生副作用。
如何解决
您可以通过使用排列运算符来解决此问题,该运算符将数组的值复制到新的值中。
...
acc.push([...curr]);
...
一些不错的阅读:https://javascript.info/object-copy
这是因为:
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"
如果需要副本,则必须使用排列运算符显式生成副本。假设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>