scala foreach and map initializers
Just seen an interesting possibility to initialize code blocks in Scala for high order functions such as foreach or map:
(1 to 3) map {
val t = 5
i => i * 5
}
(1 to 3) foreach {
val line = Console.readLine
i => println(line开发者_如何学Go)
}
Is this some documented feature or should I avoid such constructs? I could imagine, the "initialization" block comes into the constructor and the closure itself becomes an apply() method?
Thanks Pat for the original Question (http://extrabright.com/blog/2010/07/10/scala-question-regarding-readline)
While the features used are not uncommon, I'll admit is is a fairly odd combination of features. The basic trick is that any block in Scala is an expression, with type the same as the last expression in the block. If that last expression is a function, this means that the block has functional type, and thus can be used as an argument to "map" or "foreach" . What happens in these cases is that when "map" or "foreach" is called, the block is evaluated. The block evaluates to a function ( i=> i*5 in the first case ), and that function is then mapped over the range.
One possible use of this construct is for the block to define mutable variables, and the resulting function mutate the variables each time it is called. The variables will be initialized once, closed over by the function, and their values updated every time the function is called.
For example, here's a somewhat surprising way of calculating the first 6 factorial numbers
(1 to 6) map {
var total = 1
i => {total *= i;total}
}
(BTW, sorry for using factorial as an example. It was either that or fibonacci. Functional Progamming Guild rules. You gotta problem with that, take it up with the boys down at the hall.)
A less imperative reason to have a block return a function is to define helper functions earlier in the block. For instance, if your second example were instead
(1 to 3) foreach {
def line = Console.readLine
i => println(line)
}
The result would be that three lines were read and echoed once each, while your example had the line read once and echoed three times.
First, the comment of the original blog "Scala Question Regarding readLine" post mention
The “
line
” is a value and cannot be executed, it is assigned only once from the result of the “Console.readLine
” method execution.
It is used less than three times in your closure.
But if you define it as a method, it will be executed three times:
(1 to 3) foreach {
def line = Console.readLine
i => println(line)
}
The blog Scala for Java Refugees Part 6: Getting Over Java has an interesting section on Higher Order function, including:
Scala provides still more flexibility in the syntax for these higher-order function things.
In the iterate invocation, we’re creating an entire anonymous method just to make another call to theprintln(String)
method.
Consideringprintln(String)
is itself a method which takes aString
and returnsUnit
, one would think we could compress this down a bit. As it turns out, we can:
iterate(a, println)
By omitting the parentheses and just specifying the method name, we’re telling the Scala compiler that we want to use
println
as a functional value, passing it to theiterate
method.
Thus instead of creating a new method just to handle a single set of calls, we pass in an old method which already does what we want.
This is a pattern commonly seen in C and C++. In fact, the syntax for passing a function as a functional value is precisely the same. Seems that some things never change…
精彩评论