Cómo codificar una restricción en el formato de valores de cadena

Como observo con frecuencia y cómo a menudo implemento un name atributo, es simplemente modelarlo como String.

¿Y ahora qué, si el nombre tiene que seguir una determinada sintaxis, es decir, formato? En Java, probablemente definiría un constructor con una verificación de sus argumentos, algo como:

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;
}

En Scala se me ocurrió la siguiente solución:

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

}

Mis intenciones aquí son:

  1. Redactar un Name de su representación natural, es decir, un String VALORAMOS
  2. Compruebe si su representación natural forma una Name en el momento de la construcción.
  3. No permita ninguna otra construcción que no sea el método de fábrica. apply:(str:Str)Str.
  4. Hacer implícita la construcción a partir de su representación natural, p. Ej. val a: Name = "ISBN 978-0-9815316-4-9".
  5. Descomponer un Name en sus partes de acuerdo con sus elementos sintácticos.
  6. Tienen errores con mensajes, como:

===

--
^
[1.3] error: string matching regex `\p{Alpha}' expected but end of source found

Me gustaría saber qué soluciones se te ocurren.

Después de darle más ideas al tema, actualmente estoy adoptando el siguiente enfoque.

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:

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

preguntado el 27 de agosto de 11 a las 19:08

1 Respuestas

Dado que se sentiría cómodo usando una expresión regular en Java, parece excesivo intentar resolver el mismo problema con un analizador en Scala.

Quédese con lo que sabe aquí, pero agregue un toque de Scala para limpiar un poco la solución. Las expresiones regulares en Scala también definen extractores, lo que les permite ser utilizados en una coincidencia de patrones:

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

descargo de responsabilidad: en realidad no probé este código, puede contener errores tipográficos

Respondido 28 ago 11, 02:08

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.