JavaScript中的模式匹配

模式是用于转换输入数据的规则。 以将数据与一个或多个逻辑结构进行比较,将数据分解为各个构成部分,或以各种方式从数据中提取信息。

安装

JavaScript已经实现模式匹配解构功能,没有实现模式匹配过滤功能。用模式来控制程序流,可以编写更加声明性,更加模块化的代码,请安装structural-comparison以支持此功能。structural-comparison是一个函数库。包括函数式编程的常用函数。开源于GitHub,见xp44mm/structural-comparison仓库。

  1. npm i structural-comparison

用法:

  1. import { match } from 'structural-comparison'
  2. match: (pattern:string) -> (input:any) -> boolean

match是一个柯里函数,模式参数是一个字符串,输入参数可以是任何值,当匹配成功返回真,否则返回假。

示例

模式匹配分为字面量模式,类型测试模式,标识符模式,数组模式,对象模式,OR模式,以及它们的组合嵌套模式。

字面量模式

测试原子值。模式是基元值字面量,支持JSON中的所有字面量。包括null,布尔值,数字值,字符串值。字符串是JSON的双引号格式。不支持undefinedNaNInfinity等非JSON值。

  1. test("value NULL", () => {
  2. let y = match('null')
  3. expect(y(null)).toEqual(true)
  4. expect(y(3)).toEqual(false)
  5. })

这里y函数等价于:

  1. let y = input => input === null

输入对象相等(===)于模式。

其他字面量模式的示例:

  1. test("value boolean", () => {
  2. let y = match('false')
  3. let v = y(false)
  4. let w = y(3)
  5. expect(v).toEqual(true)
  6. expect(w).toEqual(false)
  7. })
  8. test("value number", () => {
  9. let y = match('123')
  10. let v = y(123)
  11. let w = y(3)
  12. expect(v).toEqual(true)
  13. expect(w).toEqual(false)
  14. })
  15. test("value quote", () => {
  16. let y = match('""')
  17. let v = y('')
  18. let w = y(3)
  19. expect(v).toEqual(true)
  20. expect(w).toEqual(false)
  21. })

类型模式

测试数据的数据类型。模式是数据类型的名称,不带双引号。包括boolean,string,number,function

  1. test("value TYPE", () => {
  2. let y = match('number')
  3. let v = y(5)
  4. let w = y(true)
  5. expect(v).toEqual(true)
  6. expect(w).toEqual(false)
  7. })

这里y函数等价于:

  1. let y = input => typeof input === 'number'

输入对象typeof值等于模式中的数据类型名称。

标识符模式

是一个合法的JavaScript标识符,除了标识符不包括$字符,但是不能是类型名称。模式中的标识符和类型名都是区分大小写的,这和JavaScript语法一致。标识符模式始终成功匹配任何一个值。

  1. test("value ID", () => {
  2. let y = match('x')
  3. let v = y(5)
  4. let w = y({})
  5. expect(v).toEqual(true)
  6. expect(w).toEqual(true)
  7. })

通配模式虽然是标识符,但实际上,是一个弃元(discard),弃元表示一个我们完全用不上的数值。仅用于占位。相同的名称不会引起名称冲突。

数组模式

匹配一个数组。数组匹配根据数组元素长度分为长度严格匹配,和最短长度匹配。严格匹配示例:

  1. test("value array", () => {
  2. let y = match('[]')
  3. let v = y([])
  4. let w = y({})
  5. expect(v).toEqual(true)
  6. expect(w).toEqual(false)
  7. })
  8. test("array elements", () => {
  9. let input = '[1]'
  10. let y = match(input)
  11. let v = y([1])
  12. let w = y([{ x: 0 }])
  13. expect(v).toEqual(true)
  14. expect(w).toEqual(false)
  15. })
  16. test("elements elements value", () => {
  17. let input = '[1, 2]'
  18. let y = match(input)
  19. let v = y([1, 2])
  20. let w = y([null, 1])
  21. expect(v).toEqual(true)
  22. expect(w).toEqual(false)
  23. })

