首页 > 编程语言 >Yargs里的Levenshtein距离算法

Yargs里的Levenshtein距离算法

时间:2024-09-18 20:36:14浏览次数:7  
标签:-- argv alias 算法 Levenshtein 字符串 Yargs yargs

“Yargs 是一个 Node.js 库,专为那些想要解析命令行选项字符串的开发者设计。”

yargs介绍

yargs 是一个用于解析命令行参数的流行库,周下载量达到了惊人的93154k,它能帮助开发者轻松地定义 CLI(命令行接口),并提供参数处理、命令组织、help文本自动生成等功能。

它通过简洁的 API 使复杂的命令行应用开发变得更加直观。

官网地址:https://yargs.js.org/

yargs 提供了丰富的功能支持,如:

  • 自动生成帮助和版本信息
  • 参数类型验证
  • 别名处理
  • 子命令支持

使用案例

自动生成帮助和版本信息

假设你正在编写一个命令行工具来处理文件,以下是如何使用 yargs 来解析命令行参数的示例:

背景:你需要创建一个工具,接受 --input 和 --output 选项,并支持一个 --verbose 标志。

const yargs = require('yargs');

const argv = yargs
  .option('input', {
    alias: 'i',
    description: 'Input file path',
    type: 'string',
    demandOption: true
  })
  .option('output', {
    alias: 'o',
    description: 'Output file path',
    type: 'string',
    demandOption: true
  })
  .option('verbose', {
    description: 'Run with verbose logging',
    type: 'boolean',
    default: false
  })
  .help()
  .alias('help', 'h')
  .argv;

console.log(argv);

输出如下:

可以看出来,通过这个工具可以很方便地获得一个cmd的帮助提示。

参数类型验证

yargs 允许你为命令行参数设置类型,并进行验证。这可以确保用户输入符合预期的格式,从而提高程序的健壮性。

示例:假设我们需要一个工具来处理文件,--port 选项应为一个数字,--debug 选项应为布尔值。

const yargs = require('yargs');

const argv = yargs
  .option('port', {
    alias: 'p',
    description: 'Port number for the server',
    type: 'number',
    demandOption: true,
    coerce: (arg) => {
      if (isNaN(arg)) throw new Error('Port must be a number');
      return arg;
    }
  })
  .option('debug', {
    description: 'Enable debugging',
    type: 'boolean',
    default: false
  })
  .help()
  .alias('help', 'h')
  .argv;

console.log(argv);

解析

  • type: 'number' 指定 --port 选项必须是一个数字。
  • coerce 函数进一步验证输入,确保它是有效的数字。如果用户输入的不是数字,会抛出错误。

别名处理

yargs 支持为参数设置别名,使得命令行选项更具灵活性和可读性。

我们可以为 --input--output 选项设置别名。

const yargs = require('yargs');

const argv = yargs
  .option('input', {
    alias: 'i',
    description: 'Input file path',
    type: 'string',
    demandOption: true
  })
  .option('output', {
    alias: 'o',
    description: 'Output file path',
    type: 'string',
    demandOption: true
  })
  .help()
  .alias('help', 'h')
  .argv;

console.log(argv);

解析

  • alias: 'i'--input 设置了 -i 的别名。
  • alias: 'o'--output 设置了 -o 的别名。

这样,用户可以使用 -i-o 代替 --input--output,提高了命令行的便捷性。

子命令支持

yargs 支持定义多个子命令,每个子命令可以有自己独立的选项和参数。这使得创建复杂的命令行工具变得更加简单。

创建一个命令行工具,支持两个子命令 addremove,每个子命令有不同的选项。

const yargs = require('yargs');

yargs
  .command('add [name]', 'Add a new item', (yargs) => {
    yargs
      .positional('name', {
        describe: 'Name of the item to add',
        type: 'string'
      })
      .option('quantity', {
        alias: 'q',
        description: 'Quantity of the item',
        type: 'number',
        demandOption: true
      });
  })
  .command('remove [name]', 'Remove an item', (yargs) => {
    yargs
      .positional('name', {
        describe: 'Name of the item to remove',
        type: 'string'
      });
  })
  .help()
  .alias('help', 'h')
  .argv;

