繁体   English   中英

Scala、C# 等效于 F# 活动模式

[英]Scala, C# equivalent of F# active patterns

F# 具有使用户能够扩展模式匹配的功能:

let (|Even|Odd|) n = if n % 2 = 0 then Even else Odd
match 3 with | Odd -> printfn "odd"
             | Even -> printfn "even"

或:

(** Parsing date from any of the formats: MM/DD/YY, MM/DD/YYYY, YYYY-MM-DD *)
let parseDate = function
| ParseRegex "(\d{1,2})/(\d{1,2})/(\d{1,2})$" [Integer m; Integer d; Integer y]
   -> new DateTime(y + 2000, m, d)
| ParseRegex "(\d{1,2})/(\d{1,2})/(\d{3,4})" [Integer m; Integer d; Integer y]
   -> new DateTime(y, m, d)
| ParseRegex "(\d{1,4})-(\d{1,2})-(\d{1,2})" [Integer y; Integer m; Integer d]
   -> new DateTime(y, m, d)

神奇发生在 ParseRegex 模式中,其定义如下:

(** Definition of the MatchRegex active pattern *)
let (|ParseRegex|_|) rgx s = match Regex(rgx).Match(s) with
                         | m when not m.Success -> None
                         | m -> Some(List.tail [for x in m.Groups->x.Value])

ParseRegex 现在可以在任何地方使用,它的全部意义在于可以以非常简洁的方式解析出部分字符串。

Scala 有类似的功能吗?

在 C# 中,这种简洁程度似乎很难实现,但也许使用隐式转换的一些巧妙技巧会有所帮助?

免责声明:我不知道 F#。

在 Scala 中,如果一个值有一个名为 unapply(或 unapplySeq)的方法,那么它将被用于模式匹配。

不适用的定义是:

object Example {
    def unapply(t: T): Option[(T1, ..., Tn)] = ...
}

如果None返回则表示不匹配, Some((t1, ..., tn))表示匹配成功并且可以绑定到值 t1 到 tn

然后它可以用作:

value match {
  case Example(t1, ..., tn) => ...
}

如果匹配可以有动态数量的结果,则使用unapplySeq返回Option[List[T]]

最后,如果匹配时不需要绑定变量,则 unapply 可以只返回一个布尔值

例子:

val Date = ""(\d{1,2})/(\d{1,2})/(\d{1,2})$".r

"12/12/12" match {
   case Date(m, d, y) => ...
}

.r方法将字符串转换为正则表达式。 该类有一个 unapplySeq 方法,它匹配正则表达式中的组。

case class Person(name: String, age: Int)

somePerson match {
    case Person(name, age) => ...
}

在这种情况下,语法case class使用与构造函数参数匹配的 unapply 方法创建一个具有相同名称 (Person) 的单例对象。

更新:这里是如何定义偶数和奇数 首先,一些概括。 Even 和 Odd 都可以通过一个函数来表达,但是我们需要让这个函数符合提取器规范,即通过 unapply 应用它

class BooleanExtractor[T](f: T => Boolean) { 
  def unapply(t: T) = f(t)
}

现在,我们可以这样使用(显示两种方式)

val Even = new BooleanExtractor[Int](_ % 2 == 0)
object Odd extends BooleanExtractor[Int](_ % 2 == 1)

并使用:

scala> 3 match {
     |   case Even() => println("even")
     |   case Odd() => println("odd")
     | }
odd

您可以通过称为Extractors的 Scala 功能来实现此功能。

对于您的偶数/奇数示例:

object Even {
  def unapply(x:Int) = if (x % 2 == 0) Some(x) else None
}

object Odd {
  def unapply(x:Int) = if (x % 2 == 1) Some(x) else None
}

3 match {
  case Even(x) => println("even")
  case Odd(x) => println("odd")
}

您可以在 Scala 中实现相同的目标。 这个概念被称为提取器。 定义它们的语法似乎比在 F# 中更难看。 我将提供第一个示例:

scala> object Even {def unapply(z: Int) = (z%2 == 0)}                      
defined module Even    
scala> object Odd {def unapply(z: Int) = (z%2 != 0)}                       
defined module Odd    
scala> 2 match {
     | case Even() => "even";
     | case Odd() => "odd";
     | }
res10: java.lang.String = even

你的第二个例子也有效。 您必须从 unapply 方法返回 DateTime 对象。 在这里提供了一个链接,您可以其中阅读有关该主题的更多信息。

我注意到没有人为此添加 C# 代码,因此我尝试在此处的代码中重现该功能: https : //siderite.dev/blog/c-equivalent-to-f-active-patterns.html基本上是我创建的一两个辅助类可以让我编写这样的代码:

var apInt = Option<int>.From<string>(s =>
{
    int i;
    return System.Int32.TryParse(s, out i) 
        ? new Option<int>(i) 
        : Option<int>.Empty;
});

var apBool = Option<bool>.From<string>(s =>
{
    bool b;
    return System.Boolean.TryParse(s, out b)
        ? new Option<bool>(b)
        : Option<bool>.Empty;
});

var testParse = new Action<string>(s =>
{
    FluidFunc
        .Match(s)
        .With(apInt, r => Console.WriteLine($"The value is an int '{r}'"))
        .With(apBool, r => Console.WriteLine($"The value is an bool '{r}'"))
        .Else(v => Console.WriteLine($"The value '{v}' is something else"));
});

testParse("12");
testParse("true");
testParse("abc");

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM