是否可以对 Javascript 中的嵌套数组使用闭包和递归方法

Is it possible to use closure and recursive approach for nested arrays in Javascript

我需要计算嵌套数组中的元音,我想用闭包来完成它以避免全局命名空间污染。 这是我的代码:

let nestedArr = [
    "Elie",
    ["Matt", ["Tim"]],
    ["Colt", ["Whiskey", ["Janey"], "Tom"]],
    "Lorien"
];


function countVowels() {
    let vowelsCount = 0;
    let vowels = ['a', 'e', 'i', 'o', 'u'];
    return function foo(arr) {
        for (let i = 0; i < arr.length; i++) {
            if (typeof arr[i] === 'string') {
                for (let letter of arr[i]) {
                    if (vowels.includes(letter.toLowerCase())) {
                        vowelsCount++;
                    }
                }
            } else {
                return foo(arr[i]);
            }
        }
        return vowelsCount;
    }
}

const counter = countVowels();
console.log(counter(nestedArr));

我希望元音的数量正确,但得到 5 个。我尝试调试并发现它在最深的子数组“Tim”之后停止,所以很明显我的函数没有升级,我遗漏了一些东西。

我怎样才能做到这一点?

提前谢谢你。

您不需要嵌套函数,您可以声明一个递归函数并仍然保持所有内容独立。

const countVowels = (arr) => {
  const vowels = ['a', 'e', 'i', 'o', 'u'];
  const vowel_count = (s) => [...s].filter((c) => vowels.includes(c.toLowerCase())).length;

  let vowels_total = 0;

  for (const e of arr) {
    vowels_total += Array.isArray(e) ? countVowels(e) : vowel_count(e);
  }

  return vowels_total;
};

const nestedArr = ['Elie', ['Matt', ['Tim']], ['Colt', ['Whiskey', ['Janey'], 'Tom']], 'Lorien'];

console.log(countVowels(nestedArr));

或者,使用 Array#flat()(到 Infinity

const countVowels = (
  (v) => (arr) =>
    [...arr.flat(Infinity).join('')].filter((c) => v.includes(c.toLowerCase())).length
)(['a', 'e', 'i', 'o', 'u']);

const nestedArr = ['Elie', ['Matt', ['Tim']], ['Colt', ['Whiskey', ['Janey'], 'Tom']], 'Lorien'];

console.log(countVowels(nestedArr));

如果你只是改变你的功能就可以正常工作

return foo(arr[i]);

收件人:

foo(arr[i]);

你需要让整个循环 运行(很明显),return 让它提前停止。

let nestedArr = [
    "Elie",
    ["Matt", ["Tim"]],
    ["Colt", ["Whiskey", ["Janey"], "Tom"]],
    "Lorien"
];


function countVowels() {
    let vowelsCount = 0;
    let vowels = ['a', 'e', 'i', 'o', 'u'];
    return function foo(arr) {
        for (let i = 0; i < arr.length; i++) {
            if (typeof arr[i] === 'string') {
                for (let letter of arr[i]) {
                    if (vowels.includes(letter.toLowerCase())) {
                        vowelsCount++;
                    }
                }
            } else {
               foo(arr[i]);
            }
        }
        return vowelsCount;
    }
}

const counter = countVowels();
console.log(counter(nestedArr));

let nestedArr = [
  'Elie',
  ['Matt', ['Tim']],
  ['Colt', ['Whiskey', ['Janey'], 'Tom']],
  'Lorien',
];

function countVowels(str) {
  return str.match(/['a', 'e', 'i', 'o', 'u']/gi)?.length || 0;
}

function vowelsAmount(arr) {
  return arr.reduce((accumulator, currentElement) => {
    return Array.isArray(currentElement)
      ? (accumulator += vowelsAmount(currentElement))
      : (accumulator += countVowels(currentElement));
  }, 0);
}

console.log(vowelsAmount(nestedArr));

这听起来像是学习递归的练习。如果是这样,我在这里留给你其他很好的递归答案。

但一个简单的替代方法是注意数组的 toString 格式,并意识到我们可以简单地将我们的测试应用于其结果,留下一些非常简单的代码:

const countVowels = (a) =>
  [... a .toString () .toLowerCase ()] .filter (c => 'aeiou' .includes (c)) .length

const nestedArr = ['Elie', ['Matt', ['Tim']], ['Colt', ['Whiskey', ['Janey'], 'Tom']], 'Lorien']

console .log (countVowels (nestedArr))

递归是一种函数式继承,因此将它与函数式风格一起使用会产生最佳效果。这意味着要避免诸如突变、变量重新分配和其他副作用之类的事情。这是我们可以使用类型分析和 inductive reasoning

重写程序的一种方法

字符串

  1. 如果输入的t少于一个字符,则不算数。 Return 空和,0.
  2. (归纳法)t 至少有 1 个字符。如果第一个字符是元音,则将其转换为 1,否则转换为 0,并将其添加到子问题的结果中。

数组

  1. 如果输入t的元素少于1个,则没有什么可统计的。 Return空和,0
  2. (inductive) t 至少有一个元素。统计第一个元素的元音字母,并将其添加到子问题的结果中。

任何其他类型

  • 如果输入 t 既不是字符串也不是数组,抛出错误以通知调用者我们无法计算该输入类型的元音。

function countVowels (t) {
  switch (t?.constructor) {
    case String:
      if (t.length < 1)
        return 0                                             // 1
      else
        return isVowel(t[0]) + countVowels(t.slice(1))       // 2
    case Array:
      if (t.length < 1)
        return 0                                             // 1
      else
        return countVowels(t[0]) + countVowels(t.slice(1))   // 2
    default:
      throw Error(`cannot count input type: {t?.constructor}`)
  }
}

function isVowel (t) {
  switch (t.toLowerCase()) {
    case "a":
    case "e":
    case "i":
    case "o":
    case "u":
      return true
    default:
      return false
  }
}

const nestedArr =
  ['Elie', ['Matt', ['Tim']], ['Colt', ['Whiskey', ['Janey'], 'Tom']], 'Lorien']

console.log(countVowels(nestedArr))

14