解析

  • add [name]remove [name] 是两个子命令。
  • add 命令要求 name 参数,并且支持 --quantity 选项。
  • remove 命令要求 name 参数。

这样,用户可以使用以下命令:

  • node script.js add item --quantity 10
  • node script.js remove item

Yargs里的Levenshtein距离算法

命令修正是指用户输入错误的命令时,会给出有效的提示。例如,用户输入了 lis,但正确的命令是 list。启用 recommendCommands 后,yargs 会提示类似的命令并推荐使用 list。

const yargs = require('yargs');

// 定义命令
yargs
  .command('list', 'List all items', () => {}, (argv) => {
    console.log('Listing items');
  })
  .command('add', 'Add a new item', () => {}, (argv) => {
    console.log('Adding a new item');
  })
  .recommendCommands() // 启用推荐功能
  .help()
  .argv;

这是如何实现的呢?其内部使用了一个叫做Levenshtein的算法,一种计算两个字符串之间编辑距离(也称为 Levenshtein 距离)的动态规划算法。编辑距离表示从一个字符串转换到另一个字符串所需的最小操作次数。

这些操作包括以下三种:

  • 插入(Insertion):在一个字符串中插入一个字符。
  • 删除(Deletion):删除一个字符串中的字符。
  • 替换(Substitution):将一个字符替换为另一个字符。

Levenshtein算法是由苏联科学家 Vladimir Levenshtein 于 1965年 提出的。他是一名数学家和信息学家,他的研究领域包括信息论、纠错编码和组合数学。编辑距离的概念最初源于信息论中的编码纠错,Levenshtein 的目标是找到一种方法来衡量两个字符串之间的差异,以此用于分析不同数据序列的相似性和错误修复策略。

在提出 Levenshtein 算法的年代,计算机科学刚刚起步,信息理论和编码理论等领域的研究对数据传输中的错误检测和纠正有着重要影响。Levenshtein 距离的提出为编码理论提供了新的工具,可以量化两个符号序列(如文本或DNA序列)之间的差异。

该算法当前常用来处理如下场景:

  • 拼写检查:在文本编辑器和搜索引擎中,Levenshtein 距离被用来纠正用户的拼写错误。
  • 自然语言处理:用于衡量文本之间的相似度,例如判断两个句子或单词的差异程度。
  • 生物信息学:Levenshtein距离也被应用于比较DNA序列或蛋白质序列,查看它们在进化或功能上的相似性。
