Default parameter in closure - groovy

Looking at the groovy manual, I see that I should be able to use default parameters in closures, like so:
def closureWithTwoArgAndDefaultValue = { int a, int b=2 -> a+b }
assert closureWithTwoArgAndDefaultValue(1) == 3
However, running that in groovysh gives me the following error:
ERROR groovy.lang.MissingMethodException:
No signature of method: groovysh_evaluate.closureWithTwoArgAndDefaultValue() is applicable for argument types: (java.lang.Integer) values: [1]
Could somebody tell me why?

Try to omit def:
closureWithTwoArgAndDefaultValue = { int a, int b=2 -> a+b }
assert closureWithTwoArgAndDefaultValue(1) == 3
For further explanation, see here.

Related

How to sort elements in an array or just print them sorted?

I wrote the following Groovy code which returns an array of CIDR blocks in use throughout all 3 AWS regions we use, the results are populated to a Jenkins extended parameter:
def regions = ['us-west-2', 'us-east-1', 'eu-west-1']
def output = []
regions.each { region ->
def p = ['/usr/local/bin/aws', 'ec2', 'describe-vpcs', '--region', region].execute() | 'grep -w CidrBlock'.execute() | ['awk', '{print $2}'].execute() | ['tr', '-d', '"\\"\\|,\\|\\{\\|\\\\["'].execute() | 'uniq'.execute()
p.waitFor()
p.text.eachLine { line ->
output << line
}
}
output.each {
println it
}
The output of the code looks like so:
172.31.0.0/16
172.56.0.0/16
172.55.0.0/16
172.64.0.0/16
172.52.0.0/16
I would like to sort the output in a numeric way, can it be done?
Edit #1:
If I use ".sort()" I get the following error:
Caught: groovy.lang.MissingMethodException: No signature of method: java.lang.String.sort() is applicable for argument types: () values: []
Possible solutions: drop(int), tr(java.lang.CharSequence, java.lang.CharSequence), wait(), toSet(), size(), size()
groovy.lang.MissingMethodException: No signature of method: java.lang.String.sort() is applicable for argument types: () values: []
Possible solutions: drop(int), tr(java.lang.CharSequence, java.lang.CharSequence), wait(), toSet(), size(), size()
at populate_parameter_with_used_cidrs$_run_closure2.doCall(populate_parameter_with_used_cidrs.groovy:15)
at populate_parameter_with_used_cidrs.run(populate_parameter_with_used_cidrs.groovy:14)
Some general hints to your code first:
p.waitFor() is not necessary if you do p.text, as this waits for the process to finish first anyway.
To get a list of Strings for the lines of a multi-line String, you can simply use readLines().
To transform one list into another list you can use collect() or collectMany().
This would boil down your code to
def regions = ['us-west-2', 'us-east-1', 'eu-west-1']
def output = regions.collectMany { ['/usr/local/bin/aws', 'ec2', 'describe-vpcs', '--region', it].execute() | 'grep -w CidrBlock'.execute() | ['awk', '{print $2}'].execute() | ['tr', '-d', '"\\"\\|,\\|\\{\\|\\\\["'].execute() | 'uniq'.execute().text.readLines() }
output.each { println it }
And to get the number-aware sorting, you add to that
output = output.sort { a, b ->
def aparts = a.split('[./]').collect { it as short }
def bparts = b.split('[./]').collect { it as short }
(0..4).collect { aparts[it] <=> bparts[it] }.find() ?: 0
}
How about .sort()?
def list = ['172.31.0.0/16', '172.56.0.0/16', '172.55.0.0/16', '172.64.0.0/16', '172.52.0.0/16']
println list.sort()
As an option: to sort and remove duplicates
(output as SortedSet).each {
println it
}

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

Why C# doesn't support overloading based on return type

