这段代码如何从雄辩JS确定主导的写作方向?



我正在挑选雄辩的JavaScript,这个高阶函数练习的答案让我难住了:

function characterScript(code) {
for (let script of SCRIPTS) {
if (script.ranges.some(([from, to]) => {
return code >= from && code < to;
})) {
return script;
}
}
return null;
}
// takes a test function and tells you whether that function
// returns true for any of the elements in the array
function countBy(items, groupName) {
let counts = [];
for (let item of items) {
let name = groupName(item);
let known = counts.findIndex(c => c.name == name);
if (known == -1) {
counts.push({name, count: 1});
} else {
counts[known].count++;
}
}
return counts;
}
// returns an array of objects, each of which names a group
// and tells you the number of elements that were found in that group
function dominantDirection(text) {
let scripts = countBy(text, char => {
let script = characterScript(char.codePointAt(0));
return script ? script.direction : "none";
}).filter(({name}) => name != "none");

if (scripts.length == 0) return "ltr";

return scripts.reduce((a, b) => a.count > b.count ? a : b).name;
}
console.log(dominantDirection("Hello!"));
// → ltr
console.log(dominantDirection("Hey, مساء الخير"));
// → rtl 

这段代码返回一个大型数据集中占主导地位的写入方向,如下所示:

[
{
name: "Coptic",
ranges: [[994, 1008], [11392, 11508], [11513, 11520]],
direction: "ltr",
year: -200,
living: false,
link: "https://en.wikipedia.org/wiki/Coptic_alphabet"
},
// …
]

我理解如何使用some方法的循环来查找字符代码返回true的任何数组。我无法理解countBy函数或dominantDirection函数是如何导致底部显示的结果的。

这两个函数的分解以及它们如何导致正确的结果将非常感谢!

检查一些中间结果会更容易理解。添加console.log以查看scripts返回的内容,删除.name以查看reduce调用的结果:

function dominantDirection(text) {
const scripts = countBy(text, (char) => {
const script = characterScript(char.codePointAt(0));

return (script
? script.direction
: "none"
);
})
.filter(({name}) => name !== "none");

if(scripts.length === 0){
return "ltr";
}

console.log(scripts); // What is the result of the `countBy` function?

return scripts.reduce((a, b) => (a.count > b.count
? a
: b)); // What is the object that the `name` property comes from?
}

现在dominantDirection("Hello!")scripts记录为

[
{ name: "ltr", count: 5 }
]

结果也是

{ name: "ltr", count: 5 }

dominantDirection("Hey, مساء الخير")将记录scripts

[
{ name: "ltr", count: 3 },
{ name: "rtl", count: 9 }
]

显示结果

{ name: "rtl", count: 9 }

scripts数组来自countBy调用,它返回字符串中每个脚本方向有多少代码点的计数。它试图通过比较codePoint属于哪个ranges并获得相应的direction属性,从每个SCRIPTS中找到相应的脚本。

这个高阶函数countBy接受参数itemsgroupNamedominantDirection用两个参数调用countBy,并将结果存储在scripts中。

  • items是一个可迭代的值,在这种情况下是一个字符串(代码点):这只是输入字符串,例如"Hey, مساء الخير"。从这个值出发,单独的项目(代码点)将被分组到"桶"中并单独计数。
  • groupName是一个返回单个代码点(例如一个字符)所属的"桶"名称的函数(基于代码点本身):在这种情况下,它是箭头函数char => {},它调用characterScript与单个char的代码点并返回相应的脚本对象(你说你理解)。然后它得到脚本的direction,例如"ltr"{ name: "Coptic",}对象从你的例子(或"none"如果没有脚本对象可以找到)。

顺便说一句,groupName不是一个好名字,因为它期望是一个函数,但是这个名字暗示了一个字符串。也许groupNameFromItem更好。

countBy在字符串(for (let item of items))上迭代时,这个函数(最初是char => {})被调用并赋值给name(let name = groupName(item);)。由于char => {}返回脚本的direction,name变成"ltr","rtl""none"-这是"桶"的名称。数组counts{ name: "ltr", count: 1 }这样的对象填充。如果下一个代码点也来自ltr脚本,则使用findIndex找到该对象,并使用++增加其count

然后返回这个填充的数组(scriptsdominantDirection内部引用的数组)。

reduce很容易解释:ab是来自scripts数组之一的对象。如果a.count大于b.count,则返回a,否则返回b;然后将返回的对象用于下一次比较,或者,如果不需要比较其他对象,则作为结果返回。因此,reduce调用查找具有最大count的对象。在原始代码中,最后只返回name,而不是整个对象。


总结:

text是一个字符串,由来自不同脚本的代码点组成。countBy接受text,迭代代码点,调用groupName以获得当前代码点的"桶名",用{ name, count }条目填充counts数组(名为scripts,在函数之外),这些条目告诉您count许多代码点来自name方向的脚本。然后一个reduce在这些表项中查找最大的count,并返回它的name


还有两点:

  • 我理解如何使用some方法的循环来查找字符代码返回true的任何数组。

    字符代码本身不返回true。如果代码点code落在from(包含)和to(不包含)之间的任何一个范围内,则some调用返回true,否则返回false

  • 本章是关于高阶函数的,所以理解function countBy(items, groupName){}中的groupName参数是如何工作的很重要。我不太确定你对这个概念有多熟悉,但这里有一个更简单的例子,其中奇数和偶数被计数,并附有一些解释性注释:

    const countOddAndEvenNumbers = (iterable) => {
    const oddOrEvenBucketFromNumber = (number) => (number % 2 === 0 ? "even" : "odd"); // This is the function that distinguishes odd and even numbers.
    return countGroups(iterable, oddOrEvenBucketFromNumber); // The distinguishing function is passed to `countGroups` to be used.
    },
    countGroups = (iterable, bucketNameFromItem) => {
    const result = {}; // Usually counting is done with hash maps, e.g. objects or Maps, instead of arrays.
    for(let item of iterable){
    const bucketName = bucketNameFromItem(item); // Generic way of `const bucketName = (item % 2 === 0 ? "even" : "odd")`; acts as `const bucketName = oddOrEvenBucketFromNumber(item)`, but with no own knowledge of what odd or even numbers are: it’s entirely separated and knows nothing about the implementation of the function.
    result[bucketName] = (result[bucketName] ?? 0) + 1; // Increment entry `bucketName` by one. If it doesn’t exist, initialize it to `0` first.
    }
    return result;
    };
    countOddAndEvenNumbers([0, 1, 1, 2, 3, 5, 8, 13]); // { "even": 3, "odd": 5 }
    

最新更新