export function levenshtein(a: string, b: string) {
  // 如果 a 是空字符串,则返回 b 的长度,意味着需要对 b 进行 b.length 次的插入操作才能变为 a。
  if (a.length === 0) return b.length;
  // 如果 b 是空字符串,同理,返回 a.length,即删除 a.length 次。
  if (b.length === 0) return a.length;

  // 创建一个矩阵,用来存储两个字符串在不同位置的编辑距离。矩阵的每个单元格 matrix[i][j] 代表 a 的前 j 个字符与 b 的前 i 个字符的编辑距离。
  const matrix = [];

  // 初始化矩阵的第一列,每个值都等于当前行号 i。这是因为将一个空字符串变为另一个字符串的操作次数等于该字符串的长度,即多次插入。
  let i;
  for (i = 0; i <= b.length; i++) {
    matrix[i] = [i];
  }

  // 初始化矩阵的第一行,每个值都等于当前列号 j。将 a 变为空字符串需要 a.length 次删除操作。
  let j;
  for (j = 0; j <= a.length; j++) {
    matrix[0][j] = j;
  }

  // 从第二行和第二列开始,遍历每个矩阵单元,计算两个字符串在该位置的最小编辑距离。
  for (i = 1; i <= b.length; i++) {
    for (j = 1; j <= a.length; j++) {
      // 如果 a 和 b 当前的字符相等,则它们之间没有操作,编辑距离等于上一个单元格 matrix[i - 1][j - 1] 的值。
      if (b.charAt(i - 1) === a.charAt(j - 1)) {
        matrix[i][j] = matrix[i - 1][j - 1];
      } else {
        // 如果当前字符不相等,检查是否可以通过字符调换(transposition)来匹配。调换的前提是当前字符和前一个字符可以相互交换,即 b 的第 i-1 个字符等于 a 的第 j-2 个字符,b 的第 i-2 个字符等于 a 的第 j-1 个字符。如果可以调换,编辑距离加 1。
        if (
          i > 1 &&
          j > 1 &&
          b.charAt(i - 2) === a.charAt(j - 1) &&
          b.charAt(i - 1) === a.charAt(j - 2)
        ) {
          matrix[i][j] = matrix[i - 2][j - 2] + 1; // transposition
        } else {
          // 如果不能调换,计算三个可能的编辑操作中的最小值:
          // 替换(substitution):将 a 的第 j 个字符替换为 b 的第 i 个字符。
          // 插入(insertion):在 a 中插入一个字符,使其匹配 b 的当前字符。
          // 删除(deletion):从 a 中删除一个字符。
          matrix[i][j] = Math.min(
            matrix[i - 1][j - 1] + 1, // substitution
            Math.min(
              matrix[i][j - 1] + 1, // insertion
              matrix[i - 1][j] + 1
            )
          ); // deletion
        }
      }
    }
  }
  // 最终返回矩阵右下角的值,即两个字符串 a 和 b 的 Levenshtein 距离
  return matrix[b.length][a.length];
}

这个算法通过构建一个矩阵并逐步填充其值,找到了最小的编辑操作次数来将一个字符串转换为另一个。它有效地处理了字符串比较问题,广泛用于拼写检查、DNA 序列比对等领域。

  • 时间复杂度:Levenshtein 算法的时间复杂度为 O(m * n),其中 m 和 n 分别是字符串 a 和 b 的长度。因为它需要遍历每个字符,并计算所有可能的操作。
  • 空间复杂度:由于使用了一个 m x n 的矩阵,空间复杂度也是 O(m * n)。

在执行recommendCommands的时候,实际的操作如下:

 self.recommendCommands = function recommendCommands(cmd, potentialCommands) {
     // 设置了一个阈值为 3,表示如果某个候选命令与输入命令的编辑距离超过 3,则认为它与输入命令差异太大,不再考虑。
    const threshold = 3;
    // 将所有候选命令按长度从大到小排序,增加命令建议的精确性
    potentialCommands = potentialCommands.sort((a, b) => b.length - a.length);
    // recommended 用于存储找到的最佳推荐命令,bestDistance 用于记录当前找到的最小编辑距离,初始为 Infinity。
    let recommended = null;
    let bestDistance = Infinity;
    // 遍历所有 potentialCommands 并计算编辑距离
    for (
      let i = 0, candidate;
      (candidate = potentialCommands[i]) !== undefined;
      i++
    ) {
      const d = distance(cmd, candidate);
      // 如果编辑距离 d 小于或等于 threshold 且比当前最小距离 bestDistance 小,则更新 bestDistance 和 recommended。
      if (d <= threshold && d < bestDistance) {
        bestDistance = d;
        recommended = candidate;
      }
    }
    if (recommended) usage.fail(__('Did you mean %s?', recommended));
  };

总结

1965年提出的算法,历经数十年仍在现代技术中发挥着重要作用,真是太厉害了。看完你还会觉得算法没用么?算法通常具备简单性与普适性,这才是它的持久价值来源。

不说了,好好阅读,好好思考,才能好好写代码。

本文由mdnice多平台发布

标签:--,argv,alias,算法,Levenshtein,字符串,Yargs,yargs
From: https://www.cnblogs.com/miniwa/p/18419288