In C# why can't we have two functions with same signature except return type:
(1) int Function(int a,int b)
{
---}
(2) string Function(int a,int b)
{
---}
Why C# doesn't support overloading based on return type?
object result = Function(a, b);
Which one do you call?
Because you can't specify the return type when you call it.
// int or string?
Function(a, b);
I'm also curious why you would want to do this, naming something the same but returning two different things is probably a bad idea. This code is far more readable and the intent is clearer:
string x = FunctionToString(a, b);
int y = FunctionToInt(a, b);

Groovy error with method mod or %

I just started to program in Groovy, I have got this error and I wanted to see if anyone could help me to work it out.
java.lang.UnsupportedOperationException: Cannot use mod() on this number type: java.math.BigDecimal with value: 5
at Script1.hailstone(Script1.groovy:8)
at Script1$hailstone.callCurrent(Unknown Source)
at Script1.hailstone(Script1.groovy:11)
at Script1$hailstone.callCurrent(Unknown Source)
at Script1.hailstone(Script1.groovy:14)
at Script1$_run_closure1.doCall(Script1.groovy:1)
at Script1.run(Script1.groovy:1)
I have the following Groovy code
def list = [1,2,3].findAll{it-> hailstone(it)}
def hailstone(num){
if(num==1){
return 1;
}
println num
println num.mod(2)
if(num.mod(2)==0){
num = num/2;
return 1 + hailstone(num)
}else{
num = 3*num + 1
return 1 + hailstone(num)
}
}
​
​
and the output:
2
0
3
1
10
0
5
and then it suddenly throws an error on 5.mod(2).
Thanks in advance.
Looks like 'num' is getting coerced into a BigDecimal when you hit the line
num = num/2
If you change the signature of the hailstone method to:
def hailstone(int num) it will not crash (because the parameter will get coerced to an int on each invocation), but this may not give the results that you want, as you will lose precision, e.g. when 'num' is an odd number, and num/2 yields a decimal value, the value will be truncated.
For more info on the (sometimes surprising) way that Groovy math operations work, take a look at http://groovy.codehaus.org/Groovy+Math
After running this code, it was not producing the right output because of the findAll. Here is the code with some minor tweaks:
def list = [1,2,3].collect { hailstone(it) } // use collect, no need for the "it ->" variable it is implicit.
def hailstone(int num) { // int data type to prevent BigDecimal from being passed to mod()
if (num == 1) {
return 1 // no need for semi-colons in groovy
} else if (num % 2 == 0) { // use modulus operator
num = num / 2
} else {
num = 3 * num + 1
}
return 1 + hailstone(num) // this line happens regardless of the condition in the else if or the else
}
println list // outputs : [1,2,8]

groovy method that take closure with one or two arguments

I would like to write a method that take a Closure as argument and pass to it tow arguments, but who write that closure can specify one or two arguments as he prefer
I tried in this way:
def method(Closure c){
def firstValue = 'a'
def secondValue = 'b'
c(firstValue, secondValue);
}
//execute
method { a ->
println "I just need $a"
}
method { a, b ->
println "I need both $a and $b"
}
If I try to execute this code the result is:
Caught: groovy.lang.MissingMethodException: No signature of method: clos2$_run_closure1.call() is applicable for argument types: (java.lang.String, java.lang.String) values: [a, b]
Possible solutions: any(), any(), dump(), dump(), doCall(java.lang.Object), any(groovy.lang.Closure)
at clos2.method(clos2.groovy:4)
at clos2.run(clos2.groovy:11)
How can I do it?
You can ask for the maximumNumberOfParameters of the Closure before calling it:
def method(Closure c){
def firstValue = 'a'
def secondValue = 'b'
if (c.maximumNumberOfParameters == 1)
c(firstValue)
else
c(firstValue, secondValue)
}
//execute
method { a ->
println "I just need $a"
}
method { a, b ->
println "I need both $a and $b"
}
Output:
I just need a
I need both a and b
The simplest is to give it a default value:
method { a, b=nil ->
println "I just need $a"
}
You can also use an array:
method { Object[] a ->
println "I just need $a"
}

Resources