Groovy: Detecting when being passed arrays - groovy

The function f in the following code simply attempts to print out it's arguments and how many it receives. However, it expands array parameters (but not arraylists) as illustrated on the line f(x) // 3. Is there anyway to get f not to expand array parameters, or alternatively at the very least detect that it has happened, and perhaps correct for it. The reason for this is because my "real" f function isn't as trivial and instead passes it's parameters to a given function g, which often isn't a variable parameter function which instead expects an array directly as an argument, and the expansion by f mucks that up.
def f = {
Object... args ->
print "There are: ";
print args.size();
println " arguments and they are: ";
args.each { println it };
println "DONE"
}
def x = new int[2];
x[0] = 1;
x[1] = 2;
f(1,2); // 1
f([1,2]); // 2
f(x); // 3

I doubt there is any clean solution to this, as it behaves as Java varargs. You may test the size of the array inside the closure, or, as in Java, use a method overload:
public class Arraying {
public static void main(String[] args) {
// prints "2"
System.out.println( concat( new Object[] { "a", "b" } ) );
// prints "a". Commenting the second concat(), prints "1"
System.out.println( concat( "a" ) );
// prints "3"
System.out.println( concat( "a", "b", "c" ) );
}
static String concat(Object... args) {
return String.valueOf(args.length);
}
static String concat(Object obj) { return obj.toString(); }
}
If you comment the concat(Object obj) method, all three methods will match the concat(Object... args).

You can use a label for the argument as follow:
def f = {
Object... args ->
print "There are: ";
print args.size();
println " arguments and they are: ";
args.each { println it };
println "DONE"
}
def x = new int[2];
x[0] = 1;
x[1] = 2;
f(1,2); // 1
f([1,2]); // 2
f(a:x); // <--- used label 'a', or anything else
then the output is:
There are: 2 arguments and they are:
1
2
DONE
There are: 1 arguments and they are:
[1, 2]
DONE
There are: 1 arguments and they are:
[a:[1, 2]]
DONE

Related

Eager interpolation with just a closure behaves like a lazy one?