相关文章

  • 地平线占用预测 FlashOcc 参考算法-V1.0
    1.简介3DOccupancyNetworks的基本思路是将三维空间划分成体素网格,并对每个网格进行各类感知任务的预测。目前以网格为中心的方法能够预测每个网格单元的占用率、语义类别、未来运动位移和实例信息。3Doccupancy可以对道路障碍物进行更细粒度的划分,同时获取更精确的占用和语......
  • 代码随想录算法 - 回溯算法1
    题目177.组合给定两个整数n和k,返回范围[1,n]中所有可能的k个数的组合。你可以按任何顺序返回答案。示例1:输入:n=4,k=2输出:[[2,4],[3,4],[2,3],[1,2],[1,3],[1,4],]示例2:输入:n=1,k=1输出:[[1]]提示:1<=n<=201<=k<=......
  • 深入理解算法效率:时间复杂度与空间复杂度
    目录引言一、算法效率的基础二、时间复杂度1.概念2.常见类型1.O(1)—常数阶 2.O(n)—线性阶3.O(n^2)—平方阶4.O(2^......
  • 数据挖掘实战-基于朴素贝叶斯算法构建真假新闻分类模型
     ......
  • 代码随想录算法训练营,9月18日 | 77.组合,216.组合总和III,17.电话号码的字母组合
    回溯算法理论基础:1.回溯是递归的副产品,有递归就有回溯。2.回溯的本质是穷举,想让回溯法高效些,可以加一些剪枝的操作3.组合问题:N个数里面按一定规则找出k个数的集合切割问题:一个字符串按一定规则有几种切割方式子集问题:一个N个数的集合里有多少符合条件的子集排列问题:N个数按......
  • 算法学习每日一题之2332. 坐上公交的最晚时间:二分答案 & 贪心双指针
    Problem:2332.坐上公交的最晚时间人话题意:你是一个懒惰的人,虽然你要赶公交车,但你想多睡会,恰好你知道每辆车的发车时间buses和每辆车容capacity,和每个乘客乘车的时间passenger,旨在求可以赶上公交车的最晚出发时间。思路一:二分答案求最晚能满足赶上公交的时间,可以发现......
  • 算法面试总结-传统图像算法
    目录1.说说相机标定?2.说说图像的边缘是什么?3.说说边缘检测的任务以及基本原理4.说说Canny边缘检测算子?5.说说除Canny外还知道什么边缘检测算子?6.说说霍夫变换步骤?7.说说仿射变换?8.说说透视变换?9.说说最小二乘法?10.说说SIFT算子以及有什么特点?11.说说SIFT特征提取与匹配算......
  • 算法笔记2:二分
    二分二分可以求得满足条件的左边界或右边界,如下图所示查找左边界(绿色区域的最左边):intSL(intl,intr){while(l<r){intmid=l+r>>1;if(check(mid))r=mid;elsel=mid+1;}re......
  • 多输入多输出 | Matlab实现SMA-BP黏菌算法优化BP神经网络多输入多输出预测
    多输入多输出|Matlab实现SMA-BP黏菌算法优化BP神经网络多输入多输出预测目录多输入多输出|Matlab实现SMA-BP黏菌算法优化BP神经网络多输入多输出预测预测效果基本介绍程序设计往期精彩参考资料预测效果基本介绍多输入多输出|Matlab实现SMA-BP黏菌算法优......
  • 2024年JCR一区极光优化算法+分解对比!VMD-PLO-Transformer-BiLSTM多变量时间序列光伏功
    中秋献礼!2024年中科院一区极光优化算法+分解对比!VMD-PLO-Transformer-LSTM多变量时间序列光伏功率预测目录中秋献礼!2024年中科院一区极光优化算法+分解对比!VMD-PLO-Transformer-LSTM多变量时间序列光伏功率预测效果一览基本介绍程序设计参考资料效果一览......