2048:reduceRight 在具有 lodash/fp 的地图中的奇怪行为
2048 : Strange behaviour of reduceRight in map with lodash/fp
以下代码是尝试使用 lodash-fp 制作我的 2048(游戏)版本的开始。习惯了普通的lodash,但这是我第一次接触fp flavor
它实现了将一排瓦片向右推的动作,使用了两个函数:
slide
将行向右推,前提是 space 为空(由值 0 表示)。实际上,这相当于将所有零都放在左边。
merge
合并成对的相同值的连续图块,生成一个空 space 和一个新的双倍值图块。
(在 merge
之后第二次调用 slide
以清除该步骤可能产生的零)。
函数使用 _.reduceRight
方法,从右到左遍历图块。
import _ from "lodash/fp";
let game = [[2, 2, 0, 0], [4, 0, 4, 0], [8, 0, 0, 8], [16, 16, 0, 0]]; // Sample state
let slide = _.flow(
_.reduceRight(
(v, acc) =>
v === 0 ? [_.concat(acc[0], v), acc[1]] : [acc[0], _.concat(v, acc[1])],
[[], []]
),
_.flatten
);
let merge = _.flow(
_.reduceRight(
(v, acc) => {
acc[0].unshift(v);
if (acc[0].length === 2) {
if (acc[0][0] === acc[0][1]) {
acc[1] = _.concat(0, _.concat(acc[0][0] + acc[0][1], acc[1]));
acc[0] = [];
} else {
acc[1] = _.concat(acc[0].pop(), acc[1]);
}
}
return acc;
},
[[], []]
),
_.flatten
);
// Moves one line
let moveLine = _.flow(
slide,
merge,
slide
);
// Moves the 4 lines
let moveBoard = _.map(moveLine);
moveLine
似乎效果不错。例如,moveLine(game[0])
将 [2, 2, 0, 0]
转换为 [0, 0, 0, 4]
。
奇怪的是,moveBoard(game)
(将 moveLine
映射到 4 行)给出了一个奇怪的结果,每次迭代都变得更长,就好像附加了前面步骤的结果一样:
[
[0,0,0,4],
[0,0,0,0,0,0,8,4],
[0,0,0,0,0,0,0,0,0,16,8,4],
[0,0,0,0,0,0,0,0,0,0,0,0,32,16,8,4]
]
我看到问题出在merge
,但我真的看不出这是怎么回事。
更改 move
方法以删除前 4 个元素。
出于某些奇怪的原因,merge
的每次迭代
ReduceRight 的acc[1]作为其中的前一个数组
此补丁将修复它
let take = arr => arr.slice(0,4);
// Moves one line
let moveLine = _.flow(
slide,
merge,
take
);
这是一个带有实现的 runkit
我已经打开了一个 GitHub 问题的简化版本:https://github.com/lodash/lodash/issues/4287。
lodash 创建者 John-David Dalton 给出的简短回答:
With fp composition the accumulator is not create fresh for each composed reduce function. This is why create a mutationless version of the accumulator with fp.concat() works.
以下代码是尝试使用 lodash-fp 制作我的 2048(游戏)版本的开始。习惯了普通的lodash,但这是我第一次接触fp flavor
它实现了将一排瓦片向右推的动作,使用了两个函数:
slide
将行向右推,前提是 space 为空(由值 0 表示)。实际上,这相当于将所有零都放在左边。merge
合并成对的相同值的连续图块,生成一个空 space 和一个新的双倍值图块。 (在merge
之后第二次调用slide
以清除该步骤可能产生的零)。
函数使用 _.reduceRight
方法,从右到左遍历图块。
import _ from "lodash/fp";
let game = [[2, 2, 0, 0], [4, 0, 4, 0], [8, 0, 0, 8], [16, 16, 0, 0]]; // Sample state
let slide = _.flow(
_.reduceRight(
(v, acc) =>
v === 0 ? [_.concat(acc[0], v), acc[1]] : [acc[0], _.concat(v, acc[1])],
[[], []]
),
_.flatten
);
let merge = _.flow(
_.reduceRight(
(v, acc) => {
acc[0].unshift(v);
if (acc[0].length === 2) {
if (acc[0][0] === acc[0][1]) {
acc[1] = _.concat(0, _.concat(acc[0][0] + acc[0][1], acc[1]));
acc[0] = [];
} else {
acc[1] = _.concat(acc[0].pop(), acc[1]);
}
}
return acc;
},
[[], []]
),
_.flatten
);
// Moves one line
let moveLine = _.flow(
slide,
merge,
slide
);
// Moves the 4 lines
let moveBoard = _.map(moveLine);
moveLine
似乎效果不错。例如,moveLine(game[0])
将 [2, 2, 0, 0]
转换为 [0, 0, 0, 4]
。
奇怪的是,moveBoard(game)
(将 moveLine
映射到 4 行)给出了一个奇怪的结果,每次迭代都变得更长,就好像附加了前面步骤的结果一样:
[
[0,0,0,4],
[0,0,0,0,0,0,8,4],
[0,0,0,0,0,0,0,0,0,16,8,4],
[0,0,0,0,0,0,0,0,0,0,0,0,32,16,8,4]
]
我看到问题出在merge
,但我真的看不出这是怎么回事。
更改 move
方法以删除前 4 个元素。
出于某些奇怪的原因,merge
ReduceRight 的acc[1]作为其中的前一个数组
此补丁将修复它
let take = arr => arr.slice(0,4);
// Moves one line
let moveLine = _.flow(
slide,
merge,
take
);
这是一个带有实现的 runkit
我已经打开了一个 GitHub 问题的简化版本:https://github.com/lodash/lodash/issues/4287。
lodash 创建者 John-David Dalton 给出的简短回答:
With fp composition the accumulator is not create fresh for each composed reduce function. This is why create a mutationless version of the accumulator with fp.concat() works.