As part of learning Groovy, I'm trying to explore all intricate possibilities provided by string interpolation.
One of my little experiments gave results that don't make sense to me, and now I'm wondering whether I've completely misunderstood the basic concepts of lazy and eager interpolation in Groovy.
Here's the code I ran:
def myVar1 = 3
// An eager interpolation containing just a closure.
def myStr = "${{->myVar1}}"
print ("Just after the creation of myStr\n")
print (myStr as String)
myVar1 += 1 // Bump up myVar1.
print ("\nJust after incrementing myVar1\n")
print (myStr as String)
Here's the output I got:
Just after the creation of myStr
3
Just after incrementing myVar1
4
Clearly, the closure has been invoked a second time. And the only way the closure could have been re-executed is by the containing interpolation getting re-evaluated. But then, the containing interpolation is, by itself, not a closure, though it contains a closure. So then, why is it getting re-evaluated?
This is how GString.toString() method is implemented. If you take a look at the source code of GString class, you will find something like this:
public String toString() {
StringWriter buffer = new StringWriter();
try {
writeTo(buffer);
}
catch (IOException e) {
throw new StringWriterIOException(e);
}
return buffer.toString();
}
public Writer writeTo(Writer out) throws IOException {
String[] s = getStrings();
int numberOfValues = values.length;
for (int i = 0, size = s.length; i < size; i++) {
out.write(s[i]);
if (i < numberOfValues) {
final Object value = values[i];
if (value instanceof Closure) {
final Closure c = (Closure) value;
if (c.getMaximumNumberOfParameters() == 0) {
InvokerHelper.write(out, c.call());
} else if (c.getMaximumNumberOfParameters() == 1) {
c.call(out);
} else {
throw new GroovyRuntimeException("Trying to evaluate a GString containing a Closure taking "
+ c.getMaximumNumberOfParameters() + " parameters");
}
} else {
InvokerHelper.write(out, value);
}
}
}
return out;
}
Notice that writeTo method examines what the values passed for interpolation are, and in case of closure, it invokes it. This is the way GString handles lazy-evaluation of interpolated values.
Now let's take a look at a few examples. Let's assume we want to print a GString and interpolate a value returned by some method call. This method will also print something to the console, so we can see if the method call was triggered eagerly or lazily.
Ex.1: Eager evaluation
class GStringLazyEvaluation {
static void main(String[] args) {
def var = 1
def str = "${loadValue(var++)}"
println "Starting the loop..."
5.times {
println str
}
println "Loop ended..."
}
static Integer loadValue(int val) {
println "This method returns value $val"
return val
}
}
The output:
This method returns value 1
Starting the loop...
1
1
1
1
1
Loop ended...
The default eager behavior. The method loadValue() was invoked before we have printed out str to the console.
Ex.2: Lazy evaluation
class GStringLazyEvaluation {
static void main(String[] args) {
def var = 1
def str = "${ -> loadValue(var++)}"
println "Starting the loop..."
5.times {
println str
}
println "Loop ended..."
}
static Integer loadValue(int val) {
println "This method returns value $val"
return val
}
}
The output:
Starting the loop...
This method returns value 1
1
This method returns value 2
2
This method returns value 3
3
This method returns value 4
4
This method returns value 5
5
Loop ended...
In the second example, we take advantage of lazy evaluation. We define str with a closure that invokes loadValue() method and this invocation is executed when we explicitly print the str to the console (to be more specific - when the GString.toString() method gets executed).
Ex.3: Lazy evaluation and closure memoization
class GStringLazyEvaluation {
static void main(String[] args) {
def var = 1
def closure = { -> loadValue(var++)}
def str = "${closure.memoize()}"
println "Starting the loop..."
5.times {
println str
}
println "Loop ended..."
}
static Integer loadValue(int val) {
println "This method returns value $val"
return val
}
}
The output:
Starting the loop...
This method returns value 1
1
1
1
1
1
Loop ended...
And here is the example you most probably look for. In this example, we still take advantage of lazy evaluation thanks to the closure parameter. However, in this case, we use closure's memoization feature. The evaluation of the string is postponed to the first GString.toString() invocation and closure's result gets memorized, so the next time it gets called it returns the result instead of re-evaluating the closure.
What is the difference between ${{->myVar1}} and ${->myVar1}?
As it was mentioned earlier, GString.toString() method uses GString.writeTo(out) that checks if the given placeholder stores a closure for a lazy evaluation. Every GString instance store placeholder values in the GString.values array and it gets initialized during GString initialization. Let's consider the following example:
def str = "${myVar1} ... ${-> myVar1} ... ${{-> myVar1}}"
Now let's follow GString.values array initialization:
${myVar1} --> evaluates `myVar1` expression and copies its return value to the values array
${-> myVar1} --> it sees this is closure expression so it copies the closure to values array
${{-> myVar1}} --> evaluates `{-> myVar1}` which is closure definition expression in this case and copies its return value (a closure) to the values array
As you can see, in the 1st and 3rd example it did exactly the same - it evaluated the expression and stored it in the GString.values array of type Object[]. And here is the crucial part: the expression like {->something} is not a closure invocation expression. The expression that evaluates the closure is
{->myVar1}()
or
{->myVar1}.call()
It can be illustrated with the following example:
def str = "${println 'B'; 2 * 4} ${{ -> println 'C'; 2 * 5}} ${{ -> println 'A'; 2 * 6}.call()}"
println str
Values initialization is as follows:
${println 'B'; 2 * 4} ---> evaluates the expression which prints 'B' and returns 8 - this value is stored in values array.
${{ -> println 'C'; 2 * 5}} ---> evaluates the expression which is nothing else than creation of a closure. This closure is stored in the values array.
${{ -> println 'A'; 2 * 6}.call()}" ---> evaluates the expression which creates a closure and then calls it explicitely. It prints 'A' and returns 12 which is stored in the values array at the last index.
That is why after GString object initialization we end up with values array like:
[8, script$_main_closure1, 12]
Now, the creation of this GString caused a side effect - the following characters shown on the console:
B
A
This is because the 1st and the 3rd values evaluation invoked println method call.
Now, when we finally call println str which invokes GString.toString() method, all values get processed. When the interpolation process starts it does the following:
value[0] --> 8 --> writes "8"
value[1] --> script$_main_closure1 --> invoke script$_main_closure1.call() --> prints 'C' --> returns 10 --> 10 --> writes "10"
value[2] --> 12 --> writes "12"
That is why the final console output looks like this:
B
A
C
8 10 12
This is why in practice expressions like ${->myVar1} and ${{->myVar1}} are similar. In the first case GString initialization does not evaluate the closure expression and puts it directly to the values array, in the second example placeholder gets evaluated and the expression it evalutes creates and returns the closure which then gets stored in the values array.
Note on Groovy 3.x
If you try to execute the expression ${{->myVar1}} in Groovy 3.x you will end up with the following compiler error:
org.codehaus.groovy.control.MultipleCompilationErrorsException: startup failed:
General error during conversion: java.lang.NullPointerException
java.lang.NullPointerException
at org.apache.groovy.parser.antlr4.AstBuilder.lambda$visitGstring$28(AstBuilder.java:3579)
at java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:193)
at java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1382)
at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:481)
at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:471)
at java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:708)
at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
at java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:499)
at org.apache.groovy.parser.antlr4.AstBuilder.visitGstring(AstBuilder.java:3591)
at org.apache.groovy.parser.antlr4.AstBuilder.visitGstring(AstBuilder.java:356)
at org.apache.groovy.parser.antlr4.GroovyParser$GstringContext.accept(GroovyParser.java:4182)
at groovyjarjarantlr4.v4.runtime.tree.AbstractParseTreeVisitor.visit(AbstractParseTreeVisitor.java:20)
at org.apache.groovy.parser.antlr4.AstBuilder.visit(AstBuilder.java:4287)
.....
at org.codehaus.groovy.control.CompilationUnit.compile(CompilationUnit.java:565)
at org.codehaus.groovy.tools.FileSystemCompiler.compile(FileSystemCompiler.java:72)
at org.codehaus.groovy.tools.FileSystemCompiler.doCompilation(FileSystemCompiler.java:240)
at org.codehaus.groovy.tools.FileSystemCompiler.commandLineCompile(FileSystemCompiler.java:163)
at org.codehaus.groovy.tools.FileSystemCompiler.commandLineCompileWithErrorHandling(FileSystemCompiler.java:203)
at org.codehaus.groovy.tools.FileSystemCompiler.main(FileSystemCompiler.java:187)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.codehaus.groovy.tools.GroovyStarter.rootLoader(GroovyStarter.java:114)
at org.codehaus.groovy.tools.GroovyStarter.main(GroovyStarter.java:136)
1 error

