【问题标题】:Pattern matching expressions in JavascriptJavascript 中的模式匹配表达式
【发布时间】:2019-05-02 13:52:08
【问题描述】:

我正在用 JS 进行函数式编程。最近开始使用 Daggy 来完成简单的模式匹配,但我觉得我还没有完全掌握。

鉴于此代码:

  if(a === 1 && !b) {
   do(y)
  }

   if(b === 3 && c === 2) {
    do(z)
  }

  if (a) { 
    do(x) 
  }

有没有办法将这种凝聚力改进为类似的东西?:

   when [a === 1 && !b] : do(y)
   when [a] : do(x)
   when [b === 3 && c === 2]: do(z)

【问题讨论】:

  • do 是保留关键字...你会得到一个SyntaxError,而when 不是关键字
  • 什么是“凝聚力”?
  • 如果块中只有一条语句,则可以放弃花括号:if (a === 1 && !b) go(y);
  • “改进”在什么意义上?您所做的只是将if 更改为when,在条件下使用[] 而不是(),并使用: 而不是完全关闭它(if 没有阻塞)或使用块。如果你喜欢 (pastebin.com/TxMnum54),你可以在一行上写 ifs,或者如果你喜欢 (pastebin.com/FgjfGufe) 甚至滥用 && 运算符,这两者都不是更“实用”,也不是(恕我直言)改进. (我已将do 更改为foo,请参阅@CertainPerformance 的评论)。还添加了缺少的分号,尤其是第二种形式不适用于 ASI。
  • 如果这些是独家的,你会想要else

标签: javascript functional-programming


【解决方案1】:

JavaScript 没有您可能正在谈论的那种模式匹配。有an active proposal 可以使用case/when 添加它,这是该提案的一个示例:

const res = await fetch(jsonService)
case (res) {
  when {status: 200, headers: {'Content-Length': s}} -> {
    console.log(`size is ${s}`)
  }
  when {status: 404} -> {
    console.log('JSON not found')
  }
  when {status} if (status >= 400) -> {
    throw new RequestError(res)
  }
}

不过,它目前仅处于 the process 的第 1 阶段,因此它可能无法继续进行,在继续之前可能会发生根本性的变化,并且可能需要数年时间才能完成这些阶段并进入该语言。 Babel plugin 有工作。

不过,恐怕我还不清楚如何将其应用于您的示例,因为它似乎需要 case 的操作数。

与此同时,如果您正在寻找简洁,一系列if/else if 可能会相当简洁:

if (a === 1 && !b)           foo(y);
else if (a)                  foo(x);
else if (b === 3 && c === 2) foo(z);

或者 JavaScript 的 switch 异常灵活(实际上只是另一种编写 if/else if 的方式):

switch (true) {
    case a === 1 && !b:      foo(y); break;
    case a:                  foo(x); break;
    case b === 3 && c === 2: foo(z); break;
}

(我不是在提倡,只是指出这是一种选择。)

【讨论】:

  • 由于 these proposed features 还没有被添加到 JavaScript,我使用 ex-patterns 库代替。它有一个非常相似的match / when 语法来进行模式匹配。
【解决方案2】:

您可以为您的数据创建一个包装类,然后使用函数检查条件并在满足特定条件时执行操作。

简单易用,无需任何库。

class typeWrap {

  constructor(obj) {
    this.var  =  obj;
    this.done = false;
  }

  when (condition, doFn) {

    if (!this.done && condition(this.var)) {
      this.done = true;
      doFn(this.var);
    }
    return this;
  }
}

const data = new typeWrap({
  b: 3, c : 9
});

data
  .when(
    d => d.a === 1 && !d.b,
    () => console.log('C1 => y')
  )
  .when(
    d => d.b === 3 && d.c !== 2,
    () => console.log('C2 => z')
  )
  .when(
    d => d.a,
    () => console.log('C3 => x')
  )
  .when(
    d => true,
    () => console.log('Last stop')
  );

【讨论】:

    【解决方案3】:

    当然,你可以用 Daggy 定义 Maybe

        const Maybe = daggy.taggedSum('Option', {
            Just: ['a'],
            Nothing: []
        })
    

    然后在其上定义一个原型函数alt,它基本上可以回退到传递的值

        // alt :: Alt f => f a ~> f a -> f a
        Maybe.prototype.alt = function(o): Maybe<T> {
            return this.cata({
              Just: _ => this,
              Nothing: () => o
            })
          }
    

    所以我们可以用它做一些模式匹配或类似的事情

        function match(comparator, res) {
          switch (comparator()) {
            case true: return Maybe.of(res)
            case false: return Maybe.Nothing
            default: return Maybe.Nothing
          }
    
        }
    
    
        MaybeF.Nothing
          .alt(match(() => a === 1 && !b, () => y))
          .alt(match(() => a, () => x))
          .alt(match(() => b === 3 && c === 2, () => z))
    

    【讨论】:

      【解决方案4】:

      Patroon 的开发者和维护者。我一直在寻找 js 风格的模式匹配,不得不自己构建。这就是您的示例使用 patroon 库的样子:

      const isNil = x => !(x != null)
      
      patroon(
        {a: 1, b: isNil}, y,
        {b: 3, c: 2}, z,
        {a: _}, x
      )({a, b, c})
      

      https://github.com/bas080/patroon

      【讨论】:

        猜你喜欢
        • 2020-04-29
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-12-03
        • 1970-01-01
        • 1970-01-01
        • 2013-06-14
        • 1970-01-01
        相关资源
        最近更新 更多