JavaScript 中的函数式编程风格模式匹配
Functional programming style pattern matching in JavaScript
我正在编写从函数式语言到 JS 的编译器。编译器将在浏览器中 运行。我需要在 JS 中实现模式匹配机制,因为原始语言有一个。我找到了 Sparkler and Z。据我所知,Sparkler 无法在浏览器中执行,Z 也没有我需要的所有可能性。
所以我的语言有这样的语义:
count x [] <- 0
count x [ x : xs ] <- 1 + count x xs
count x [ y : xs ] <- count x xs
这就是这段代码中发生的事情:
第一行是一个函数的定义,它有两个参数:一些变量 x 和空列表,以及 returns 零。
第二行是一个函数的定义,它也有两个参数:一些变量x和以x开头的列表,以及returns 1 + count(x, xs)
对于这个例子,我想生成这样的代码:
const count = (x, list) => {
match(x, list) => (
(x, []) => {...}
(x, [ x : xs ]) => {...}
(x, [ y : xs ]) => {...}
)
}
如何将这种模式匹配正确展开为ifs
和ors
?
一般情况
有 proposal for Pattern Matching in ECMAScript,但截至 2018 年还处于非常早期的阶段。
目前,实现部分仅列出:
- Babel Plugin
- Sweet.js macro (NOTE: this isn't based on the proposal, this proposal is partially based on it!)
案例列表
const count = list => {
const [x, ...xs] = list;
if (x === undefined) {
return 0;
} else if (xs === undefined) {
return 1;
} else {
return 1 + count(xs);
}
}
使用ex-patterns
,您可以按如下方式编写您的示例。您需要使用包附带的占位符名称(_
、A
、B
、C
、... Z
)但您可以重命名具有解构的回调函数中的匹配变量(包含所有命名匹配项的对象作为第一个参数传递给回调函数)。
import { when, then, Y, _, tail, end } from 'ex-patterns';
const count = list => (
when(list)
([], then(() => 0)) // match empty array
([_], then(() => 1)) // match array with (any) 1 element
([_, tail(Y)], then(({ Y: xs }) => 1 + count(xs))) // match array and capture tail
(end);
);
这也涵盖了 list = [undefined, 'foo', 'bar']
的情况,我认为接受的答案不会涵盖这种情况。
为了提高代码效率,您可以使用 Immutable.js List
而不是数组来调用 count
(无需更改)。在这种情况下,不需要在每个循环中将数组的 tail
部分切片并复制到新数组中。
与您提到的包一样,浏览器本身不会 运行,但我想这不是现代捆绑工具的主要障碍。
这是文档:https://moritzploss.github.io/ex-patterns/
免责声明:我是 ex-patterns
的作者 :)
我需要模式匹配并做了一些适合我的东西。
const count = patroon(
[_], ([, ...xs]) => 1 + count(xs),
[], 0
)
count([0,1,2,3])
4
有关更多用法示例,请参阅自述文件。
我正在编写从函数式语言到 JS 的编译器。编译器将在浏览器中 运行。我需要在 JS 中实现模式匹配机制,因为原始语言有一个。我找到了 Sparkler and Z。据我所知,Sparkler 无法在浏览器中执行,Z 也没有我需要的所有可能性。
所以我的语言有这样的语义:
count x [] <- 0
count x [ x : xs ] <- 1 + count x xs
count x [ y : xs ] <- count x xs
这就是这段代码中发生的事情:
第一行是一个函数的定义,它有两个参数:一些变量 x 和空列表,以及 returns 零。
第二行是一个函数的定义,它也有两个参数:一些变量x和以x开头的列表,以及returns 1 + count(x, xs)
对于这个例子,我想生成这样的代码:
const count = (x, list) => {
match(x, list) => (
(x, []) => {...}
(x, [ x : xs ]) => {...}
(x, [ y : xs ]) => {...}
)
}
如何将这种模式匹配正确展开为ifs
和ors
?
一般情况
有 proposal for Pattern Matching in ECMAScript,但截至 2018 年还处于非常早期的阶段。
目前,实现部分仅列出:
- Babel Plugin
- Sweet.js macro (NOTE: this isn't based on the proposal, this proposal is partially based on it!)
案例列表
const count = list => {
const [x, ...xs] = list;
if (x === undefined) {
return 0;
} else if (xs === undefined) {
return 1;
} else {
return 1 + count(xs);
}
}
使用ex-patterns
,您可以按如下方式编写您的示例。您需要使用包附带的占位符名称(_
、A
、B
、C
、... Z
)但您可以重命名具有解构的回调函数中的匹配变量(包含所有命名匹配项的对象作为第一个参数传递给回调函数)。
import { when, then, Y, _, tail, end } from 'ex-patterns';
const count = list => (
when(list)
([], then(() => 0)) // match empty array
([_], then(() => 1)) // match array with (any) 1 element
([_, tail(Y)], then(({ Y: xs }) => 1 + count(xs))) // match array and capture tail
(end);
);
这也涵盖了 list = [undefined, 'foo', 'bar']
的情况,我认为接受的答案不会涵盖这种情况。
为了提高代码效率,您可以使用 Immutable.js List
而不是数组来调用 count
(无需更改)。在这种情况下,不需要在每个循环中将数组的 tail
部分切片并复制到新数组中。
与您提到的包一样,浏览器本身不会 运行,但我想这不是现代捆绑工具的主要障碍。
这是文档:https://moritzploss.github.io/ex-patterns/
免责声明:我是 ex-patterns
的作者 :)
我需要模式匹配并做了一些适合我的东西。
const count = patroon(
[_], ([, ...xs]) => 1 + count(xs),
[], 0
)
count([0,1,2,3])
4
有关更多用法示例,请参阅自述文件。