如果有省略号表示可以匹配任何更多的数组元素。最短长度匹配:

  1. test("array ELLIPSIS", () => {
  2. let input = '[...]'
  3. let y = match(input)
  4. let v = y([1])
  5. let w = y({})
  6. expect(v).toEqual(true)
  7. expect(w).toEqual(false)
  8. })
  9. test("array elements ELLIPSIS", () => {
  10. let input = '[null, ...]'
  11. let y = match(input)
  12. let v = y([null])
  13. let w = y([null, 0])
  14. let p = y([0])
  15. expect(v).toEqual(true)
  16. expect(w).toEqual(true)
  17. expect(p).toEqual(false)
  18. })
  19. test("array ELLIPSIS elements", () => {
  20. let input = '[ ..., null]'
  21. let y = match(input)
  22. let v = y([null])
  23. let w = y([0, null])
  24. let p = y([null, 0])
  25. expect(v).toEqual(true)
  26. expect(w).toEqual(true)
  27. expect(p).toEqual(false)
  28. })
  29. test("array elements ELLIPSIS elements", () => {
  30. let y = match('[1,2,...,4,5]')
  31. let v = y([1,2,3,4,5])
  32. let w = y([1,2,3])
  33. expect(v).toEqual(true)
  34. expect(w).toEqual(false)
  35. })

数组语法不支持洞(连续逗号),不支持尾逗号。不支持迭代器。

数组模式大致编译成如下:

  1. let y = input => Array.isArray(input) && every elements matched

对象模式

匹配一个对象。如果有省略号表示对象可以有任何更多的属性。只检测自有属性,忽略原型中的属性。对象语法支持特殊标识属性,快捷属性,属性不支持尾逗号。

  1. test("value object", () => {
  2. let input = '{}'
  3. let y = match(input)
  4. let v = y({})
  5. let w = y({ x: 0 })
  6. expect(v).toEqual(true)
  7. expect(w).toEqual(false)
  8. })
  9. test("object ELLIPSIS", () => {
  10. let input = '{...}'
  11. let y = match(input)
  12. let v = y({})
  13. let w = y({ x: 0 })
  14. let p = y([])
  15. expect(v).toEqual(true)
  16. expect(w).toEqual(true)
  17. expect(p).toEqual(false)
  18. })
  19. test("object properties", () => {
  20. let input = '{x}'
  21. let y = match(input)
  22. let v = y({ x: 0 })
  23. let w = y([null, 1])
  24. expect(v).toEqual(true)
  25. expect(w).toEqual(false)
  26. })
  27. test("object properties ELLIPSIS", () => {
  28. let input = '{x,...}'
  29. let y = match(input)
  30. let v = y({ x: 0, y: 1 })
  31. let w = y({})
  32. expect(v).toEqual(true)
  33. expect(w).toEqual(false)
  34. })
  35. test("properties properties prop", () => {
  36. let input = '{x,y}'
  37. let y = match(input)
  38. let v = y({ x: 0, y: 1 })
  39. let w = y({})
  40. expect(v).toEqual(true)
  41. expect(w).toEqual(false)
  42. })
  43. test("prop key value", () => {
  44. let input = '{x:null}'
  45. let y = match(input)
  46. let v = y({ x: null })
  47. let w = y([null, 1])
  48. expect(v).toEqual(true)
  49. expect(w).toEqual(false)
  50. })
  51. test("key QUOTE", () => {
  52. let input = '{"1":null}'
  53. let y = match(input)
  54. let v = y({ '1': null })
  55. let w = y([null, 1])
  56. expect(v).toEqual(true)
  57. expect(w).toEqual(false)
  58. })

对象模式编译成:

  1. let y = obj => typeof obj === 'object' && obj && !Array.isArray(obj) && every props matched

