Scala: Обходной путь для непараметризуемого экстрактора - PullRequest
0 голосов
/ 11 мая 2018

Поскольку экстракторы не могут принимать пользовательские параметры (как было сказано в Переполнение стека: можно ли настроить экстракторы ... ), я пытаюсь найти альтернативный способ решения следующей проблемы.

У меня много переводов, которые можно комбинировать.В моем фрагменте кода измерение может быть объединено с фактором.Например "width multiplied by 2".Но это также может быть "width" (без умножения).И будут еще такие случаи.Я пытаюсь классифицировать эти строковые входы, используя сопоставление с образцом."width" и "width multiplied by x" должны классифицироваться как «ширина» (ключ "w"), "height" и "height multiplied by x" должны классифицироваться как «высота» (ключ "h") и т. Д.

Это должно быть сделано последним match в следующем примере фрагмента кода, который будет содержать много случаев (6 в примере фрагмента кода), каждый из которых должен принимать параметр key: String ("w", "h", "l", "r", "t", "b").

Я пытаюсь достичь передачи ключа (то есть "w", "h", "l", "r", "t", "b" и т. Д.) К делу Untranslation(v).Но, очевидно, я не могу этого сделать (функция unapply может принимать неявные параметры, но без дополнительных явных).

Теперь я пытаюсь найти альтернативный, но все же лаконичный способ классификации моих строковых входов.

implicit val translations = Map(
  "w" -> "width",
  "h" -> "height",
  "l" -> "left",
  "r" -> "right",
  "t" -> "top",
  "b" -> "bottom",
  // + some more translations
  "m" -> "multiplied by"
)

sealed trait CommandType
object CommandType {
  case object Unmodified extends CommandType
  case object Multiplied extends CommandType
  // ...
}

object Untranslation {
  def unapply(s: String)(implicit t: Map[String, String]): Option[CommandType] = {
    val key: String = "w" // should be variable by case
    val a: List[String] = t(key).split(" ").toList
    val b: List[String] = t("m").split(" ").toList
    val ab: List[String] = a ++ b
    s.split(" ").toList match {
      case `a` => Some(CommandType.Unmodified)
      case `ab` :+ value => Some(CommandType.Multiplied)
      // + some more cases
      case _ => None
    }
  }
}

"width multiplied by 2" match {
  case Untranslation(v) => println(v) // here I would like to pass the key ("w"/"h"/"l"/...)
  case _ => println("nothing found")
}
// outputs: Multiplied

Ответы [ 3 ]

0 голосов
/ 14 мая 2018

Независимо от того, хотите вы реализовать правильный синтаксический анализатор или нет, вы должны по крайней мере создать структуры данных, которые могут точно представлять ваши команды.

Вот одно предложение:

sealed trait Dimension {
  def translate(implicit t: Map[Symbol, String]) = 
    t(Symbol(toString.toLowerCase))
}
case object W extends Dimension
case object H extends Dimension
case object L extends Dimension
case object R extends Dimension
case object T extends Dimension
case object B extends Dimension
object Dimension {
  def all = List(W, H, L, R, T, B)
}

