-1

由于提取器不能采用自定义参数(如Stack Overflow 中的回答:可以自定义提取器...),我尝试找到解决以下问题的替代方法。

我有很多可以组合的翻译。在我的代码片段中,维度可以与因子结合。例如"width multiplied by 2". 但它也可以是"width"(不相乘的)。而且还会有更多类似的案例。我尝试使用模式匹配对这些字符串输入进行分类。"width""width multiplied by x"应归类为“宽度”(key "w"),"height""height multiplied by x"归类为“高度”(key "h"),以此类推。

这应该由match以下示例代码片段中的最后一个完成,该示例代码片段将包含许多案例(示例代码片段中的 6 个),每个案例都应带有一个key: String参数("w", "h", "l", "r", "t", "b")。

我试图实现的是将密钥(即"w", "h", "l", "r", "t","b"等)传递给 case 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
4

3 回答 3

3

您可以轻松地为提取器创建参数化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
    }
  }
}

To match,提取器需要有一个稳定的标识符,这可以通过将其分配给 a 来完成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")
}
于 2018-05-14T11:26:48.237 回答
2

可能您的问题与此重复。

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
于 2018-05-16T10:12:57.243 回答
1

不管你是否想实现一个合适的解析器,你至少应该创建可以忠实地表示你的命令的数据结构。

这是一个建议:

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是一个适当的案例类,它具有维度和修饰符作为成员。CommandModifiers 被建模为一个单独的密封特征。s(宽度、Dimension高度等)本质上只是一个枚举。短魔法值字符串"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-dictionary,您现在可以匹配英文命令:

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

请注意,字符串拆分和模式匹配的整个方法非常脆弱,根本无法扩展。如果你想正确地做到这一点,你必须编写一个适当的解析器(使用解析器生成器,或者使用解析器组合库)。

于 2018-05-14T14:50:09.147 回答