OR模式

或模式,用一个竖杠符号连接两个模式,两个模式中任何一个模式成功即整体匹配成功。

  1. test("OR", () => {
  2. let y = match('string|{...}')
  3. let a = y('')
  4. let b = y({})
  5. let c = y([])
  6. let d = y(x => x)
  7. expect(a).toEqual(true)
  8. expect(b).toEqual(true)
  9. expect(c).toEqual(false)
  10. expect(d).toEqual(false)
  11. })

OR模式编译成:

  1. let y = obj => pat1 matched || pat2 matched

嵌套模式。匹配任意深度数据结构。

memoize解析的结果

上面的代码示例都用match(pattern)来缓存,但是实际上,我们常用if else条件选择语句来和模式匹配连用。

  1. let y = x => {
  2. if (match('[...]')(x)) console.log('[]')
  3. else if (match('{...}')(x)) console.log('{}')
  4. else if (match('string')(x)) console.log('string')
  5. }
  6. y([1]) // print []

这个是没有缓存的程序,每次调用y函数都会重新解析模式,对性能造成负面冲击。所以,我们需要缓存。

  1. let arr = match('[...]')
  2. let obj = match('{...}')
  3. let str = match('string')
  4. let y = x => {
  5. if (arr(x)) console.log('[]')
  6. else if (obj(x)) console.log('{}')
  7. else if (str(x)) console.log('string')
  8. }
  9. y([1]) // print []

上面程序成功解决了性能问题,避免重复解析,但是引入中间变量导致代码复杂难懂。我们使用另一种解决方案。

  1. import { match, cond } from 'structural-comparison'
  2. let y = cond([
  3. [match('[...]'), x => { console.log('[]') }],
  4. [match('{...}'), x => { console.log('{}') }],
  5. [match('string'), x => { console.log('string') }],
  6. x => {
  7. console.log('no matched!')
  8. }
  9. ])
  10. y([1]) // print []
  11. y({}) // print {}
  12. y(1) // print no matched!

cond函数是一个返回函数的组合子,用来模拟if else语句。它接受一个数组,数组的每个元素代表条件语句的一个分支。分支分为两种形式,第一种是断言函数,和行为函数组成的数组,当断言为真时,执行并返回行为,断言为假时跳过行为函数,执行下一分支。第二种是一个函数,当函数为真时,返回函数的返回值,当函数为假时,丢弃函数返回值,执行下一分支。cond函数依次执行每个分支,返回第一个为真的分支结果为整体的结果。忽略其后所有分支。

