伟大伟大的伟大-...示例 - 雄辩的JS

时间:2014-12-08 20:09:19

标签: javascript

我正在努力与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"}

2 个答案:

答案 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.motherperson.father表达式将评估为nullsharedDNA想要调用,但需要等待两个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。然后,返回01,并且来自它的sharedDNA来电将进行评估。当最外面的树“分支”开始返回01时,它们组合在一起(并除以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()。它始终返回一个数字,defaultValuesharedDNA的结果,它始终返回一个数字。这是关键:如果一个人没有父母,则使用defaultValue,否则,它会生成给定关系之间的共享DNA比率,或者第三种情况下,#1;&#39;如果恰好有人命名为#Pauels van Haverbeke&#34;。

要回答你的问题,&#34; valueFor(...)如何从人物对象生成数值&#34;,它只是返回sharedDNA递归生成的值。