Scala:不可参数化提取器的解决方案

Scala:不可参数化提取器的解决方案,scala,pattern-matching,extractor,unapply,Scala,Pattern Matching,Extractor,Unapply,由于提取器无法获取自定义参数(如中所述),因此我尝试找到解决以下问题的替代方法 我有很多可以合并的翻译。在我的代码片段中,维度可以与因子组合。例如,“宽度乘以2”。但它也可以是“宽度”(不乘以)。还有更多类似的案例。我尝试使用模式匹配对这些字符串输入进行分类“宽度”和“宽度乘以x”应归类为“宽度”(键“w”),“高度”和“高度乘以x”应归类为“高度”(键“h”),依此类推 这应该由下面示例代码段中的最后一个匹配来完成,它将包含许多情况(示例代码段中有6个),每个情况都应该采用键:String参数

由于提取器无法获取自定义参数(如中所述),因此我尝试找到解决以下问题的替代方法

我有很多可以合并的翻译。在我的代码片段中,维度可以与因子组合。例如,
“宽度乘以2”
。但它也可以是
“宽度”
(不乘以)。还有更多类似的案例。我尝试使用模式匹配对这些字符串输入进行分类<代码>“宽度”和
“宽度乘以x”
应归类为“宽度”(键
“w”
),
“高度”
“高度乘以x”
应归类为“高度”(键
“h”
),依此类推

这应该由下面示例代码段中的最后一个
匹配
来完成,它将包含许多情况(示例代码段中有6个),每个情况都应该采用
键:String
参数(
“w”
“h”
“l”
“r”
“t”
“b”

我试图实现的是将钥匙(即
“w”
“h”
“l”
“r”
“t”
“b”
等)传递到案例
非翻译(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

您可以轻松地为提取器创建参数化的
,而不是
对象

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
    }
  }
}
要进行
匹配
,提取器需要有一个稳定的标识符,这可以通过将其分配给
val
来完成(因此不幸的是,每个键都需要一个额外的行,但它们当然可以用于多个匹配):


无论您是否想要实现一个合适的解析器,您至少应该创建能够忠实地表示您的命令的数据结构

以下是一项建议:

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
}
命令
是一个适当的case类,其成员为维度和修饰符。
CommandModifier
s被建模为一个单独的密封特征。
维度
s(宽度、高度等)本质上只是一个枚举。短魔术值字符串
“w”
“h”
已被符号
'w
'h
等替换

现在,您可以实现一个
反翻译
提取器,它一次提取整个命令,因此不需要任何其他参数:

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
  }
}
使用
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")
  }
}
从德语到英语,情况正好相反:

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")
  }
}

请注意,使用字符串拆分和模式匹配的整个方法非常脆弱,根本无法扩展。如果您想正确地执行此操作,您必须编写一个正确的解析器(使用解析器生成器或解析器组合器库)。

您的问题可能重复

自定义将内置到案例表达式中的选择中。该字符串用于构造所需的提取器

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

$ scalac ex.scala && scala ex.Test 60min
60 minutes

我一点也不明白你在那里想干什么。如果你想用“命令”做一些事情,为什么不正确地解析它,然后相应地解释解析后的结构呢?你为什么要“分类”任何东西?为什么您不能通过检查命令是否使用某个单词启动?我甚至不明白你所说的“翻译”是什么意思:你指的是几何翻译(似乎是半真半假的,因为你在谈论宽度、高度和因素等),还是指语言之间的翻译(哪种语言?)@Andreytukin,我想我不是OP,但我认为这是关于不同的人类语言。我认为目标是将一个已知固定语言中的字符串解析为一个内部抽象表示。计划似乎是
t
包含所有相关部分的翻译。所以对于英语来说,t(“w”)是
“宽度”
t(“m”)是
“乘以”
。看起来OP希望使用模式匹配来解析将映射到
“wm(value)”
“hm(value)”
等的字符串,以及只映射到
“w”
或只映射到
“h”
等的字符串。@SergGr如果是这种情况,然后OP应该阅读关于具有注意机制的堆叠式长-短期记忆神经网络以及其他不具有注意机制的内容。。。试图通过一些复杂的硬编码模式匹配来实现这一点,似乎与其他人正在做的一切都相去甚远。@Andreytukin,我同意现实世界中的人类语言比这个简单的结构复杂得多,但可能是被解析的文本实际上是一种伪自然语言,完全符合这种结构。更重要的是,你问了这个问题的意思,在我看来,我上面的解释非常符合代码和文本。谢谢。这几乎就是我所需要的,但是我的等价物将以符号的形式存储。如何用符号替换
units.s
units.m
等?(键入
Symbol
Symbol(种类)
。我需要写入
单位。MyObject。Symbol
其中
Symbol
是定义为对象成员的符号。这给了我一个编译器错误。我不能再取消你的想法。如果
TheSymbol
(指您的单位?)是一个稳定的引用,您可以有一个
(值,单位)
的常规提取器和
大小写提取器(值,TheSymbol)
,大写的symbol(或反勾号)使其与该值匹配。可能我不知道您想要什么语法。应该可以使用shapeless在模式中的路径之间提供编译时安全性
------------------------------------------------------------
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
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