If I define method with (arg, ...args), the parameters will reverse

for instance:
def m(arg, ...args) {
println "arg: $arg"
println "args: $args"
}
m('arg', k:'v')
output:
arg:['k':'v']
args:['arg']
I think the right output should be
args:['arg']
arg:['k':'v']
Groovy has a special ordering rules for map parameters, if they take the first position in the arguments list
def fn(Map params, ...args) {
println "params = $params and args = $args"
}
Then, calling the method with:
fn(1, 2, 3, something:'else')
Will print:
params = [something:else] and args = [1, 2, 3]
Groovy also has special ordering rules for Closure parameters, in that if they are the last parameter:
def fn2(a, b, Closure cl) {
cl(a, b)
}
Then you can place them outside the parentheses when calling the method, ie:
println fn2(1, 2) { a, b -> a + b } // prints 3
Because you have omitted types on all your parameters, it's just sticking the map as the first parameter

Iterate and print content of groovy closures

In a loop I create 4 closures and add them to a list:
closureList = []
for (int i=0; i<4; i++) {
def cl = {
def A=i;
}
closureList.add(cl)
}
closureList.each() {print it.call()println "";};
This results in the following output:
4
4
4
4
But I would have expected 0,1,2,3 instead. Why does the 4 closures have the same value for A?
Yeah, this catches people out, the free variable i is getting bound to the last value in the for loop, not the value at the time the closure was created.
You can either, change the loop into a closure based call:
closureList = (0..<4).collect { i ->
{ ->
def a = i
}
}
closureList.each { println it() }
Or create an extra variable that gets re-set every time round the loop, and use that:
closureList = []
for( i in (0..<4) ) {
int j = i
closureList << { ->
def a = j
}
}
closureList.each { println it() }
In both of these variants the variable closed by the closure is created afresh each time round the loop, so you get the result you'd expect

