getAt operator mutating list - groovy

The following code:
def a = [4,10,6].iterator().sort();
println a[1];
println a[1];
Produces the following output:
6
null
Bizarrely it seems like a[1] is mutating the list. Where is this documented?

It should rather be
def a = [4,10,6].sort();
println a[1];
println a[1];
without iterator()!
Then it produces what you expects
6
6
It is not mutating anything at all! Result of [...].iterator().sort() is of type java.util.ArrayList$ListItr and than you use it with index 1 (method getAt() is called at Iterator) so you are shifting iterator by 2 elements and iterator is at last position. Then you do it again and trying to read item from last + 1 position, so it is returning null.
Documentation of Iterator.getAt().

Related

How to capture a value in a Python Closure

How does one capture a value (as opposed to a reference) in a python closure?
What I have tried:
Here is a function which makes a list of parameter-less functions, each of which spits out a string:
def makeListOfFuncs( strings):
list = []
for str in strings:
def echo():
return str
list.append( echo)
return list
If closures in python worked like every other language, I would expect that a call like this:
for animalFunc in makeListOfFuncs( ['bird', 'fish', 'zebra']):
print( animalFunc())
... would yield output like this:
bird
fish
zebra
But in python instead, we get:
zebra
zebra
zebra
Apparently what is happening is that the closure for the echo function is capturing the reference to str, as opposed to the value in the call frame at the time of closure construction.
How can I define makeListOfFuncs, so that I get 'bird', 'fish', 'zebra'?
I found the answer here on a blog post by Bob Kerner.
def makeListOfFuncs( strings):
list = []
for str in strings:
def generateFunc( str):
def echo():
return str
return echo
list.append( generateFunc( str))
return list
for animalFunc in makeListOfFuncs( ['bird', 'fish', 'zebra']):
print( animalFunc())
Python closures are weird.
Python closure is not weird if you know how closure internally works in python.
def makeListOfFuncs( strings):
list = []
for str in strings:
def echo():
return str
list.append( echo)
return list
You are returning a list of closures. variable "str" is shared between scopes, it is in two different scopes. When python sees it, it creates an intermediary object. this object contains a reference to another object which is "str". and for each closure, it is the same cell. You can test it:
closures_list= makeListOfFuncs( ['bird', 'fish', 'zebra'])
# Those will return same cell address
closures_list[0].__closure__
closures_list[1].__closure__
closures_list[2].__closure__
When you call makeListOfFuncs, it will run the for-loop and at the end of the loop, the intermediary object will point to "zebra". So when you call each closure print( animalFunc()) , it will visit the intermediary obj which will reference to "zebra".
You have to think about what happens when python creates a function and when it evaluates it.
def echo():
return str
We need to somehow able to capture the value of "str" as the function being created. Because if you wait until function gets evaluated, then it is too late. Another solution would be defining a default value:
def makeListOfFuncs( strings):
list = []
for str in strings:
def echo(y=str):
return y
list.append( echo)
return list
for animalFunc in makeListOfFuncs( ['bird', 'fish', 'zebra']):
print( animalFunc())
This solution works because default values get evaluated at creation time. So when echo is created, the default value of "str" will be used. the default value will not point to the cell.
in the first iteration, the default value will be "bird". Python will not see this as a free variable. in this case we are not even creating closure, we are creating function. in next iteration "fish" and in the last iteration "zebra" will be printed

Why does this closure not execute on every element of the int range?

Here is my code:
def myclosey = {items ->
items + 1}
myclosey(1..3);
I expect 1 to be added to every element in the intrange. Instead, 1 is just added to the end of intrange,
so output is
[1,2,3,1]
Why?
IntRange is a list. So the plus operator appends an element to the list. The closure is being called on the range itself, not each element of the range.
If you wanted to add one to all of the elements, you could do (1..3).collect { it + 1 }, or use the syntax that #dmahpatro suggested.
Because you are passing in a range to the closure so you have to do:
def myclosey = {items ->
items*.plus(1) //Spread on the range
}
assert myclosey(1..3) == [2, 3, 4]

Groovy split csv and empty fields

