几个注意事项:
第一,我同意ggorlen的前四点,部分同意第五点。这都是很好的建议,请考虑一下。
第二,如果您正在寻找对现有代码的最小更改,它可能如下所示:
post1970() {
let res = [];
let birth = this.birth;
let descendants = this.desc;
// if (birth > 1970) return this; // No, we will need to return an array of people
if (birth > 1970) res .push (this)
for (let i = 0; i < descendants.length; i++) {
let child = descendants[i];
let curr = child.post1970(); // Q: What should I do after this line?
res = res .concat (curr) // A: this! :-)
// console.log(curr)
}
return res;
}
(但请注意,这会立即禁止 ggorlen 的 const-over-let 建议,因为我们会在此过程中重新分配 res。这是我个人不喜欢此代码的原因之一。)
第三,我同意 ggorlen 让这段代码更灵活的目标。在这里,我建议使用普通函数而不是类方法。这也可以继续使用普通对象而不是整个树的类实例,但相同的代码将适用于任何一个。以下是我的处理方法:
const treeFilter = (getChildren) => (pred) => (node) => [
... (pred (node) ? [node] : []),
... (getChildren (node) || []) .flatMap (treeFilter (getChildren) (pred))
]
const familyTreeFilter = treeFilter (n => n .desc)
const post1970 = familyTreeFilter (n => n .birth > 1970)
const original = {name: "root", birth: 1900, desc: [{name: "andrew", birth: 1940, desc: [{name: "sarah", birth: 1960, desc: []}]}, {name: "charlie", birth: 1980, desc: [{name: "david", birth: 1990, desc: [{name: "ethan", birth: 2000,desc: []}]}]}]}
console .log (
post1970 (original) /* for display --> */ .map (n => n .name)
)
我们从treeFilter 开始,它接受一个描述树结构的函数(这里我们的孩子在节点desc [这听起来像是“描述”而不是“后代”],但是可能还有其他可能性,例如children。)这将返回一个接受谓词的函数——为谓词提供一个值,它会报告我们是否要包含该值。它返回一个接受树的函数,并将树的匹配元素作为平面数组返回。
这个 curried 版本的优点是我们可以在任何类似结构的树上重用简单的post1970。我们可以重用familyTreeFilter 为此类descendant-child 结构创建其他过滤器。也许我们想要所有在 2000 年之前拥有birth 的人,或者那些name 以'c' 开头的人,或者那些至少有四个后代的人。以这种方式编写每一个都是微不足道的。我们可以重用treeFilter 来描述完全不同的树结构上的过滤器,同样,只需简单的实现。
第四,如果这些抽象层次过于庞大,那么很容易将它们组合起来,将它们逐个内联,从而为您的用例获取自定义函数。我很少发现这是必要的,但我可能会在性能密集型代码块上这样做,或者如果我需要以某种简短形式添加函数(比如在 Stack Overflow 帖子上的 a comment。?)但如果你需要,我们可以这样:
const treeFilter = (getChildren) => (pred) => (node) => [
... (pred (node) ? [node] : []),
... (getChildren (node) || []) .flatMap (treeFilter (getChildren) (pred))
]
const familyTreeFilter = treeFilter (n => n .desc)
const post1970 = familyTreeFilter (n => n .birth > 1970)
和内联familyTreeFilter,把它变成
const treeFilter = (getChildren) => (pred) => (node) => [
... (pred (node) ? [node] : []),
... (getChildren (node) || []) .flatMap (treeFilter (getChildren) (pred))
]
const post1970 = treeFilter (n => n .desc) (n => n .birth > 1970)
然后我们可以内联treeFilter 来获取
const post1970 = (node) => [
... (node .birth > 1970 ? [node] : []),
... (node .desc || []) .flatMap (post1970)
]
这段代码本身就更简单了。但是我们之前所做的分解不仅帮助我们找到了这段代码,还为我们提供了更大的灵活性来构建其他工具。
最后,为了完整起见,我们应该证明此代码在您的基于 class 的实现上的工作方式相同。展开这个 sn-p 可以看到:
const treeFilter = (getChildren) => (pred) => (node) => [
... (pred (node) ? [node] : []),
... (getChildren (node) || []) .flatMap (treeFilter (getChildren) (pred))
]
const familyTreeFilter = treeFilter (n => n .desc)
const post1970 = familyTreeFilter (n => n .birth > 1970)
class Person {
constructor(name, birth) {
this.name = name;
this.birth = birth;
this.desc = [];
}
adddesc(person) {
this.desc.push(person);
}
}
const original = new Person('root', 1900)
let desc1 = new Person("andrew", 1940);
let desc2 = new Person("sarah", 1960);
let desc3 = new Person("charlie", 1980);
let desc4 = new Person("david", 1990);
let desc5 = new Person("ethan", 2000);
original.adddesc(desc1); //Andrew + Charlie
original.adddesc(desc3);
desc1.adddesc(desc2); //Andrew: Sarah
desc3.adddesc(desc4); //Charlie: David
desc4.adddesc(desc5); //David: Ethan
console .log (
post1970 (original) /* for display --> */ .map (n => n .name)
)