javascript - JavaScript:在递归之间需要一个共享数组才能将结果推送到
问题描述
有一个数组,其中有一个家谱,其中有人的名字和他们的性别以及他们父母的名字(父亲和母亲)。
现在我在这里(不成功)试图列出这个家庭中一个人的所有孩子的名字:
function allChildren(parent) {
var children = ancestors.filter(
function(person) {
if (parent.sex == 'm') {
return person.father == parent.name
} else {
return person.mother == parent.name
}
}
)
if (children.length == 0) {
console.log(parent.name);
} else {
children.forEach(allChildren);
}
}
目前这只是控制台记录没有孩子的人。问题是,我需要将每个孩子推入一个结果数组,但我使用的是递归,所以我不能在所有递归之间共享一个结果变量。
(当然我还应该检查之前没有推送过相同的名称 - 假设没有重复 - 这不是我的问题,我知道该怎么做,我的问题是关于获得一系列结果,并填写递归步骤逐渐(每个递归步骤都可以将一个元素推入其中))。
解决方案
这是一种类似于 KarelG 的方法。但是,它已经足够不同了,足以保证它自己的答案。
差异
这个将祖先作为参数传递,而不是将它们作为函数中的自由变量。自由变量使代码更难理解,也更难测试。
它将递归函数与公共函数分开,将它们放在一个闭包中。现在人们在很多地方都在使用某种模块,这是一种不太常见的技术,但它仍然很有用,尤其是在网络上。这就是
(() => {/* ... return some function */ })()
语法。它使用 Set 作为递归累加器,以避免重复。
它忽略了性别。我看不出检查它的理由。如果孩子说母亲是某某,我认为仔细检查 `mother.sex === 'f'` 没有任何意义。我可能在这里遗漏了一些东西。
虽然它保留了提供完整人员对象并返回姓名列表的现有 API,但将其切换为仅提供姓名或返回人员列表相对容易。更多关于下面的内容。
我命名它
allDescendents
,因为这似乎比allChildren
.
代码
const allDescendents = (() => {
const all = (family, name, children = new Set()) => {
const kids = family.filter(p => p.father === name || p.mother === name).map(k => k.name)
kids.forEach(kid => (children.add(kid), all(family, kid, children)))
return children
}
return (family, person) => Array.from(all(family, person.name))
})()
const bourbons = [ // https://en.wikipedia.org/wiki/Bourbon_family_tree
{name: 'Philip III', sex: 'm', father: 'Louis IX', mother: 'Margaret'},
{name: 'Robert', sex: 'm', father: 'Louis IX', mother: 'Margaret'},
{name: 'Charles', sex: 'm', father: 'Philip III', mother: '?'},
{name: 'Louis I', sex: 'm', father: 'Robert', mother: 'Beatrice'},
{name: 'Philip IV', sex: 'm', father: 'Charles', mother: '?'},
{name: 'Isabella', sex: 'f', father: 'Charles', mother: '?'},
{name: 'Peter I', sex: 'm', father: 'Louis I', mother: 'Mary'},
{name: 'James I', sex: 'm', father: 'Louis I', mother: 'Mary'},
{name: 'John II', sex: 'm', father: 'Philip IV', mother: '?'},
{name: 'Peter II', sex: 'm', father: 'James I', mother: 'Jeanne'},
{name: 'John I', sex: 'm', father: 'James I', mother: 'Jeanne'},
{name: 'Charles V', sex: 'm', father: 'John II', mother: '?'},
{name: 'Joanna', sex: 'f', father: 'Peter I', mother: 'Isabella'},
{name: 'Louis II', sex: 'f', father: 'Peter I', mother: 'Isabella'},
{name: 'James II', sex: 'm', father: 'John I', mother: 'Catherine'},
{name: 'Louis', sex: 'm', father: 'John I', mother: 'Catherine'},
/* .. */
]
const louis1st = bourbons[3]
console.log(allDescendents(bourbons, louis1st))
变体
考虑到这一点,使得重大改变变得相当容易。大多数这些变化也可以组合起来。
返回一个Set
Set
返回 a of 值而不是a 可能更有意义Array
。大概您不希望此集合包含重复项。ASet
是正确的数据结构。此解决方案在Set
内部使用 a,将其设为公共接口很简单。只需删除对Array.from
:
- return (family, person) => Array.from(all(family, person.name))
+ return (family, person) => all(family, person.name)
提供名称
由于您要返回一组名称,因此为输入提供名称可能会更容易。我们可以通过更改输入参数来接收名称来做到这一点:
- return (family, person) => Array.from(all(family, person.name))
+ return (family, name) => Array.from(all(family, name))
返回人
相反,您可能更喜欢直接返回人员对象。名单只有这么有用。包含完整 Person 对象的列表可能更易于使用。这就是它的样子:
- const all = (family, name, children = new Set()) => {
- const kids = family.filter(p => p.father === name || p.mother === name).map(k => k.name)
+ const all = (family, person, children = new Set()) => {
+ const kids = family.filter(p => p.father === person.name || p.mother === person.name)
- return (family, person) => Array.from(all(family, person.name))
+ return (family, person) => Array.from(all(family, person))
避免默认参数
如果函数中的默认参数困扰您(例如,编辑器中的红色波浪线),那么您可以将参数移动到导出的函数中。我喜欢将复杂性保留在一个地方,这就是为什么我在递归函数上有默认值的原因,但是有一个很好的论据可以以牺牲公共函数为代价来简化递归函数;它确实使递归函数更容易理解。
- const all = (family, name, children = new Set()) => {
+ const all = (family, name, children) => {
- return (family, person) => Array.from(all(family, person.name))
+ return (family, person) => Array.from(all(family, person.name, new Set()))
制作一个模块
最后,我们可以很容易地把它变成一个模块。如果您要部署到浏览器,这仍然需要一个构建步骤,但它确实简化了代码:这是一个版本:
const all = (family, name, children = new Set()) => {
const kids = family.filter(p => p.father === name || p.mother === name).map(k => k.name)
kids.forEach(kid => (children.add(kid), all(family, kid, children)))
return children
}
const allDescendents = (family, person) => Array.from(all(family, person.name))
export {allDescendents}
推荐阅读
- c# - 减少 C# 中的 Foreach 循环
- javascript - React native 需要一个字符串(用于内置组件)或一个类/函数(用于复合组件),但得到:object
- javascript - highcharts 列的表单数据系列
- visual-studio-code - 如何设置键盘映射以在 Visual Studio Code 中运行代码?
- php - 将 Symfony 项目部署到产品服务器时出现问题
- javascript - 支持“快速排序”算法
- python-3.x - 在两个不同的字符串中查找重复的字母
- python - 导入 pygmsh 模块后,Python 子进程会丢失我的 PATH 中的内容吗?
- php - Woocommerce:从插件中获取一些自定义购物车项目数据值
- c# - 将 Sqlite 数据库保存到文件