Now lets turn to the code. First of all I have a type called Argument which represents an argument that the parser should recognize:
class Argument(val name: String,
val action: String => Unit,
val helpText: String)
The declaration of the ArgParser type itself (with the whole implementation yanked out) is:
class ArgParser(private val args: List[String],
argumentSpecification: List[Argument]) {
//code omittedAgain the declaration of the class itself and a private immutable property - args - are condensed to a single line. Notice the second argument; a list of Argument objects. That list defines the arguments the parser is able to recognize, but internally I would like to store those arguments in a map from argument names to argument objects instead of as a list. The code that does that transformation is:
}
private val argumentMap: Map[String, Argument] =
(Map[String, Argument]() /: argumentSpecification){
(map, a) => map+((a.name, a))
}
Notice the /: method. In Scala a method can be called anything including names consisting of special characters, like /:. The /: method performs a fold left on the list argumentSpecification, i.e. it applies the code block immediately after the call to each element in the list, while passing the result of one such invocation as an argument to the next. The end result is the result of the last invocation. Again I get a pretty concise piece of code.
The actual parsing of the args parameter is done by looping through the args list, looking up matches in the argumentMap, and invoking the action:
def parse {That's pretty similar to the C# version.
for(i <- 0 until(args.length, 2)) {
args(i) match {
case "--help" => prettyPrintHelpText
case x if argumentMap.contains(x) => argumentMap(x).action(args(i + 1))
case _ =>
}
}
_hasParsed = true
}
The complete argument parser looks like this:
class ArgParser(private val args: List[String],
argumentSpecification: List[Argument]) {
args.length match {
case 1 if (args.first == "--help") =>
case x if (x % 2) == 1 =>
throw new IllegalArgumentException
case _ =>
}
private val argumentMap: Map[String, Argument] =
(Map[String, Argument]() /: argumentSpecification){
(map, a) => map+((a.name, a))
}
private var _hasParsed = false
private var _inputFileName = ""
def hasParsed = _hasParsed
def parse {
for(i <- 0 until(args.length, 2)) {
args(i) match {
case "--help" => prettyPrintHelpText
case x if argumentMap.contains(x) =>
argumentMap(x).action(args(i + 1))
case _ =>
}
}
_hasParsed = true
}
private def prettyPrintHelpText {
println("Usage:")
argumentMap.values.foreach { argument =>
println("\t" + argument.name + ": " + argument.helpText)
}
}
}Apart from a couple of syntactical things, like Scalas way of declaring properties and constructors and its way of accepting closures as arguments to methods, the Scala ArgParser code is pretty close to the C# ArgParser code. Why? Well I see a couple of possible reasons:
- I'm only just learning Scala now, so maybe I'm not thinking in Scala idioms and style
- Using a map from names to arguments containing lambda function for the handling of the arguments is actually the way to implement this sample, regardless of the language.
- C# has sufficiently many functional features that moving to a more functional language doesn't make much of a difference for simple cases like this ArgParser.
Which one is the right explanation I don't know. If you have opion please leave a comment.
No comments:
Post a Comment