对于一项家庭作业,我被要求完成此CodeStepByStep问题。它的编码正确,给了我正确的答案。我只是想知道,如果没有if语句中的大块代码,是否有一种更简单的方法来解决此问题。我必须先检查每个小写的元音,然后再检查大写的元音这一事实。

编写一个名为vowelCount的函数,该函数接受一个字符串并返回
元音的数量(a,e,字符串包含的i,o或u)。例如,对vowelCount("kookaburra")的调用应返回5(两个
o,两个a和一个u)。当传递不带任何元音的字符串(例如
为空字符串,“ 01234”或“ sky”)时,应返回0。

function vowelCount(string) {
    if(string.length == 0) { return 0; }
    let count = 0;
    for(let i = 0; i < string.length; i++) {
        let v = string[i];
        if(v == "a" || v == "e" || v == "i" || v == "o" || v == "u" ||
           v == "A" || v == "E" || v == "I" || v == "O" || v == "U") {
            count += 1;
        }
    }
    return count;
}


评论

我只想指出,虽然问题和答案都很好,但它们仅适用于英语。许多其他语言(例如法语,挪威语等)具有许多其他的元音。尽管这与作业无关紧要,但最好记住这一点,因为许多应用程序已成为全球性应用程序,而且本地化可以成为一个主题(如果您这样做的话)应该会在除美国以外的更多国家/地区使用。例如,当检查一个人的名字中允许的符号时。

#1 楼

另一种方法是使用string.replace()和正则表达式从字符串中除去元音以外的所有内容。然后计算结果字符串的长度。这样可以完全避免迭代。




 const vowelCount = s => s.replace(/[^aeiou]/gi, '').length

console.log(vowelCount('The quick brown fox jumps over the lazy dog')) 





正则表达式为/[^aeiou]/gi,这意味着匹配集合(^)中未匹配的所有内容(aeiou),全局匹配(g标志),而不考虑大小写(i标志)。然后,string.replace()使用此模式将所有匹配的字符替换为空白字符串。剩下的就是你的元音。

评论


\ $ \ begingroup \ $
这完全避免了迭代-好吧,只是想指出一点,我相信replace()方法会在内部进行自己的迭代。 (不过,我确实认为您的观点是正确的。)
\ $ \ endgroup \ $
– Marc.2377
19年5月22日在2:02



\ $ \ begingroup \ $
我想知道为什么不只是vowelCount = str => str.match(/ [aeiou] / ig).length?
\ $ \ endgroup \ $
– morbusg
20年8月28日在18:32

#2 楼


我只是想知道,如果没有if语句中没有大块代码的情况,是否有一种更简单的方法来解决此问题。


那么,您可以将所有这些都放在一个数组:

const vowels = ['A', 'E', 'I', 'O', 'U', 'a', 'e', 'i', 'o', 'u'];


,然后可以使用if简化Array.prototype.includes()的条件:

