问题描述
正如我经常观察到的以及我经常实现 name
属性的方式,就是将其简单地建模为 String
.
As I frequently observe and how I often implement a name
attribute, is to simply model it as String
.
现在,如果名称必须遵循某种语法,即格式,该怎么办?在 Java 中,我可能会定义一个构造函数并检查其参数,例如:
What now, if the name has to follow a certain syntax, i.e. format? In Java I probably would define a constructor with a check on its arguments, something like:
public Name(str: String) {
if (str == null) throw new IllegalArgumentException("Str must not be null.");
if (!str.matches("name format expressed as regex")) throw new IllegalArgumentException("Str must match 'regex' but was " + str);
this.str = str;
}
在 Scala 中,我想出了以下解决方案:
In Scala I came up with the following solution:
import StdDef.Str
import StdDef.Bol
import StdDef.?
import scala.util.parsing.combinator.RegexParsers
final case class Name private (pfx: ?[Str] = None, sfx: Str) {
override def toString = pfx.mkString + sfx
}
object Name extends RegexParsers {
implicit def apply(str: Str): Name = parseAll(syntax, str) match {
case Success(res, _) => Name(res._1, res._2)
case rej: NoSuccess => error(rej.toString)
}
lazy val syntax = (prefix ?) ~! suffix
lazy val prefix = (("x" | "X") ~! hyph) ^^ { case a ~ b => a + b }
lazy val suffix = alpha ~! (alpha | digit | hyph *) ^^ { case a ~ b => a + b.mkString }
lazy val alpha: Parser[Str] = """\p{Alpha}""".r
lazy val digit: Parser[Str] = """\p{Digit}""".r
lazy val hyph: Parser[Str] = "-"
override lazy val skipWhitespace = false
}
我的意图是:
- 从其自然表示中组合一个
Name
,即一个String
值 - 在构建时检查其自然表示是否形成有效的
Name
. - 除了通过工厂方法
apply:(str:Str)Str
之外,禁止任何其他构造. - 使构造从其自然表示隐含,例如
val a: Name = "ISBN 978-0-9815316-4-9"
. - 根据语法元素将
Name
分解成各个部分. - 在消息中抛出错误,例如:
- Compose a
Name
from its natural representation, i.e. aString
value - Check whether its natural representation forms a valid
Name
at construction time. - Disallow any other construction than through the factory method
apply:(str:Str)Str
. - Make the construction from its natural representation implicit, e.g.
val a: Name = "ISBN 978-0-9815316-4-9"
. - Decompose a
Name
into its parts according to its syntactical elements. - Have errors being thrown with messages, such as:
===
--
^
[1.3] error: string matching regex `\p{Alpha}' expected but end of source found
我想知道您想出了什么解决方案.
在对主题进行了更多思考之后,我目前正在采取以下方法.
After giving the topic some more thoughts, I am currently taking the following approach.
Token.scala:
Token.scala:
abstract class Token {
val value: Str
}
object Token {
def apply[A <: Token](ctor: Str => A, syntax: Regex) = (value: Str) => value match {
case syntax() => ctor(value)
case _ => error("Value must match '" + syntax + "' but was '" + value + "'.")
}
}
Tokens.scala:
Tokens.scala:
final case class Group private (val value: Str) extends Token
final case class Name private (val value: Str) extends Token
trait Tokens {
import foo.{ bar => outer }
val Group = Token(outer.Group, """(?i)[a-z0-9-]++""".r)
val Name = Token(outer.Name, """(?i)(?:x-)?+[a-z0-9-]++""".r)
}
推荐答案
鉴于您已经习惯在 Java 中使用正则表达式,然后尝试在 Scala 中使用解析器解决同样的问题似乎有些过分.
Given that you'd be comfortable using a regex in Java, it seems like overkill to then try and solve the same problem with a parser in Scala.
>
坚持你在这里所知道的,但添加一个 Scala 扭曲来清理解决方案.Scala 中的正则表达式还定义了提取器,允许在模式匹配中使用它们:
Stick with what you know here, but add a Scala twist to clean up the solution a bit. Regexes in Scala also define extractors, allowing them to be used in a pattern match:
//triple-quote to make escaping easier, the .r makes it a regex
//Note how the value breaks normal naming conventions and starts in uppercase
//This is to avoid backticks when pattern matching
val TestRegex = """xxyyzz""".r
class Name(str: String) {
str match {
case Null => throw new IllegalArgumentException("Str must not be null")
case TestRegex => //do nothing
case _ => throw new IllegalArgumentException(
"Str must match 'regex' but was " + str)
}
}
免责声明:我没有实际测试这段代码,它可能包含错别字
这篇关于如何对字符串值格式的约束进行编码的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!