Scala, C # эквивалент F # активных шаблонов - PullRequest
17 голосов
/ 13 января 2011

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 # этот уровень краткости кажется труднодостижимым, но, может быть, какой-то умный трюк с использованием неявного преобразования или так может помочь?

Ответы [ 4 ]

19 голосов
/ 13 января 2011

Отказ от ответственности: я не знаю F #.

В scala, если у значения есть метод с именем unapply (или unapplySeq), он будет использоваться при сопоставлении с образцом.

Определение unapply:

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 создает одноэлементный объект с тем же именем (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
7 голосов
/ 13 января 2011

Эту функцию можно реализовать с помощью функции scala, называемой Extractors .

. Для вашего четного / нечетного примера:

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")
}
5 голосов
/ 13 января 2011

Вы можете достичь того же в 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

Ваш второй пример тоже работает.Вы должны вернуть объекты DateTime из метода unapply.Я предоставляю ссылку здесь , где вы можете прочитать больше по теме.

1 голос
/ 13 марта 2016

Я заметил, что никто не добавил код C # для этого, поэтому я попытался воспроизвести эту функцию в коде здесь: http://siderite.blogspot.com/2016/03/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");
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...