if( vowels.includes(v)) {



我必须先检查每个小写的元音,然后再检查大写的元音这一事实。


还可以包含大写或小写字母,然后调用String.prototype.toUpperCase()String.prototype.toLowerCase()如果性能是您的目标,那么可能需要考虑额外的函数调用。


此外,可以使用for...of循环代替常规的for循环,以避免索引进入数组。

for( const v of string) {


后缀增量运算符(即++)可用于增加count而不是添加1。

检查零长度可以删除字符串,因为不会运行循环。




 const vowels = ['A', 'E', 'I', 'O', 'U'];
function vowelCount(string) {
    let count = 0;
    for( const v of string) {
        if( vowels.includes(v.toUpperCase())) {
            count++;
        }
    }
    return count;
}

console.log(vowelCount('kookaburra'));

console.log(vowelCount('sky')); 






以下是一些高级技术,许多初学者/中级学生都不希望使用这些技术。如果您确实想缩短此代码,则可以使用传播运算符将字符串转换为数组,然后对Array.prototype.reduce()使用数组归约:




 const vowels = ['A', 'E', 'I', 'O', 'U'];
const charInVowels = c => vowels.includes(c.toUpperCase());
const checkChar = (count, c) => charInVowels(c) ? ++count : count; 
const vowelCount = string => [...string].reduce(checkChar, 0);

console.log(vowelCount('kookaburra'));

console.log(vowelCount('sky')); 





-

P.s.d.您是否故意将关于主题外帖子的自动评论作为您的个人资料正文?

评论


\ $ \ begingroup \ $
另外,if(string.length == 0){返回0; }是不必要的。通过空字符串循环只会发生零次,并且无论如何都会返回零。
\ $ \ endgroup \ $
– JollyJoker
19年5月21日在7:32

\ $ \ begingroup \ $
我知道这个答案是正确的,但是作为一项家庭作业,如果学生的水平使得在一个字符串中计算元音有一定难度,作为老师,如果有人提交了代码,我将非常怀疑带过滤器或减少
\ $ \ endgroup \ $
–ChatterOne
19年5月21日在11:05

\ $ \ begingroup \ $
@JollyJoker-是的,我确实考虑过建议OP将其删除;我继续并添加了这一点。
\ $ \ endgroup \ $
–SᴀᴍOnᴇᴌᴀ
19年5月21日15:16

\ $ \ begingroup \ $
@ChatterOne如果有人提交的代码中有其他答案之一建议的代码,您也不会很怀疑吗?我为最后一段添加了序言。
\ $ \ endgroup \ $
–SᴀᴍOnᴇᴌᴀ
19年5月21日在15:17

\ $ \ begingroup \ $
@SᴀᴍOnᴇᴌᴀ老实说,是的,但这走了某种“取决于人”的道路。如果学生可以清楚地表明他们了解它的工作原理,甚至知道它是从互联网复制粘贴的,也许老师会接受它。
\ $ \ endgroup \ $
–ChatterOne
19年5月22日在6:55

#3 楼

SᴀᴍOnᴇᴌᴀ答案对于小字符串来说是正确的主意,但是可以通过使用Set保留元音而不是数组来改进。这样可以减少Array.includes的开销,该开销将迭代元音数组中的每个字符以查找不匹配的字符

您可以将集合创建为const vowels = new Set([..."AEIOUaeiou"]);

要封装常数vowels,请使用函数将vowels的作用域扩展到全局作用域之外,并使其闭合以使其对函数可用。

const countVowels = (() => {
    const VOWELS = new Set([..."AEIOUaeiou"]);
    return function(str) {
         var count = 0;
         for (const c of str) { count += VOWELS.has(c) }
         return count;
    }
})();

/> UNICODE元音

当然,第一个片段更好,因为它是\ $ O(1)\ $存储,并且它使用Set(哈希表)它是\ $ O(n) \ $复杂度(其中\ $ n \ $是字符串的长度)而不是\ $ O(n * m)\ $(其中\ $ m \ $是元音的数量)。如果要包含完整的unicode元音,这将变得尤为重要。

const countVowels = (() => {
    const VOWELS = new Set([..."AEIOUaeiou"]);
    return str => [...str].reduce((count, c) => count += VOWELS.has(c), 0);
})();


注意以上代码段不起作用。请参见下文。

请勿拆分unicode

如果您使用的是Unicode,请务必声明每个unicode 16位字符并不总是代表单个视觉字符。

例如最后两个元音“ɪ̈ʊ̈”需要显示两个字符。例如,字符串"\u026A\u0308\u028A\u0308" === "ɪ̈ʊ̈"为true。您不能只计算它们
,因为表达式"ɪ̈ʊ̈".split("").length的结果将为4。

这甚至更成问题,因为\u026A\u028A的第一个字符代码也是元音"ɪʊ"

要解决完整的元音问题并将复杂度保持在\ $ O(n)\ $和存储在\ $ O(1)\ $,我们可以使用3套

const countVowels = (() => {
    // Reference https://en.wikipedia.org/wiki/Phonetic_symbols_in_Unicode
    const VOWELS = new Set([..."AEIOUaeiouiyɨʉiyɯuɪʏeøɘɵɤoɛœɜɞʌɔaɶɑɒʊəɐæɪ̈ʊ̈IYƗɄIYƜUꞮʏEØɘƟɤOƐŒꞫɞɅƆAɶⱭⱰƱƏⱯÆꞮ̈Ʊ̈"]);
    return function(str) {
         var count = 0;
         for (const c of str) { count += VOWELS.has(c) }
         return count;
    }
})();


评论


\ $ \ begingroup \ $
我正要写一个新答案,但这只是对您的一个小修改。而不是减少,我会使用.filter(c => VOWELS.has(c))。length
\ $ \ endgroup \ $
– JollyJoker
19年5月21日在7:27

\ $ \ begingroup \ $
@JollyJoker我的第一个示例是最好的,因为在使用任何数组方法时它都是O(1)存储,要求将字符串转换为一个表示存储为O(n)的数组,此外,使用Array.filter进行计数意味着最坏的情况会使内存使用量增加一倍
\ $ \ endgroup \ $
– Blindman67
19年5月21日在9:28

\ $ \ begingroup \ $
尽管您说的是正确的,但很高兴指出可以使用更好的数据结构,但是在这种情况下,数组实际上不是O(n * m),因为m是常数(不会改变) (带有输入),并且不会影响Big Oh提供的渐近分析。
\ $ \ endgroup \ $
–莱文
20年8月26日在22:57

\ $ \ begingroup \ $
@Levon是的,我同意对于固定编码的元音数量是恒定的。但是要怪说。元音的数量取决于所使用的字符编码(任何不断发展的集合(ASCII,(ISO 8859 1-16),(unicode(UTF 8,16,32))),仅举几例))因此,在不确定的元音表示集合中,定义n时应考虑m。就复杂度O(n * m)== O(n)而言,我没有改变
\ $ \ endgroup \ $
– Blindman67
20年8月27日在3:01

\ $ \ begingroup \ $
@ Blindman67很有道理。初学者将可以从明确编写的内容中受益:)拥有一个好的!
\ $ \ endgroup \ $
–莱文
20年8月28日在14:59

#4 楼

也许存在这样一个风险,即要求您对元音进行计数的同一机构很快就会要求您对辅音进行计数,只是为了让您了解代码的灵活性。

所以从一个带有两个参数的函数开始可能是一个好主意:


被测字符串和
接受的集合元音。

像下面的代码片段中的countCharsFromVowelSet()函数一样。

请注意,确定什么是可接受的元音完全取决于语言和国家/地区。

const countCharsFromVowelSet = function(str, vowelSet) {
     let arr   = [...str];
     let count = (arr.filter(c => vowelSet.includes(c))).length;
     return count;
};

/* auxiliary function builder function:  */
const makeCharCounter = function(charSet) {
    return (str => countCharsFromVowelSet(str, charSet));
};

const EnglishVowelList   = "AEIOUaeiou";
const GermanVowelList    = "AEIOUYÄÖÜaeiouyäöü";

const countEnglishVowels = makeCharCounter(EnglishVowelList);
const countGermanVowels  = makeCharCounter(GermanVowelList);

text1  = "William Shakespeare";
count1 = countEnglishVowels(text1);
text2  = "Die Schöpfung";
count2 = countGermanVowels(text2);

console.log("There are " + count1.toString() + " vowels in: " + text1);
console.log("There are " + count2.toString() + " vowels in: " + text2);


评论


\ $ \ begingroup \ $
欢迎进行代码审查,并感谢您提供答案。为什么将var用作函数内的变量,而不是let和const? Array.from(str)可以使用[... str]进行优化。并且makeCharCounter返回的函数可以简化为str => countCharsFromVowelSet(str,charSet);
\ $ \ endgroup \ $
–SᴀᴍOnᴇᴌᴀ
19年5月23日在17:37

\ $ \ begingroup \ $
@SᴀᴍOnᴇᴌᴀ:是的,您是对的,感谢您指出这一点。我比Python更熟悉Python。我的观点主要是关于体系结构和灵活性;考虑可能的规格变更。给定语言的元音集是从负责处理国际化(i18n)的源代码部分中导入的一条数据。您不需要为诸如俄语和法语之类的元音丰富的语言编写多或结构。
\ $ \ endgroup \ $
– jpmarinier
19年5月24日在13:05

#5 楼

function countVowels(str){
  let vowel_count = 0;
  str = str.toLowerCase();
  for (let i = 0; i < str.length; i++) {
      if (str.charAt(i) === 'a' || str.charAt(i) === 'e' || str.charAt(i) === 'i' 
          || str.charAt(i) === 'o' || str.charAt(i) === 'u'){
          vowel_count++;
      }
  }
  return vowel_count;
}
console.log(countVowels("Celebration"))


评论


\ $ \ begingroup \ $
欢迎。请解释一下您的代码如何改进现有代码
\ $ \ endgroup \ $
– Billal Begueradj
20年8月26日15:46