sealed trait CommandModifier {
  def translate(implicit t: Map[Symbol, String]): String
}
case object Unmodified extends CommandModifier {
  def translate(implicit t: Map[Symbol, String]) = ""
}
case class Multiplied(factor: Int) extends CommandModifier {
  def translate(implicit t: Map[Symbol, String]) = t('m) + " " + factor
}


case class Command(dim: Dimension, mod: CommandModifier) {
  def translate(implicit t: Map[Symbol, String]) = 
    dim.translate + " " + mod.translate
}

ACommand - это правильный класс падежа, который имеет размер и модификатор в качестве члена.CommandModifier моделируются как отдельная запечатанная черта.Dimension s (ширина, высота и т. Д.) - это просто перечисление.Короткие строки магического значения "w", "h" были заменены символами 'w, 'h и т. Д.

Теперь вы можете реализовать экстрактор Untranslation, который извлекает всю команду за один раз.и, следовательно, не требует никаких дополнительных параметров:

object Untranslation {
  def unapply(s: String)(implicit t: Map[Symbol, String]): Option[Command] = {
    val sParts = s.split(" ").toList
    for (dim <- Dimension.all) {
      val a: List[String] = dim.translate.split(" ").toList
      val b: List[String] = t('m).split(" ").toList
      val ab: List[String] = a ++ b
      sParts match {
        case `a` => return Some(Command(dim, Unmodified))
        case `ab` :+ value => return Some(Command(dim, Multiplied(value.toInt)))
        // + some more cases
        case _ => None
      }
    }
    None
  }
}

Небольшой пример.Вот как вы можете анализировать и записывать команды на английском и немецком языках.Во-первых, два словаря, которые отображают формальные символы в фактические слова на естественном языке:

val En = Map(
  'w -> "width",
  'h -> "height",
  'l -> "left",
  'r -> "right",
  't -> "top",
  'b -> "bottom",
  'm -> "multiplied by"
)

val De = Map(
  'w -> "Breite",
  'h -> "Höhe",
  'l -> "links",
  'r -> "rechts",
  't -> "oben",
  'b -> "unten",
  'm -> "mal"
)

Используя En -директион, вы можете теперь сопоставлять команды на английском языке:

for (example <- List(
  "width multiplied by 2",
  "top",
  "height multiplied by 42"
)) {
  println("-" * 60)
  implicit val lang = En
  example match {
    case Untranslation(v) => {
      println(v)
      println(v.translate(En))
      println(v.translate(De))
    }
    case _ => println("invalid command")
  }
}

Вот что соответствует, и как оно переводится на английский и немецкий:

------------------------------------------------------------
Command(W,Multiplied(2))
width multiplied by 2
Breite mal 2
------------------------------------------------------------
Command(T,Unmodified)
top 
oben 
------------------------------------------------------------
Command(H,Multiplied(42))
height multiplied by 42
Höhe mal 42

То же самое работает наоборот, с немецкого на английский:

for (example <- List(
  "Breite mal 2",
  "oben",
  "Höhe mal 42"
)) {
  println("-" * 60)
  implicit val lang = De
  example match {
    case Untranslation(v) => {
      println(v)
      println(v.translate(En))
      println(v.translate(De))
    }
    case _ => println("invalid command")
  }
}

Вывод:

------------------------------------------------------------
Command(W,Multiplied(2))
width multiplied by 2
Breite mal 2
------------------------------------------------------------
Command(T,Unmodified)
top 
oben 
------------------------------------------------------------
Command(H,Multiplied(42))
height multiplied by 42
Höhe mal 42

Обратите внимание, что весь подход с разделением строк и сопоставлением с образцом является чрезвычайно хрупким и не масштабируется вообще.Если вы хотите сделать это правильно, вы должны написать правильный синтаксический анализатор (либо с помощью генератора синтаксических анализаторов, либо с помощью библиотеки комбинатора синтаксического анализатора).

0 голосов
/ 16 мая 2018

Возможно, ваш вопрос дублирует этот .

package ex

import language._

object units extends Dynamic {
  class Helper(kind: String) {
    val kindof = kind match {
      case "s" => Symbols.s
      case "m" => Symbols.m
    }
    def value = raw"(\d+)${kindof.name}".r
    object pair {
      def unapply(s: String): Option[(Int, Symbol)] =
        value.unapplySeq(s).map(vs => (vs.head.toInt, kindof))
    }
  }
  def selectDynamic(kind: String) = new Helper(kind)
  object Symbols { val s = 'sec ; val m = 'min }
}

object Test {
  def main(args: Array[String]): Unit = println {
    args(0) match {
      case units.s.pair(x, s) => s"$x ${s.name}"
      case units.s.value(x) => s"$x seconds"
      case units.m.value(x) => s"$x minutes"
    }
  }
}

Настройка встроена в выбор в выражении регистра.Эта строка используется для создания желаемого экстрактора.

$ scalac ex.scala && scala ex.Test 24sec
24 sec

$ scalac ex.scala && scala ex.Test 60min
60 minutes
0 голосов
/ 14 мая 2018

Вы можете легко создать параметризованный class для экстракторов вместо object:

class Untranslation(val key: String) {
  def unapply(s: String)(implicit t: Map[String, String]): Option[CommandType] = {
    val a: List[String] = t(key).split(" ").toList
    val b: List[String] = t("m").split(" ").toList
    val ab: List[String] = a ++ b
    s.split(" ").toList match {
      case `a` => Some(CommandType.Unmodified)
      case `ab` :+ value => Some(CommandType.Multiplied)
      // + some more cases
      case _ => None
    }
  }
}

Для match, экстрактор должен иметь стабильный идентификатор, что можно сделать, назначивдо val (поэтому, к сожалению, вам нужна дополнительная строка для каждого ключа, но, конечно, они могут использоваться в нескольких совпадениях):

val UntranslationW = new Untranslation("w")
val UntranslationT = new Untranslation("t")
...

"width multiplied by 2" match {
  case UntranslationW(v) => ...
  case UntranslationT(v) => ...
  case _ => println("nothing found")
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...