I need to pass some options to a method, some of these options are optional (something like Object destructuring in JS).
My current code:
def initialize( arg1 : String, options = {} of Symbol => String )
opt = MyClass.get_option options, :opt1
#opt1 = !opt.empty? ? opt : "Def value"
opt = MyClass.get_option options, :opt2
#opt2 = !opt.empty? ? opt : "False"
# ...
end
def self.get_option( options, key : Symbol )
( options && options[key]? ) ? options[key].strip : ""
end
And I call it: MyClass.new "Arg", { opt2: "True", opt4: "123" }
It works but I'm looking for a better way. It would be useful to set the type of each option and to have default values directly in the function signature.
Using a NamedTuple seems a good way but I had problems with optional values - options : NamedTuple( opt1: String, opt2: Bool, opt3: String, opt4: Int ) | Nil = nil
Another way that I tried is with a struct but it seems to complicate the situation.
Crystal has optional and named method arguments as core language features, and does not require writing special code for handling the arguments. See the official documentation about Method arguments. In particular, here is an example:
def method(arg1 : String, *, opt1 = "Def value", opt2 = false)
The asterisk is not always needed, it only ensures that the following optional arguments can only be passed by name:
method("test", opt1: "other value", opt2: false)
Related
I have two lists. The first contains original product data as following:
data class InputProductData (val optionFamilyInput: String?, val optionCodeInput: String?, val optionDescriptionInput: String?)
val inputProductData = mutableListOf(
InputProductData("AAA", "111","Chimney with red bricks"),
InputProductData(null,"222","Two wide windows in the main floor"),
InputProductData("CCCC",null,"Beautiful door in green color"),
InputProductData("DDDD",null,"House with area 120 square meters"),
InputProductData(null,"555","Old wood windows")
)
Second list consists of customizing data. The list can have many identical option ids (first column).
data class CustomizingProductOption(val id: Int, val optionName: String, val optionCategory: String, val optionFamily: String?, val optionCode: String?, val searchPattern: String?, val outputValue: String)
val customizingProductOptions = mutableListOf(
CustomizingProductOption(10001, "Chimney", "Additional options", "^AAA$", "", "^Chimney with", "Available"),
CustomizingProductOption(10002, "Windows", "Basic options", "", "^222$", "^Two wide windows", "Available"),
CustomizingProductOption(10002, "Windows", "Basic options", "", "^555$", "wood windows$", "Available"),
CustomizingProductOption(10003, "Door color", "Basic options", "^CCCC$", "", "door in green color$", "green"),
CustomizingProductOption(10004, "House area", "Basic options", "^DDD", "", "120 square meters", "120")
)
The target is to check the product input data and to identify different product options. Whitin the following loop it is done by use of a business logic. There are 2 different constelations which can occure:
Option family + regex within option description
Option code + regex within option description
data class IndicatedOptions(val id: Int, val output: String)
val indicatedOptions: MutableList<IndicatedOptions> = mutableListOf()
for (i in 0 until inputProductData.size) {
for (k in 0 until customizingProductOptions.size) {
if(inputProductData[i].optionFamilyInput.toString().contains(Regex(customizingProductOptions[k].optionFamily.toString())) == true &&
inputProductData[i].optionDescriptionInput.toString().contains(Regex(customizingProductOptions[k].searchPattern.toString())) == true ||
inputProductData[i].optionCodeInput.toString().contains(Regex(customizingProductOptions[k].optionCode.toString())) == true &&
inputProductData[i].optionDescriptionInput.toString().contains(Regex(customizingProductOptions[k].searchPattern.toString())) == true) {
indicatedOptions.add(IndicatedOptions(customizingProductOptions[k].id, customizingProductOptions[k].outputValue))
}
}
}
println("\n--- ALL INDICATED OPTIONS ---")
indicatedOptions.forEach { println(it) }
val indicatedOptionsUnique = indicatedOptions.distinct().sortedBy { it.id }
println("\n--- UNIQUE INDICATED OPTIONS ---")
indicatedOptionsUnique.forEach {println(it)}
QUESTION: Do you see any ways to optimize this codein order to get it more faster?
First, the "regex" code looks broken. Why do you test if a String contains a Regex? This is the wrong way around you would normally test a Regex to see if the target string is matched by the Regex.
Ideas for performance
Precompile your Regex in the constructor of CustomizingProductOption
Your if logic is 4 logic ANDs. The code executes first to last in a logical expressions, so arrange the first test to be the one that is most selective (i.e. have the least number of matches).
Ideas for readability
use proper streams, e.g. inputProductData.map { customizingProductOptions.filter { LOGIC } }...
Stop using unnecessary toString() on something that is already a String
Stop testing if a boolean expression ==true
Now with sample code:
# Use Regex class here
data class CustomizingProductOption(
val id: Int, val optionName: String, val optionCategory: String,
val optionFamily: Regex?, val optionCode: Regex?, val searchPattern: String?,
val outputValue: String,
)
# Instantiate like this:
CustomizingProductOption(
10001, "Chimney", "Additional options", Regex("^AAA$"),
null, "^Chimney with", "Available",
),
# main code
val indicatedOptions: List<IndicatedOptions> = inputProductData.map { productData ->
customizingProductOptions.filter { option -> // this filter will only return matching options to product data
productData.optionFamilyInput != null && option.optionFamily?.containsMatchIn(productData.optionFamilyInput) ?: false
//&& other conditions
}
.map {option -> // transform to your desired output
IndicatedOptions(
option.id,
option.outputValue,
)
}
}.flatten() // you need this to flatten List<List<IndicatedOptions>>
I am starting to learn Scala and want to use regular expressions to match a character from a string so I can populate a mutable map of characters and their value (String values, numbers etc) and then print the result.
I have looked at several answers on SO and gone over the Scala Docs but can't seem to get this right. I have a short Lexer class that currently looks like this:
class Lexer {
private val tokens: mutable.Map[String, Any] = collection.mutable.Map()
private def checkCharacter(char: Character): Unit = {
val Operator = "[-+*/^%=()]".r
val Digit = "[\\d]".r
val Other = "[^\\d][^-+*/^%=()]".r
char.toString match {
case Operator(c) => tokens(c) = "Operator"
case Digit(c) => tokens(c) = Integer.parseInt(c)
case Other(c) => tokens(c) = "Other" // Temp value, write function for this
}
}
def lex(input: String): Unit = {
val inputArray = input.toArray
for (s <- inputArray)
checkCharacter(s)
for((key, value) <- tokens)
println(key + ": " + value)
}
}
I'm pretty confused by the sort of strange method syntax, Operator(c), that I have seen being used to handle the value to match and am also unsure if this is the correct way to use regex in Scala. I think what I want this code to do is clear, I'd really appreciate some help understanding this. If more info is needed I will supply what I can
This official doc has lot's of examples: https://www.scala-lang.org/api/2.12.1/scala/util/matching/Regex.html. What might be confusing is the type of the regular expression and its use in pattern matching...
You can construct a regex from any string by using .r:
scala> val regex = "(something)".r
regex: scala.util.matching.Regex = (something)
Your regex becomes an object that has a few useful methods to be able to find matching groups like findAllIn.
In Scala it's idiomatic to use pattern matching for safe extraction of values, thus Regex class also has unapplySeq method to support pattern matching. This makes it an extractor object. You can use it directly (not common):
scala> regex.unapplySeq("something")
res1: Option[List[String]] = Some(List(something))
or you can let Scala compiler call it for you when you do pattern matching:
scala> "something" match {
| case regex(x) => x
| case _ => ???
| }
res2: String = something
You might ask why exactly this return type on unapply/unapplySeq. The doc explains it very well:
The return type of an unapply should be chosen as follows:
If it is just a test, return a Boolean. For instance case even().
If it returns a single sub-value of type T, return an Option[T].
If you want to return several sub-values T1,...,Tn, group them in an optional tuple Option[(T1,...,Tn)].
Sometimes, the number of values to extract isn’t fixed and we would
like to return an arbitrary number of values, depending on the input.
For this use case, you can define extractors with an unapplySeq method
which returns an Option[Seq[T]]. Common examples of these patterns
include deconstructing a List using case List(x, y, z) => and
decomposing a String using a regular expression Regex, such as case
r(name, remainingFields # _*) =>
In short your regex might match one or more groups, thus you need to return a list/seq. It has to be wrapped in an Option to comply with extractor contract.
The way you are using regex is correct, I would just map your function over the input array to avoid creating mutable maps. Perhaps something like this:
class Lexer {
private def getCharacterType(char: Character): Any = {
val Operator = "([-+*/^%=()])".r
val Digit = "([\\d])".r
//val Other = "[^\\d][^-+*/^%=()]".r
char.toString match {
case Operator(c) => "Operator"
case Digit(c) => Integer.parseInt(c)
case _ => "Other" // Temp value, write function for this
}
}
def lex(input: String): Unit = {
val inputArray = input.toArray
val tokens = inputArray.map(x => x -> getCharacterType(x))
for((key, value) <- tokens)
println(key + ": " + value)
}
}
scala> val l = new Lexer()
l: Lexer = Lexer#60f662bd
scala> l.lex("a-1")
a: Other
-: Operator
1: 1
I have a list in Groovy which contains the names in the below format:
My_name_is_Jack
My_name_is_Rock
My_name_is_Sunn
How can I trim the list and get only the last part of it; i.e. Names - Jack, Rock and Sunn. (Please note that the names are only 4 characters long)
Here you go with either one of the approach.
You can use sustring with lastIndexOf
or replace method to remove My_name_is_ with empty string
Script (using the first approach):
def list = ['My_name_is_Jack', 'My_name_is_Rock', 'My_name_is_Sunn']
//Closure to get the name
def getName = { s -> s.substring(s.lastIndexOf('_')+1, s.size()) }
println list.collect{getName it}
If you want to use replace, then use below closure.
def getName = { s -> s.replace('My_name_is_','') }
You can quickly try it online demo
Or
def list = ['My_name_is_Jack', 'My_name_is_Rock', 'My_name_is_Sunn']
println list*.split('_')*.getAt(-1)
You can either remove the common prefix:
def names = [ "My_name_is_Jack", "My_name_is_Rock", "My_name_is_Sunn", ]
assert ['Jack', 'Rock', 'Sunn'] == names*.replaceFirst('My_name_is_','')
or since you are actually interrested in the last four chars, you can also take those:
assert ['Jack', 'Rock', 'Sunn'] == names*.getAt(-4..-1)
As I frequently observe and how I often implement a name attribute, is to simply model it as String.
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;
}
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
}
My intents here are:
Compose a Name from its natural representation, i.e. a String 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
I would like to know what solutions you come up with.
After giving the topic some more thoughts, I am currently taking the following approach.
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)
}
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.
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)
}
}
disclaimer: I didn't actually test this code, it may contain typos
Consider this (very ugly code):
object ExternalReferences2 {
import java.util.regex._
implicit def symbol2string(sym: Symbol) = sym.name
object Mapping {
def fromXml(mapping: scala.xml.NodeSeq) = {
new Mapping(mapping \ 'vendor text,
mapping \ 'match text,
mapping \ 'format text)
}
}
case class Mapping(vendor: String,
matches: String,
format: String) extends PartialFunction[String, String] {
private val pattern = Pattern.compile(matches)
private var _currentMatcher: Matcher = null
private def currentMatcher =
{ println("Getting matcher: " + _currentMatcher); _currentMatcher }
private def currentMatcher_=(matcher: Matcher) =
{ println("Setting matcher: " + matcher); _currentMatcher = matcher }
def isDefinedAt(entity: String) =
{ currentMatcher = pattern.matcher(entity); currentMatcher.matches }
def apply(entity: String) = apply
def apply = {
val range = 0 until currentMatcher.groupCount()
val groups = range
map (currentMatcher.group(_))
filterNot (_ == null)
map (_.replace('.', '/'))
format.format(groups: _*)
}
}
val config =
<external-links>
<mapping>
<vendor>OpenJDK</vendor>
<match>{ """^(javax?|sunw?|com.sun|org\.(ietf\.jgss|omg|w3c\.dom|xml\.sax))(\.[^.]+)+$""" }</match>
<format>{ "http://download.oracle.com/javase/7/docs/api/%s.html" }</format>
</mapping>
</external-links>
def getLinkNew(entity: String) =
(config \ 'mapping)
collectFirst({ case m => Mapping.fromXml(m)})
map(_.apply)
def getLinkOld(entity: String) =
(config \ 'mapping).view
map(m => Mapping.fromXml(m))
find(_.isDefinedAt(entity))
map(_.apply)
}
I tried to improve the getLinkOld method by using collectFirst as shown in getLinkNew, but I always get a NullPointerException because _currentMatcher is still set to null
scala> ExternalReferences2.getLinkNew("java.util.Date")
Getting matcher: null
java.lang.NullPointerException
at ExternalReferences2$Mapping.apply(<console>:32)
at ExternalReferences2$$anonfun$getLinkNew$2.apply(<console>:58)
at ExternalReferences2$$anonfun$getLinkNew$2.apply(<console>:58)
at scala.Option.map(Option.scala:131)
at ExternalReferences2$.getLinkNew(<console>:58)
at .<init>(<console>:13)
at .<clinit>(<console>)
at .<init>(<console>:11)
at .<clinit>(<console>)
while it works perfectly with getLinkOld.
What is the problem here?
Your matcher is created as a side-effect in isDefined. Passing side effecting functions to routine such as map is usually a recipe for disaster, but this is not even what happens here. Your code requires isDefined to have been called just before apply is, with the same argument. That makes your code very fragile, and that is what you should change.
Clients of PartialFunction do not have to do follow that protocol in general. Imagine for instance
if (f.isDefinedAt(x) && f.isDefinedAt(y)) {fx = f(x); fy = f(y)}.
And here the code that calls apply is not even yours, but the collection classes', so you do not control what happens.
Your specific problem in getLinkNew is that isDefined is simply never called.The PartialFunction argument of collectFirst is {case m => ...}. The isDefined that is called is the isDefined of this function. As m is an irrefutable pattern, it is allways true, and collectFirst will always return the first element if there is one. That the partial function returns another partial function (the Mapping) which happens not to be defined at m, is irrelevant.
Edit - Possible workaround
A very light change would be to check whether a matcher is available and create it if it is not. Better, keep the entity string that has been used to create it too, so that you can check it is the proper one. This should make the side effect benign as long as there is no multithreading. But the way, do not use null, use Option, so the compiler will not let you ignore the possibility that it may be None.
var _currentMatcher : Option[(String, Matcher)] = None
def currentMatcher(entity: String) : Matcher = _currentMatcher match{
case Some(e,m) if e == entity => m
case _ => {
_currentMatcher = (entity, pattern.matcher(entity))
_currentmatcher._2
}
}
Edit again. Stupid me
Sorry, the so called workaround indeed makes the class safer, but it does not make the collectFirst solution work. Again, the case m => partial function is always defined (note: entity does not even appears in your getLinkNew code, which should be worrying). The problem is that one would need a PartialFunction of a NodeSeq (not of entity, which will be known to the function, but not passed as argument). isDefined will be called, then apply. The pattern and the matcher depends on the NodeSeq, so they cannnot be created beforehand, but only in isDefined and/or apply. In the same spirit, you can cache what is computed in isDefined to reuse in Apply. This is definitely not pretty
def linkFor(entity: String) = new PartialFunction[NodeSeq, String] {
var _matcher : Option[String, Matcher] = None
def matcher(regexp: String) = _matcher match {
case Some(r, m) where r == regexp => m
case None => {
val pattern = Pattern.compile(regexp)
_matcher = (regexp, pattern.matcher(entity))
_matcher._2
}
}
def isDefined(mapping: NodeSeq) = {
matcher(mapping \ "match" text).matches
}
def apply(mapping: NodeSeq) = {
// call matcher(...), it is likely to reuse previous matcher, build result
}
}
You use that with (config \ mapping).collectFirst(linkFor(entity))