How I can find the contents of inner nesting?

Lets say I have a string like this :
string = [+++[>>[--]]]abced
Now I want a someway to return a list that has: [[--],[>>],[+++]]. That is the contents of the deepest [ nesting followed by other nesting. I came up with this solution like this :
def string = "[+++[>>[--]]]"
loop = []
temp = []
string.each {
bool = false
if(it == "["){
temp = []
bool = true
}
else if( it != "]")
temp << it
if(bool)
loop << temp
}
println loop.reverse()
But this indeed takes the abced string after the last ] and put into the result!. But what I want is only [[--],[>>],[+++]]
Are there any groovy way of solving this?
You can use this, if you wouldn't mind using recursion
def sub(s , list){
if(!s.contains('[') && !s.contains('['))
return list
def clipped = s.substring(s.lastIndexOf('[')+1, s.indexOf(']'))
list.add(clipped)
s = s - "[$clipped]"
sub(s , list)
}
Calling
sub('''[+++[>>[--]]]abced''' , [])
returns a list of all subportions enclosed between braces.
['--', '>>', '+++']
If your brackets are symmetrical, you could just introduce a counter variable that holds the depth of the bracket nesting. Only depth levels above 0 are allowed in the output:
def string = "[+++[>>[--]]]abc"
loop = []
temp = []
depth = 0;
string.each {
bool = false
if(it == "["){
temp = []
bool = true
depth++;
}
else if (it == "]"){
depth--;
}
else if (depth > 0){
temp << it
}
if(bool){
loop << temp
}
}
println loop.reverse()
class Main {
private static final def pattern = ~/([^\[]*)\[(.+?)\][^\]]*/
static void main(String[] args) {
def string = "[+++[>>[--]]]abced"
def result = match(string)
println result
}
static def match(String val) {
def matcher = pattern.matcher(val);
if (matcher.matches()) {
return matcher.group(1) ? match(matcher.group(2)) + matcher.group(1) : match(matcher.group(2))
}
[val]
}
}
System.out
[--, >>, +++]
The capturing of the first group in the regex pattern could probably be improved. Right now the first group is any character that is not [ and if there are nothing in front of the first [ then the first group will contain an empty string.

how to accept multiple parameters from returning function in groovy

I want to return multiple values from a function written in groovy and receive them , but i am getting an error
class org.codehaus.groovy.ast.expr.ListExpression, with its value '[a,
b]', is a bad expression as the left hand side of an assignment
operator
My code is
int a=10
int b=0
println "a is ${a} , b is ${b}"
[a,b]=f1(a)
println "a is NOW ${a} , b is NOW ${b}"
def f1(int x) {
return [a*10,a*20]
}
You almost have it. Conceptually [ a, b ] creates a list, and ( a, b ) unwraps one, so you want (a,b)=f1(a) instead of [a,b]=f1(a).
int a=10
int b=0
println "a is ${a} , b is ${b}"
(a,b)=f1(a)
println "a is NOW ${a} , b is NOW ${b}"
def f1(int x) {
return [x*10,x*20]
}
Another example returning objects, which don't need to be the same type:
final Date foo
final String bar
(foo, bar) = baz()
println foo
println bar
def baz() {
return [ new Date(0), 'Test' ]
}
Additionally you can combine the declaration and assignment:
final def (Date foo, String bar) = baz()
println foo
println bar
def baz() {
return [ new Date(0), 'Test' ]
}
You can declare and assign the variables in which the return values are stored in one line like this, which is a slightly more compact syntax than that used in Justin's answer:
def (int a, int b) = f1(22)
In your particular case you may not be able to use this because one of the variables passed to f1 is also used to store a return value
When running Groovy in the context of Jenkins pipeline job the above answers do not work (at least on version 2.60.2), but the following does:
node {
obj = ret2()
fw = obj[0]
lw = obj[1]
echo "fw=${fw}"
echo "lw=${lw}"
}
def ret2()
{
return [5, 7]
}
Or alternatively:
node {
obj = ret2()
fw = obj.a
lw = obj.b
echo "fw=${fw}"
echo "lw=${lw}"
}
def ret2()
{
return [a:5, b:7]
}

Resources