Jathon's blog

也无风雨也无晴

leetcode 165.版本号比对

发布于 # leetcode

版本号比对

这里讨论的是一些“不正经”的解法,

优雅的函数式风格

const compareVersions = (version1, version2) => {
  const parseVersion = (version) => version.split('.').map(Number);
  const [v1Parts, v2Parts] = [version1, version2].map(parseVersion);
  const maxLength = Math.max(v1Parts.length, v2Parts.length);

  const result = Array.from({ length: maxLength })
    .map((_, i) => (v1Parts[i] || 0) - (v2Parts[i] || 0))
    .find(diff => diff !== 0);

  return Math.sign(result || 0);
};

我们首先定义了一个函数parseVersion来解析版本号字符串并将其转换为数字数组。然后,我们使用map方法来创建两个版本号的数组。接下来,我们创建一个长度等于两个版本号中最长的那个的数组,并使用map方法计算每个位置上的版本号差异。最后,我们使用find方法来找到第一个非零的差异值。如果找到了差异值,我们使用Math.sign来确定其符号并返回;如果没有差异值(即所有差异值都为零),那么我们返回0。

迭代器

const compareVersions = (version1, version2) => {
  const normalizeVersion = (version, maxLength) =>
    version.split('.')
           .map(Number)
           .concat(Array(maxLength).fill(0));

  const maxLength = Math.max(version1.split('.').length, version2.split('.').length);
  const [v1, v2] = [version1, version2].map(v => normalizeVersion(v, maxLength));

  let comparisonResult = 0;
  
  v1.some((num, idx) => {
    if (num !== v2[idx]) {
      comparisonResult = Math.sign(num - v2[idx]);
      return true; // Stop the iteration as we found a difference
    }
    return false; // Continue iteration
  });
  
  return comparisonResult;
};

使用 some 方法来迭代版本号的每个部分。一旦发现两个版本号在某个位置不相等,我们就通过 Math.sign 函数计算比较结果并立即停止迭代。这意味着如果我们在比较的过程中找到了结果,我们就不会继续进行无用的比较。这种方式是高效的,特别是对于长版本号字符串和早期就可以确定结果的情况。

整活 函数式风格Point-free(又称为 tacit 编程)

const parseVersion = version => version.split('.').map(Number);
const fillZeros = length => Array.from({ length }, () => 0);
const zipWithDefault = (defaultVal, arr1, arr2) => 
  arr1.map((val, idx) => [val, arr2[idx] || defaultVal]);

const diffOrZero = ([num1, num2]) => num1 - num2;

const compareArrays = (arr1, arr2) => {
  const maxLength = Math.max(arr1.length, arr2.length);
  const combined = zipWithDefault(0, [...arr1, ...fillZeros(maxLength - arr1.length)], arr2);
  const result = combined.map(diffOrZero).find(diff => diff !== 0);
  return Math.sign(result || 0);
};

const compareVersions = (version1, version2) => 
  compareArrays(parseVersion(version1), parseVersion(version2));

我们定义了几个辅助函数 parseVersion、fillZeros、zipWithDefault 和 diffOrZero。这些函数都是高阶函数,它们不涉及具体的参数值,而是返回操作这些参数的函数。然后我们使用这些函数来构建 compareVersions 函数,这种方式更符合 point-free 风格。

注意,虽然 point-free 风格看起来很有趣且可以使代码更加简洁,但它并不总是最佳选择。过度使用 point-free 风格可能会使代码难以理解,特别是对于不熟悉这种风格的开发者来说。因此,在实际编程中,应该根据情况权衡代码的可读性和简洁性。