JavaScript中的模式匹配的更多相关文章

  1. JavaScript 中的数据类型

    Javascript中的数据类型有以下几种情况: 基本类型:string,number,boolean 特殊类型:undefined,null 引用类型:Object,Function,Date,Ar ...

  2. javascript中的正则表达式学习

    一.前言 关于正则表达式自身的语法这里不做过多介绍(详情可参见http://www.php100.com/manual/unze.html),这里仅仅解释javascript中和正则表达式相关的几个方 ...

  3. javascript中正则表达式的基础语法

    × 目录 [1]定义 [2]特点 [3]元字符[4]转义字符[5]字符组[6]量词[7]括号[8]选择[9]断言[10]模式[11]优先级[12]局限性 前面的话 正则表达式在人们的印象中可能是一堆无 ...

  4. 转载 javascript中的正则表达式总结 一

    定义正则表达式的方法 定义正则表达式的方法有两种:构造函数定义和正则表达式直接量定义.例如: var reg1 = new RegExp('\d{5, 11}'); // 通过构造函数定义 var r ...

  5. js学习笔记----JavaScript中DOM扩展的那些事

    什么都不说,先上总结的图~   Selectors API(选择符API) querySelector()方法 接收一个css选择符,返回与该模式匹配的第一个元素,如果没有找到匹配的元素,返回null ...

  6. C++、Java、JavaScript中的正则表达式

    C++(VS2013编译器):http://msdn.microsoft.com/zh-cn/library/bb982727.aspx#grammarsummary Java:            ...

  7. JavaScript中登录名的正则表达式及解析(0基础)

    简言 在JavaScript中,经常会用到正则表达式来进行模式匹配.例如,登录名验证,密码强度验证,字符串查找或替换等操作.现在就开始吧,零基础写出你的第一个正则表达式! 在做用户注册时,都会用到登录 ...

  8. 精通 JavaScript中的正则表达式

    精通 JS正则表达式 (精通?标题党 ) 正则表达式可以: •测试字符串的某个模式.例如,可以对一个输入字符串进行测试,看在该字符串是否存在一个电话号码模式或一个信用卡号码模式.这称为数据有效性验证  ...

  9. javascript中字符串对象常用的方法和属性

    前言 字符串是一种非常重要的数据类型,在Java等面向对象编程语言中,它代表对象类型,而在javascript中它却是一种基本数据类型,在开发的领域中,我们经常会碰到,无论是前端还是后台.比如后台验证 ...

随机推荐

  1. 一文读懂Lua元表

    元表 Lua语言中的每种类型的值都有一套可预见的操作集合.例如,我们可以将数字相加,可以连接字符串,还可以在表中插入键值对等,但是我们无法将两个表相加,无法对函数作比较,也无法调用一个字符串,除非使用 ...

  2. python库--pandas--Series

    方法 返回数据类型 参数 说明 Series(一维)       .Series() Series 实例s 创建一维数据类型Series data=None 要转化为Series的数据(也可用dict ...

  3. 马哈鱼数据血缘分析器分析case-when语句

    马哈鱼数据血缘分析器是一个分析数据血缘关系的平台,可以在线直接递交 SQL 语句进行分析,也可以选择连接指定数据库获取 metadata.从本地上传文件目录.或从指定 git 仓库获取脚本进行分析. ...

  4. 什么是 baseline 和 benchmark

    baseline 一个算法被称为 baseline 算法说明这个比目前这个算法还差的已经不能接受了,方法有革命性的创新点可以挖掘,且存在巨大提升空间和超越benchmark的潜力,只是由于发展初期导致 ...

  5. 使用metaweblog API实现通用博客发布 之 本地图片自动上传以及替换路径

    使用metaweblog API实现通用博客发布 之 本地图片自动上传以及替换路径 通过metaweblog API 发布博文的时候,由于markdown中的图片路径是本地路径,将导致发布的文章图片不 ...

  6. PHP的另一个高效缓存扩展:Yac

    之前的文章中我们已经学习过一个 PHP 自带的扩展缓存 Apc ,今天我们来学习另一个缓存扩展:Yac . 什么是 Yac 从名字其实就能看出,这又是鸟哥大神的作品.毕竟是 PHP 的核心开发人员,他 ...

  7. nuxt打包等注意事项

    打包步骤: 1.首先执行 npm run build 2.将打包好的 .nuxt static nuxt.config.js package.json 这四个文件丢到服务器的某个文件夹中,在服务器上安 ...

  8. Nginx系列(9)- Nginx常用命令

    Linux # 命令需要在Nginx的sbin目录下执行 cd /usr/local/nginx/sbin/ ./nginx #启动./nginx -s stop #停止 ./nginx -s qui ...

  9. Docker系类(25)- 发布镜像到DockerHub

    # step-1 注册账号 https://hub.docker.com/# step-2 在服务器尚提交我们的镜像[root@localhost WEB-INF]# docker login --h ...

  10. 一生挚友redo log、binlog《死磕MySQL系列 二》

    系列文章 原来一条select语句在MySQL是这样执行的<死磕MySQL系列 一> 一生挚友redo log.binlog<死磕MySQL系列 二> 前言 咔咔闲谈 上期根据 ...