曾-..曾曾示例 - 雄辩的 JS

Great-great-great-great-... Example - Eloquent JS

本文关键字:JS 曾曾示      更新时间:2023-09-26

我正在为"曾曾-...曾示例来自 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"}

在声明 sharedDNA 之后,您将看到:

var ph = byName["Philibert Haverbeke"];
console.log(reduceAncestors(ph, sharedDNA, 0) / 4);

byName是一个充满键:值对的对象,就像你为 Carolus 编写的那样。键是人的名字,对应的值是"人对象",它具有键:值对,分别是"姓名"、"母亲"、"父亲"等。这很有用,因为对于每个递归循环,我们需要找出母亲和父亲的结果,因此我们可以轻松地查找当前人的母亲和父亲的名字,然后从 byName 对象访问他们的 person 对象。所以排队

var ph = byName["Philibert Haverbeke"];

我们为菲利伯特提取这个 Person 对象并将其存储在一个名为 ph 的变量中。然后,使用此 person 对象、sharedDNA 函数和0 defaultValue调用reduceAncestors(稍后会详细介绍(。我们除以 4,因为菲利伯特是作者的祖父。所以,这个函数会返回祖父和伟大、伟大、伟大、伟大......爷爷。为了找到作者的共同DNA,我们需要将他祖父的结果除以4(每一代将共享DNA减半(。

为了更容易理解这个例子,我发现尝试找到一些更简单的例子的sharedDNA很有帮助。首先,让我们试着找到Pauwels van Haverbeke的共同DNA,伟大的,伟大的,伟大的,伟大的......祖父我们首先用来比较。所以我们知道答案:鲍威尔斯分享了他自己100%的DNA。

因此,在本例中,将使用 Pauwers 的 person 对象、sharedDNA 函数和使用以下代码0 defaultValue调用reduceAncestors

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));

person.motherperson.father表达式的计算结果为 null,因为 Pauwels的母亲和父亲不在数据集中。 sharedDNA想要调用,但需要等待两个valueFor(null)调用进行评估。

valueFornull调用时,它会进入if子句,因为person === null为真。然后,它将return defaultValue ,您还记得,我们在最初的reduceAncestors调用中0传入。

所以,现在上面的一行变成了

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的孩子将共享他1/2的DNA。

基本上,sharedDNA等待valueFor的电话,让母亲和父亲回来。但请记住,valueFor调用sharedDNA因为这是我们传入的函数。因此,从本质上讲,sharedDNA等待自己评估每个父母,而父母又需要评估每个父母。这将创建一个调用树,直到它命中 null(不在数据集中的族成员(并返回 0 ,或者它命中 Pauwels,并返回 1 。然后,返回01,并且它来自的sharedDNA调用将进行评估。当最外层的树"分支"开始返回01时,它们组合在一起(并除以2(。因此,来自鲍威尔斯的1不断被稀释 1/2。因此,您的数字越来越小。

我希望这有帮助!我写了一个更详尽的解释,向您展示如何使用 Chrome 控制台来测试代码并添加console.log语句来帮助您理解递归流程。

如果你看一下 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",如果这个人碰巧被命名为"Pauels van Haverbeke"。

回答您的问题,"如何为(...从 Person 对象生成一个数值",它只是返回sharedDNA递归生成的值。