我正在努力与Eloquent JS中的高阶函数章节中的“伟大伟大伟大的......”作例。我不明白其中一个函数如何从仅包含祖先数据的对象创建值。以下是功能:
function reduceAncestors(person, f, defaultValue) {
function valueFor(person) {
if (person == null)
return defaultValue;
else
return f(person, valueFor(byName[person.mother]),
valueFor(byName[person.father]));
}
return valueFor(person);
}
和
function sharedDNA(person, fromMother, fromFather) {
if (person.name == "Pauwels van Haverbeke")
return 1;
else
return (fromMother + fromFather) / 2;
}
我不明白valueFor(byName [person.mother])如何从这样的对象生成数值:
"Carolus Haverbeke" : {
"name": "Carolus Haverbeke",
"sex": "m",
"born": 1832,
"died": 1905,
"father": "Carel Haverbeke",
"mother": "Maria van Brussel"}
答案 0 :(得分:4)
宣告sharedDNA
后,您会看到:
var ph = byName["Philibert Haverbeke"];
console.log(reduceAncestors(ph, sharedDNA, 0) / 4);
byName
是一个充满关键的对象:你为Carolus写的价值对。关键是人的名字,相应的值是'人物对象',它有关键:'name','mother','father'等的值对。这很有用,因为对于每个递归循环,我们需要找出母亲和父亲的结果,这样我们就可以轻松查找当前人的母亲和父亲的名字,然后从byName对象访问他们的人物对象。所以在行
var ph = byName["Philibert Haverbeke"];
我们为Philibert提取此人物对象并将其存储在名为ph
的变量中。然后,使用此人物对象,reduceAncestors
函数和sharedDNA
defaultValue
来调用0
(稍后会详细介绍)。我们除以4因为Philibert是作者的祖父。所以,这个函数将返回祖父和伟大,伟大,伟大,伟大......祖父之间的共享DNA。为了找到作者的共享DNA,我们需要将他祖父的结果除以4(每一代将共享的DNA减半)。
为了更容易理解这个例子,我发现尝试找到一些更简单的例子的共享DNA是有帮助的。首先,让我们试着为Pauwels van Haverbeke找到共享的DNA,这是伟大的,伟大的,伟大的,伟大的......我们用来比较的祖父。所以我们知道答案:Pauwels分享他自己DNA的100%。
因此,在这种情况下,将使用Pauwels的人物对象,reduceAncestors
函数和sharedDNA
defaultValue
使用以下代码调用0
:< / p>
var pauwels = byName["Pauwels van Haverbeke"];
console.log(reduceAncestors(ph, sharedDNA, 0));
在reduceAncestors
中,我们将进入else子句和行
return f(person, valueFor(byName[person.mother]), valueFor(byName[person.father]));
将成为
return sharedDNA(pauwels, valueFor(null), valueFor(null));
由于Pauwels的母亲和父亲不在数据集中,person.mother
和person.father
表达式将评估为null
。 sharedDNA
想要调用,但需要等待两个valueFor(null)
调用进行评估。
使用valueFor
调用null
时,它会进入if
子句,因为person === null
将为真。然后,它会return defaultValue
,您回忆起我们在0
的原始调用中以reduceAncestors
传入。
所以,现在上面的行变为
return sharedDNA(pauwels, 0, 0)
如果我们查看sharedDNA
函数,我们会看到我们将评估第一个if
子句,因为person.name == "Pauwels van Haverbeke"
将为真。因此,我们将返回1.通过这种方式,使用最简单的示例,您可以看到sharedDNA
如何返回数字。
现在,让我们再举一个例子,这次是Pauwel的孩子,Lieven van Haverbeke。我们会这样称呼它:
var pauwelsChild = byName["Lieven van Haverbeke"];
console.log(reduceAncestors(pauwelsChild, sharedDNA, 0));
在reduceAncestors
内,我们将进入else
条款,我们将进入:
return sharedDNA(pauwelsChild, valueFor(byName["Lievijne Jans"]), valueFor(byName["Pauwels van Haverbeke"]));
我们已经知道valueFor(byName["Pauwels van Haverbeke"])
返回1.对于另一个参数,我们有valueFor(byName["Lievijne Jans"])
。这最终将调用sharedDNA({Lievijne's: object}, valueFor(null), valueFor(null));
,因为Lievijne的父母都不在数据集中。这将评估为sharedDNA({Lievijne's: object}, 0, 0);
。
进入sharedDNA
后,我们会看到它return (fromMother + fromFather) / 2;
,在这种情况下将是return (0 + 0) / 2;
或0
。所以,最后,我们回到
return sharedDNA(pauwelsChild, valueFor(byName["Lievijne Jans"]), valueFor(byName["Pauwels van Haverbeke"]));
我们现在有
return sharedDNA(pauwelsChild, 0, 1);
我们进入共享DNA,我们将评估语句return (0 + 1) / 2;
,它将返回0.5
。这是有道理的,因为Pauwel的孩子将分享他的DNA的1/2。
基本上,sharedDNA
等待父母的回复valueFor
。但请记住,valueFor
正在调用sharedDNA
,因为这是我们传入的函数。因此,实质上,sharedDNA
等待自己评估每个父级,而父级又需要评估每个父级。这将创建一个调用树,直到它达到null(不在数据集中的家庭成员)并返回0
,或者它命中Pauwels,然后返回1
。然后,返回0
或1
,并且来自它的sharedDNA
来电将进行评估。当最外面的树“分支”开始返回0
或1
时,它们组合在一起(并除以2)。因此,来自Pauwels的1
一直被稀释1/2。因此,你的数字越来越小。
我希望这有帮助!我写了一个more thorough explanation,告诉您如何使用Chrome控制台测试代码并添加console.log
语句,以帮助您了解递归的流程。
答案 1 :(得分:0)
如果您查看sharedDNA
的功能声明,则会看到以下内容:
var ph = byName["Philibert Haverbeke"];
console.log(reduceAncestors(ph, sharedDNA, 0) / 4);
如果您追溯到reduceAncestors
,参数会映射到:
person -> { name: "Philibert Haverbeke", ... },
f -> sharedDNA,
defaultValue -> 0
现在,看看valueFor()
。它始终返回一个数字,defaultValue
或sharedDNA
的结果,它始终返回一个数字。这是关键:如果一个人没有父母,则使用defaultValue
,否则,它会生成给定关系之间的共享DNA比率,或者第三种情况下,#1;&#39;如果恰好有人命名为#Pauels van Haverbeke&#34;。
要回答你的问题,&#34; valueFor(...)如何从人物对象生成数值&#34;,它只是返回sharedDNA
递归生成的值。