From 96b6c5a3fd8689aec9308c6082b9eefa4167b2bc Mon Sep 17 00:00:00 2001 From: ruanyf Date: Mon, 3 Oct 2016 22:46:08 +0800 Subject: [PATCH] docs(regex): add Unicode property class --- docs/object.md | 24 +-- docs/reference.md | 9 +- docs/regex.md | 61 ++++++++ docs/simd.md | 367 ++++++++++++++++++++++++++++++++++++++++------ docs/string.md | 44 +++++- 5 files changed, 447 insertions(+), 58 deletions(-) diff --git a/docs/object.md b/docs/object.md index a1a6d7c..9669045 100644 --- a/docs/object.md +++ b/docs/object.md @@ -883,11 +883,11 @@ function entries(obj) { ## 对象的扩展运算符 -目前,ES7有一个[提案](https://github.com/sebmarkbage/ecmascript-rest-spread),将Rest解构赋值/扩展运算符(...)引入对象。Babel转码器已经支持这项功能。 +目前,ES7有一个[提案](https://github.com/sebmarkbage/ecmascript-rest-spread),将Rest运算符(解构赋值)/扩展运算符(`...`)引入对象。Babel转码器已经支持这项功能。 -**(1)Rest解构赋值** +**(1)解构赋值** -对象的Rest解构赋值用于从一个对象取值,相当于将所有可遍历的、但尚未被读取的属性,分配到指定的对象上面。所有的键和它们的值,都会拷贝到新对象上面。 +对象的解构赋值用于从一个对象取值,相当于将所有可遍历的、但尚未被读取的属性,分配到指定的对象上面。所有的键和它们的值,都会拷贝到新对象上面。 ```javascript let { x, y, ...z } = { x: 1, y: 2, a: 3, b: 4 }; @@ -896,25 +896,25 @@ y // 2 z // { a: 3, b: 4 } ``` -上面代码中,变量`z`是Rest解构赋值所在的对象。它获取等号右边的所有尚未读取的键(`a`和`b`),将它们和它们的值拷贝过来。 +上面代码中,变量`z`是解构赋值所在的对象。它获取等号右边的所有尚未读取的键(`a`和`b`),将它们连同值一起拷贝过来。 -由于Rest解构赋值要求等号右边是一个对象,所以如果等号右边是`undefined`或`null`,就会报错,因为它们无法转为对象。 +由于解构赋值要求等号右边是一个对象,所以如果等号右边是`undefined`或`null`,就会报错,因为它们无法转为对象。 ```javascript let { x, y, ...z } = null; // 运行时错误 let { x, y, ...z } = undefined; // 运行时错误 ``` -Rest解构赋值必须是最后一个参数,否则会报错。 +解构赋值必须是最后一个参数,否则会报错。 ```javascript let { ...x, y, z } = obj; // 句法错误 let { x, ...y, ...z } = obj; // 句法错误 ``` -上面代码中,Rest解构赋值不是最后一个参数,所以会报错。 +上面代码中,解构赋值不是最后一个参数,所以会报错。 -注意,Rest解构赋值的拷贝是浅拷贝,即如果一个键的值是复合类型的值(数组、对象、函数)、那么Rest解构赋值拷贝的是这个值的引用,而不是这个值的副本。 +注意,解构赋值的拷贝是浅拷贝,即如果一个键的值是复合类型的值(数组、对象、函数)、那么解构赋值拷贝的是这个值的引用,而不是这个值的副本。 ```javascript let obj = { a: { b: 1 } }; @@ -923,9 +923,9 @@ obj.a.b = 2; x.a.b // 2 ``` -上面代码中,`x`是Rest解构赋值所在的对象,拷贝了对象`obj`的`a`属性。`a`属性引用了一个对象,修改这个对象的值,会影响到Rest解构赋值对它的引用。 +上面代码中,`x`是解构赋值所在的对象,拷贝了对象`obj`的`a`属性。`a`属性引用了一个对象,修改这个对象的值,会影响到解构赋值对它的引用。 -另外,Rest解构赋值不会拷贝继承自原型对象的属性。 +另外,解构赋值不会拷贝继承自原型对象的属性。 ```javascript let o1 = { a: 1 }; @@ -949,9 +949,9 @@ y // undefined z // 3 ``` -上面代码中,变量`x`是单纯的解构赋值,所以可以读取继承的属性;Rest解构赋值产生的变量`y`和`z`,只能读取对象自身的属性,所以只有变量`z`可以赋值成功。 +上面代码中,变量`x`是单纯的解构赋值,所以可以读取继承的属性;解构赋值产生的变量`y`和`z`,只能读取对象自身的属性,所以只有变量`z`可以赋值成功。 -Rest解构赋值的一个用处,是扩展某个函数的参数,引入其他操作。 +解构赋值的一个用处,是扩展某个函数的参数,引入其他操作。 ```javascript function baseFunction({ a, b }) { diff --git a/docs/reference.md b/docs/reference.md index 9622b16..40563da 100644 --- a/docs/reference.md +++ b/docs/reference.md @@ -2,9 +2,12 @@ ## 官方文件 -- [ECMAScript® 2015 Language Specification](http://www.ecma-international.org/ecma-262/6.0/index.html): ES6语言规格 -- [ECMAScript Current Proposals](https://github.com/tc39/ecma262): ECMAScript当前的各种提案 -- [ECMAScript® 2016 Language Specification](http://tc39.github.io/ecma262/): ECMAScript 2016草案 +- [ECMAScript® 2015 Language Specification](http://www.ecma-international.org/ecma-262/6.0/index.html): ECMAScript 2015规格 +- [ECMAScript® 2016 Language Specification](http://www.ecma-international.org/ecma-262/7.0/): ECMAScript 2016规格 +- [ECMAScript® 2017 Language Specification](https://tc39.github.io/ecma262/):ECMAScript 2017规格(草案) +- [ECMAScript Current Proposals](https://github.com/tc39/ecma262): ECMAScript当前的所有提案 +- [ECMAScript Active Proposals](https://github.com/tc39/proposals): 已经进入正式流程的提案 +- [ECMAScript Daily](https://ecmascript-daily.github.io/): TC39委员会的动态 ## 综合介绍 diff --git a/docs/regex.md b/docs/regex.md index 04361d7..cdd7899 100644 --- a/docs/regex.md +++ b/docs/regex.md @@ -409,3 +409,64 @@ JavaScript语言的正则表达式,只支持先行断言(lookahead)和先 ``` 上面代码中,如果后行断言的反斜杠引用(`\1`)放在括号的后面,就不会得到匹配结果,必须放在前面才可以。 + +## Unicode属性类 + +目前,有一个[提案](https://github.com/mathiasbynens/es-regexp-unicode-property-escapes),引入了一种新的类的写法`\p{...}`和`\P{...}`,允许正则表达式匹配符合Unicode某种属性的所有字符。 + +```javascript +const regexGreekSymbol = /\p{Script=Greek}/u; +regexGreekSymbol.test('π') // u +``` + +上面代码中,`\p{Script=Greek}`指定匹配一个希腊文字母,所以匹配`π`成功。 + +Unicode属性类要指定属性名和属性值。 + +```javascript +\p{UnicodePropertyName=UnicodePropertyValue} +``` + +对于某些属性,可以只写属性名。 + +```javascript +\p{UnicodePropertyName} +``` + +`\P{…}`是`\p{…}`的反向匹配,即匹配不满足条件的字符。 + +注意,这两种类只对Unicode有效,所以使用的时候一定要加上`u`修饰符。如果不加`u`修饰符,正则表达式使用`\p`和`\P`会报错,ECMAScript预留了这两个类。 + +由于Unicode的各种属性非常多,所以这种新的类的表达能力非常强。 + +```javascript +const regex = /^\p{Decimal_Number}+$/u; +regex.test('𝟏𝟐𝟑𝟜𝟝𝟞𝟩𝟪𝟫𝟬𝟭𝟮𝟯𝟺𝟻𝟼') // true +``` + +上面代码中,属性类指定匹配所有十进制字符,可以看到各种字型的十进制字符都会匹配成功。 + +`\p{Number}`甚至能匹配罗马数字。 + +```javascript +// 匹配所有数字 +const regex = /^\p{Number}+$/u; +regex.test('²³¹¼½¾') // true +regex.test('㉛㉜㉝') // true +regex.test('ⅠⅡⅢⅣⅤⅥⅦⅧⅨⅩⅪⅫ') // true +``` + +下面是其他一些例子。 + +```javascript +// 匹配各种文字的所有字母,等同于Unicode版的\w +[\p{Alphabetic}\p{Mark}\p{Decimal_Number}\p{Connector_Punctuation}\p{Join_Control}] + +// 匹配各种文字的所有非字母的字符,等同于Unicode版的\W +[\p{Alphabetic}\p{Mark}\p{Decimal_Number}\p{Connector_Punctuation}\p{Join_Control}] + +// 匹配所有的箭头字符 +const regexArrows = /^\p{Block=Arrows}+$/u; +regexArrows.test('←↑→↓↔↕↖↗↘↙⇏⇐⇑⇒⇓⇔⇕⇖⇗⇘⇙⇧⇩') // true +``` + diff --git a/docs/simd.md b/docs/simd.md index e3ce170..e8a2dfe 100644 --- a/docs/simd.md +++ b/docs/simd.md @@ -2,7 +2,7 @@ ## 概述 -SIMD是“Single Instruction/Multiple Data”的缩写,意为“单指令,多数据”。它是JavaScript操作CPU对应指令的接口,你可以看做这是一种不同的运算执行模式。与它相对的是SISD(“Single Instruction/Single Data”),即“单指令,单数据”。 +SIMD(发音`/sim-dee/`)是“Single Instruction/Multiple Data”的缩写,意为“单指令,多数据”。它是JavaScript操作CPU对应指令的接口,你可以看做这是一种不同的运算执行模式。与它相对的是SISD(“Single Instruction/Single Data”),即“单指令,单数据”。 SIMD的含义是使用一个指令,完成多个数据的运算;SISD的含义是使用一个指令,完成单个数据的运算,这是JavaScript的默认运算模式。显而易见,SIMD的执行效率要高于SISD,所以被广泛用于3D图形运算、物理模拟等运算量超大的项目之中。 @@ -17,7 +17,7 @@ c[0] = a[0] + b[0]; c[1] = a[1] + b[1]; c[2] = a[2] + b[2]; c[3] = a[3] + b[3]; -c; // Array[6, 8, 10, 12] +c // Array[6, 8, 10, 12] ``` 上面代码中,数组`a`和`b`的对应成员相加,结果放入数组`c`。它的运算模式是依次处理每个数组成员,一共有四个数组成员,所以需要运算4次。 @@ -41,9 +41,9 @@ v + w = 〈v1, …, vn〉+ 〈w1, …, wn〉 = 〈v1+w1, …, vn+wn〉 ``` -上面代码中,`v`和`w`是两个多元矢量。它们的加运算,在SIMD下是一个指令、而不是n个指令完成的,这就大大提高了效率。这对于3D动画、图像处理、信号处理、数值处理、加密等运算是非常重要的。 +上面代码中,`v`和`w`是两个多元矢量。它们的加运算,在SIMD下是一个指令、而不是n个指令完成的,这就大大提高了效率。这对于3D动画、图像处理、信号处理、数值处理、加密等运算是非常重要的。比如,Canvas的`getImageData()`会将图像文件读成一个二进制数组,SIMD就很适合对于这种数组的处理。 -总得来说,SIMD是数据并行处理(parallelism)的一种手段。 +总得来说,SIMD是数据并行处理(parallelism)的一种手段,可以加速一些运算密集型操作的速度。 ## 数据类型 @@ -62,21 +62,51 @@ SIMD提供多种数据类型。 - Bool8x16:十六个8位布尔值 - Bool64x2:两个64位布尔值 +每种数据类型被`x`号分隔成两部分,后面的部分表示通道数,前面的部分表示每个通道的宽度和类型。比如,`Float32x4`就表示这个值有4个通道,每个通道是一个32位浮点数。 + 每种数据类型都是一个方法,可以传入参数,生成对应的值。 ```javascript var a = SIMD.Float32x4(1.0, 2.0, 3.0, 4.0); ``` -上面代码中,变量`a`就是一个128位、包含四个32位浮点数的值。 +上面代码中,变量`a`就是一个128位、包含四个32位浮点数(即四个通道)的值。 注意,这些数据类型方法都不是构造函数,前面不能加`new`,否则会报错。 ```javascript -var v = new SIMD.Float32x4(0,1,2,3); +var v = new SIMD.Float32x4(0, 1, 2, 3); // TypeError: SIMD.Float32x4 is not a constructor ``` +如果所有数据通道都是同样的值,可以使用`splat`方法生成值。 + +```javascript +var v = SIMD.Float32x4.splat(0.0); +``` + +上面代码中,`v`的四个通道都是`0.0`。 + +如果要取出单个通道的值,可以使用`extractLane`方法。 + +```javascript +var a = SIMD.Float32x4(1.0, 2.0, 3.0, 4.0); +var b = SIMD.Float32x4.extractLane(a, 0); // 1.0 +``` + +上面代码中,`extractLane`方法的第一个参数是一个SIMD值,第二个参数是通道的编号(从0开始)。 + +如果要修改某个通道的值,可以使用`replaceLane`方法。 + +```javascript +var a = SIMD.Float32x4(1.0, 2.0, 3.0, 4.0); +var c = SIMD.Float32x4.replaceLane(a, 0, 5.0); +``` + +上面代码中,经过替换后,得到一个新的SIMD值:`(5.0, 2.0, 3.0, 4.0)`。可以看到,`replaceLane`接受三个参数:SIMD值、通道的编号(从0开始)、新的值。 + +## 方法:数学运算 + 每种数据类型都有一系列运算符,下面是其中的一些。 - float32x4.abs(v):返回`v`的绝对值 @@ -86,64 +116,316 @@ var v = new SIMD.Float32x4(0,1,2,3); - float32x4.mul(v, w):`v`和`w`对应项的相乘 - float32x4.equal(v, w):比较`v`和`w`对应项是否相等,返回的布尔值组成一个`uint32x4`的值 -下面是一个`add`运算符的例子。 +### SIMD.%type%.add() + +`add`方法接受两个SIMD值作为参数,将它们的每个通道相加,返回一个新的SIMD值。 ```javascript -c[i] = SIMD.float32x4.add(a[i], b[i]); - -// 或者 - -var tmp0 = a[i]; -var tmp1 = b[i]; -var tmp2 = SIMD.float32x4.add(tmp0, tmp1); -c[i] = tmp2; +var a = SIMD.Float32x4(1.0, 2.0, 3.0, 4.0); +var b = SIMD.Float32x4(5.0, 10.0, 15.0, 20.0); +var c = SIMD.Float32x4.add(a, b); ``` -此外,每种数据类型还有操作方法。 +上面代码中,经过加法运算,新的SIMD值为`(6.0, 12.0, 18.0. 24.0)`。 -`getAt`方法返回指定位置的值。 +### SIMD.%type%.mul() + +`mul`方法接受两个SIMD值作为参数,将它们的每个通道相乘,返回一个新的SIMD值。 ```javascript -var a = SIMD.float32x4(1.0, 2.0, 3.0, 4.0); -var b = a.getAt(0); // 1.0 +var a = SIMD.Float32x4(-1, -2, 3, 4); +var b = SIMD.Float32x4(3, 3, 3, 3); +SIMD.Float32x4.mul(a, b); +// Float32x4[-3, -6, 9, 12] ``` -`zero`方法可以将SIMD值清零。 +### SIMD.%type%.shiftLeftByScalar() + +`shiftLeftByScalar`方法接受一个SIMD值作为参数,然后将每个通道的值左移指定的位数,返回一个新的SIMD值。 ```javascript -var b = SIMD.float32x4.zero(); +var a = SIMD.Int32x4(1, 2, 4, 8); +SIMD.Int32x4.shiftLeftByScalar(a, 1); +// Int32x4[2, 4, 8, 16] ``` -上面代码中,变量`b`包含的四个32位浮点数全部是0.0。 - -`shuffle`方法根据指定模式,重新生成一个SIMD值。 +如果左移后,新的值超出了当前数据类型的位数,溢出的部分会被丢弃。 ```javascript -var a = SIMD.float32x4(1.0, 2.0, 3.0, 4.0); -var b = SIMD.float32x4.shuffle(a, SIMD.float32x4.XXYY); -// [1.0, 1.0, 2.0, 2.0] - -var c = SIMD.float32x4.shuffle(a, SIMD.float32x4.WWWW); -// [4.0, 4.0, 4.0, 4.0] - -var d = SIMD.float32x4.shuffle(a, SIMD.float32x4.WZYX); -// [4.0, 3.0, 2.0, 1.0] +var ix4 = SIMD.Int32x4(1, 2, 3, 4); +var jx4 = SIMD.Int32x4.shiftLeftByScalar(ix4, 32); +// Int32x4[0, 0, 0, 0] ``` -下面是一个求平均值的例子。 +### SIMD.%type%.shiftRightByScalar() + +`shiftRightByScalar`方法接受一个SIMD值作为参数,然后将每个通道的值右移指定的位数,返回一个新的SIMD值。 ```javascript -function average(f32x4list) { - var n = f32x4list.length; - var sum = SIMD.float32x4.zero(); - for (int i = 0; i < n; i++) { - sum = SIMD.float32x4.add(sum, f32x4list.getAt(i)); +var a = SIMD.Int32x4(1, 2, 4, -8); +SIMD.Int32x4.shiftRightByScalar(a, 1); +// Int32x4[0, 1, 2, -4] +``` + +如果原来通道的值是带符号的值,则符号位保持不变,不受右移影响。如果是不带符号位的值,则右移后头部会补`0`。 + +```javascript +var a = SIMD.Uint32x4(1, 2, 4, -8); +SIMD.Uint32x4.shiftRightByScalar(a, 1); +// Uint32x4[0, 1, 2, 2147483644] +``` + +上面代码中,`-8`右移一位变成了`2147483644`,是因为对于32位无符号整数来说,`-8`的二进制形式是`11111111111111111111111111111000`,右移一位就变成了`01111111111111111111111111111100`,相当于`2147483644`。 + +## 方法:通道处理 + +### SIMD.%type%.load() + +`load`方法用于从二进制数组读入数据,生成一个新的SIMD值。 + +```javascript +var a = new Int32Array([1,2,3,4,5,6,7,8]); +SIMD.Int32x4.load(a, 0); +// Int32x4[1, 2, 3, 4] + +var b = new Int32Array([1,2,3,4,5,6,7,8]); +SIMD.Int32x4.load(a, 2); +// Int32x4[3, 4, 5, 6] +``` + +`load`方法接受两个参数:一个二进制数组和开始读取的位置(从0开始)。如果位置不合法(比如`-1`或者超出二进制数组的大小),就会抛出一个错误。 + +这个方法还有三个变种`load1()`、`load2()`、`load3()`,表示从指定位置开始,只加载一个通道、二个通道、三个通道的值。 + +```javascript +// 格式 +SIMD.Int32x4.load(tarray, index) +SIMD.Int32x4.load1(tarray, index) +SIMD.Int32x4.load2(tarray, index) +SIMD.Int32x4.load3(tarray, index) + +// 实例 +var a = new Int32Array([1,2,3,4,5,6,7,8]); +SIMD.Int32x4.load1(a, 0); +// Int32x4[1, 0, 0, 0] +SIMD.Int32x4.load2(a, 0); +// Int32x4[1, 2, 0, 0] +SIMD.Int32x4.load3(a, 0); +// Int32x4[1, 2, 3,0] +``` + +### SIMD.%type%.splat() + +`splat`方法返回一个新的SIMD值,该值的所有通道都会设成同一个预先给定的值。 + +```javascript +SIMD.Float32x4.splat(3); +// Float32x4[3, 3, 3, 3] +SIMD.Float64x2.splat(3); +// Float64x2[3, 3] +``` + +如果省略参数,所有整数型的SIMD值都会设定`0`,浮点型的SIMD值都会设成`NaN`。 + +### SIMD.%type%.swizzle() + +`swizzle`方法返回一个新的SIMD值,重新排列原有的SIMD值的通道顺序。 + +```javascript +var t = SIMD.Float32x4(1, 2, 3, 4); +SIMD.Float32x4.swizzle(t, 1, 2, 0, 3); +// Float32x4[2,3,1,4] +``` + +上面代码中,`swizzle`方法的第一个参数是原有的SIMD值,后面的参数对应将要返回的SIMD值的四个通道。它的意思是新的SIMD的四个通道,依次是原来SIMD值的1号通道、2号通道、0号通道、3号通道。由于SIMD值最多可以有16个通道,所以`swizzle`方法除了第一个参数以外,最多还可以接受16个参数。 + +下面是另一个例子。 + +```javascript +var a = SIMD.Float32x4(1.0, 2.0, 3.0, 4.0); +// Float32x4[1.0, 2.0, 3.0, 4.0] + +var b = SIMD.Float32x4.swizzle(a, 0, 0, 1, 1); +// Float32x4[1.0, 1.0, 2.0, 2.0] + +var c = SIMD.Float32x4.swizzle(a, 3, 3, 3, 3); +// Float32x4[4.0, 4.0, 4.0, 4.0] + +var d = SIMD.Float32x4.swizzle(a, 3, 2, 1, 0); +// Float32x4[4.0, 3.0, 2.0, 1.0] +``` + +### SIMD.%type%.shuffle() + +`shuffle`方法从两个SIMD值之中取出指定通道,返回一个新的SIMD值。 + +```javascript +var a = SIMD.Float32x4(1, 2, 3, 4); +var b = SIMD.Float32x4(5, 6, 7, 8); + +SIMD.Float32x4.shuffle(a, b, 1, 5, 7, 2); +// Float32x4[2, 6, 8, 3] +``` + +上面代码中,`a`和`b`一共有8个通道,依次编号为0到7。`shuffle`根据编号,取出相应的通道,返回一个新的SIMD值。 + +## 方法:比较运算 + +### SIMD.%type%.greaterThan() + +`greatThan`方法用来比较两个SIMD值`a`和`b`的每一个通道,如果在该通道中,`a`较大就得到`true`,否则得到`false`。最后,所有通道的比较结果,会组成一个新的SIMD值,作为掩码返回。 + +```javascript +var a = SIMD.Float32x4(1, 6, 3, 11); +var b = SIMD.Float32x4(1, 4, 7, 9); + +var mask = SIMD.Float32x4.greaterThan(a,b); +// Bool32x4[false, true, false, true] +``` + +### SIMD.%type%.lessThan() + +`lessThan`方法用来比较两个SIMD值`a`和`b`的每一个通道,如果在该通道中,`a`较小就得到`true`,否则得到`false`。最后,所有通道的比较结果,会组成一个新的SIMD值,作为掩码返回。 + +```javascript +var a = SIMD.Float32x4(1, 2, 3, 11); +var b = SIMD.Float32x4(1, 4, 7, 9); + +var mask = SIMD.Float32x4.lessThan(a,b); +// Bool32x4[false, true, true, false] +``` + +### SIMD.%type%.select() + +`select`方法通过掩码生成一个新的SIMD值。它接受三个参数,分别是掩码和两个SIMD值。 + +```javascript +var a = SIMD.Float32x4(1, 2, 3, 4); +var b = SIMD.Float32x4(5, 6, 7, 8); + +var mask = SIMD.Bool32x4(true, false, false, true); + +SIMD.Float32x4.select(mask, a, b); +// Float32x4[1, 6, 7, 4] +``` + +上面代码中,`select`方法接受掩码和两个SIMD值作为参数。当某个通道对应的掩码为`true`时,会选择第一个SIMD值的对应通道,否则选择第二个SIMD值的对应通道。 + +这个方法通常与比较运算符结合使用。 + +```javascript +var a = SIMD.Float32x4(0, 12, 3, 4); +var b = SIMD.Float32x4(0, 6, 7, 50); + +var mask = SIMD.Float32x4.lessThan(a,b); +// Bool32x4[false, false, true, true] + +var result = SIMD.Float32x4.select(mask, a, b); +// Float32x4[0, 6, 3, 4] +``` + +上面代码中,先通过`lessThan`方法生成一个掩码,然后通过`select`方法生成一个由每个通道的较小值组成的新的SIMD值。 + +### SIMD.%type%.allTrue(),SIMD.%type%.anyTrue() + +`allTrue`方法接受一个SIMD值作为参数,然后返回一个布尔值,表示该SIMD值的所有通道是否都为`true`。 + +```javascript +var a = SIMD.Bool32x4(true, true, true, true); +var b = SIMD.Bool32x4(true, false, true, true); + +SIMD.Bool32x4.allTrue(a); // true +SIMD.Bool32x4.allTrue(b); // false +``` + +`anyTrue`方法则是只要有一个通道为`true`,就返回`true`,否则返回`false`。 + +```javascript +var a = SIMD.Bool32x4(false, false, false, false); +var b = SIMD.Bool32x4(false, false, true, false); + +SIMD.Bool32x4.anyTrue(a); // false +SIMD.Bool32x4.anyTrue(b); // true +``` + +这两个方法通常与比较运算符结合使用。 + +```javascript +var ax4 = SIMD.Float32x4(1.0, 2.0, 3.0, 4.0); +var bx4 = SIMD.Float32x4(0.0, 6.0, 7.0, 8.0); +var ix4 = SIMD.Float32x4.lessThan(ax4, bx4); +var b1 = SIMD.Int32x4.allTrue(ix4); // false +var b2 = SIMD.Int32x4.anyTrue(ix4); // true +``` + +### SIMD.%type%.min(),SIMD.%type%.minNum() + +`min`方法接受两个SIMD值作为参数,将它们的每个通道的较小值,组成一个新的SIMD值返回。 + +```javascript +var a = SIMD.Float32x4(-1, -2, 3, 5.2); +var b = SIMD.Float32x4(0, -4, 6, 5.5); +SIMD.Float32x4.min(a, b); +// Float32x4[-1, -4, 3, 5.2] +``` + +如果有一个通道的值是`NaN`,则会返回`NaN`。 + +```javascript +var c = SIMD.Float64x2(NaN, Infinity) +var d = SIMD.Float64x2(1337, 42); +SIMD.Float64x2.min(c, d); +// Float64x2[NaN, 42] +``` + +`minNum`方法与`min`方法的作用一模一样,唯一的区别是如果有一个通道的值是`NaN`,则会优先返回另一个通道的值。 + +```javascript +var ax4 = SIMD.Float32x4(1.0, 2.0, NaN, NaN); +var bx4 = SIMD.Float32x4(2.0, 1.0, 3.0, NaN); +var cx4 = SIMD.Float32x4.min(ax4, bx4); +// Float32x4[1.0, 1.0, NaN, NaN] +var dx4 = SIMD.Float32x4.minNum(ax4, bx4); +// Float32x4[1.0, 1.0, 3.0, NaN] +``` + +## 实例:求平均值 + +正常模式下,计算`n`个值的平均值,需要运算`n`次。 + +```javascript +function average(list) { + var n = list.length; + var sum = 0.0; + for (var i = 0; i < n; i++) { + sum += list[i]; } - var total = sum.x + sum.y + sum.z + sum.w; - return total / (n * 4); + return sum / n; } ``` +使用SIMD,可以将计算次数减少到`n`次的四分之一。 + +```javascript +function average(list) { + var n = list.length; + var sum = SIMD.Float32x4.splat(0.0); + for (var i = 0; i < n; i += 4) { + sum = SIMD.Float32x4.add( + sum, + SIMD.Float32x4.load(list, i) + ); + } + var total = SIMD.Float32x4.extractLane(sum, 0) + + SIMD.Float32x4.extractLane(sum, 1) + + SIMD.Float32x4.extractLane(sum, 2) + + SIMD.Float32x4.extractLane(sum, 3); + return total / n; +} +``` + +上面代码先是每隔四位,将所有的值读入一个SIMD,然后立刻累加。然后,得到累加值四个通道的总和,再除以`n`就可以了。 + ## 二进制数组 SIMD可以与二进制数组结合,生成数组实例。 @@ -158,7 +440,7 @@ var _ui16x8 = new Uint16Array(_f32x4.buffer); var _ui8x16 = new Uint8Array(_f32x4.buffer); ``` -下面是一些应用的例子。 +下面是一个例子。 ```javascript // a 和 b 是float32x4数组实例 @@ -173,6 +455,7 @@ function addArrays(a, b) { ## 参考链接 +- TC39, [SIMD.js Stage 2](https://docs.google.com/presentation/d/1MY9NHrHmL7ma7C8dyNXvmYNNGgVmmxXk8ZIiQtPlfH4/edit#slide=id.p19) - MDN, [SIMD](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/SIMD) - TC39, [ECMAScript SIMD](https://github.com/tc39/ecmascript_simd) - Axel Rauschmayer, [JavaScript gains support for SIMD](http://www.2ality.com/2013/12/simd-js.html) diff --git a/docs/string.md b/docs/string.md index 4edf47c..e3677c1 100644 --- a/docs/string.md +++ b/docs/string.md @@ -846,7 +846,7 @@ function tag(strings) { } ``` -上面代码中,`tag`函数的第一个参数`strings`,有一个`raw`属性,也指向一个数组。该数组的成员与`strings`数组完全一致。比如,`strings`数组是`["First line\nSecond line"]`,那么`strings.raw`数组就是`["First line\\nSecond line"]`。两者唯一的区别,就是字符串里面的斜杠都被转义了。比如,strings.raw数组会将`\n`视为`\`和`n`两个字符,而不是换行符。这是为了方便取得转义之前的原始模板而设计的。 +上面代码中,`tag`函数的第一个参数`strings`,有一个`raw`属性,也指向一个数组。该数组的成员与`strings`数组完全一致。比如,`strings`数组是`["First line\nSecond line"]`,那么`strings.raw`数组就是`["First line\\nSecond line"]`。两者唯一的区别,就是字符串里面的斜杠都被转义了。比如,strings.raw数组会将`\n`视为`\\`和`n`两个字符,而不是换行符。这是为了方便取得转义之前的原始模板而设计的。 ## String.raw() @@ -894,3 +894,45 @@ String.raw({ raw: 'test' }, 0, 1, 2); // 等同于 String.raw({ raw: ['t','e','s','t'] }, 0, 1, 2); ``` + +## 模板字符串的限制 + +前面提到标签模板里面,可以内嵌其他语言。但是,模板字符串默认会将字符串转义,因此导致了无法嵌入其他语言。 + +举例来说,在标签模板里面可以潜入Latex语言。 + +```javascript +function latex(strings) { + // ... +} + +let document = latex` +\newcommand{\fun}{\textbf{Fun!}} // 正常工作 +\newcommand{\unicode}{\textbf{Unicode!}} // 报错 +\newcommand{\xerxes}{\textbf{King!}} // 报错 + +Breve over the h goes \u{h}ere // 报错 +` +``` + +上面代码中,变量`document`内嵌的模板字符串,对于Latex语言来说完全是合法的,但是JavaScript引擎会报错。原因就在于字符串的转义。 + +模板字符串会将`\u00FF`和`\u{42}`当作Unicode字符进行转义,所以`\unicode`解析时报错;而`\x56`会被当作十六进制字符串转义,所以`\xerxes`会报错。 + +为了解决这个问题,现在有一个[提案](https://tc39.github.io/proposal-template-literal-revision/),放松对标签模板里面的字符串转义的限制。如果遇到不合法的字符串转义,就返回`undefined`,而不是报错,并且从`raw`属性上面可以得到原始字符串。 + +```javascript +function tag(strs) { + strs[0] === undefined + strs.raw[0] === "\\unicode and \\u{55}"; +} +tag`\unicode and \u{55}` +``` + +上面代码中,模板字符串原本是应该报错的,但是由于放松了对字符串转义的限制,所以不报错了,JavaScript引擎将第一个字符设置为`undefined`,但是`raw`属性依然可以得到原始字符串,因此`tag`函数还是可以对原字符串进行处理。 + +注意,这种对字符串转义的放松,只在标签模板解析字符串时生效,不是标签模板的场合,依然会报错。 + +```javascript +let bad = `bad escape sequence: \unicode`; // 报错 +```