Groovy split seems to be ignoring empty fields.
Here is the code:
line = abc,abc,,,
line.split(/,/)
println
prints only..
abc abc
It seems to ignore empty fields. How do I retrieve empty fields using split?
First of all, method split(regex) is not provided by Groovy, it is provided by Java.
Second, you can achieve what you need by using the generic split(regex, int limit) as below:
def line = "abc,abc,,,"
println line.split(/,/, -1) //prints [abc, abc, , , ]
println line.split(/,/, -1).size() //prints 5
Note:-
The string array you would end up in the print would throw a compilation error when asserted. But you can use the result as a normal list.
line.split(/,/, -1).each{println "Hello $it"}
I would rather use limit 0 or the overloaded split to discard unwanted empty strings.
Explanation on using -1 as limit:
Stress on the below statements from the javadoc.
The limit parameter controls the number of times the pattern is
applied and therefore affects the length of the resulting array. If
the limit n is greater than zero then the pattern will be applied at
most n - 1 times, the array's length will be no greater than n, and
the array's last entry will contain all input beyond the last matched
delimiter. If n is non-positive then the pattern will be applied as
many times as possible and the array can have any length. If n is zero
then the pattern will be applied as many times as possible, the array
can have any length, and trailing empty strings will be discarded.
Interesting. The split method works as expected provided there's a non-empty element at the end.
def list = 'abc,abc,,,abc'.split(/,/)
println list // prints [abc, abc, , ]
assert list.size() == 5
assert list[0] == 'abc'
assert list[1] == 'abc'
assert list[2] == ''
assert list[3] == ''
assert list[4] == 'abc'
Maybe you could just append a bogus character to the end of the string and sublist the result:
def list = 'abc,abc,,,X'.split(/,/) - 'X'
println list // prints [abc, abc, , ]

Groovy String concatenation with null checks

Is there a better way to do this? Note: part1, part2 and part3 are string variables defined elsewhere (they can be null).
def list = [part1, part2, part3]
list.removeAll([null])
def ans = list.join()
The desired result is a concatenated string with null values left out.
You can do this:
def ans = [part1, part2, part3].findAll({it != null}).join()
You might be able to shrink the closure down to just {it} depending on how your list items will evaluate according to Groovy Truth, but this should make it a bit tighter.
Note: The GDK javadocs are a great resource.
If you use findAll with no parameters. It will return every "truthful" value, so this should work:
def ans = [part1, part2, part3].findAll().join()
Notice that findAll will filter out empty strings (because they are evaluated as false in a boolean context), but that doesn't matter in this case, as the empty strings don't add anything to join() :)
If this is a simplified question and you want to keep empty string values, you can use findResults{ it }.
Alternatively, you can do this as a fold operation with inject:
def ans = [part1, part2, part3].inject('') { result, element ->
result + (element ?: '')
}
This iterates the whole list and concatenates each successive element to a result, with logic to use the empty string for null elements.
You could use grep:
groovy:000> list = ['a', 'b', null, 'c']
===> [a, b, null, c]
groovy:000> list.grep {it != null}.join()
===> abc

Groovy - Closures and bind, why this code doesn't work?

I think this is a newbie question but why it fails on the last assertion?
I was thinking that the closure binds the value on it, so changing it from the closure will change the value outside the closure.
def value = 5
def foo(n){
return {
++n
}
}
def test = foo(value)
assert test() == 6
assert test() == 7
assert value == 7
Thanks for help.
That does seem to be strange behaviour but I think it is correct. All objects event integegers are passed by reference. The call foo(value) passes value into the function. The variable 'n' is a reference to the same object as 'value' references. Essentially you have two pointers to the same object. When you increment 'n', you are incrementing that variable only.
Since the Integer class is immutable ++n is actually doing something like:
n = n + 1
This is assigning the incremented value to the variable n. The variable 'value' declared at the top is still pointing to the original object 5.
Remember that Integer (the runtime type of value) is immutable. So although n and value initially refer to the same object, when you execute ++n it creates a new Integer. The value reference is not updated to refer to this new object, so when execution completes it is still referring to the initial object (5).
You are assigning a new value to the name n, which is different than the name value. You can get the behaviour you want by making value a mutable object. Then you can change it instead of creating and assigning a new object.
Here's a simple example that wraps the value in a list:
def value = [5]
def foo(n){
return {
n[0]++
n
}
}
def test = foo(value)
assert test() == [6]
assert test() == [7]
assert value == [7]

Resources