Suppose there is a function def f = { x -> x + 4 }.
Is there a way to call it somehow like 7.f() and get 11?
Yes, you can add that function as a method to the Integer class, but, instead of using the x variable, you are better using the delegate of the closure:
Integer.metaClass.f = { delegate + 4 }
assert 7.f() == 11
Related
In Python3, I would like to create a class with an override-property, so after instantiation, the instance will behave like whatever it is being assigned, unless its override property is assigned something else than None, in which case it will behave like what has been assigned to the override property. E.g.
x = OverrideClass()
x = 1
print(x) -> 1
x.override = “abc“
print(x) -> abc
x = 2
print(x) -> abc
x.override = None
print(x) -> 2
Can this be done? From what I've learned by now, it is not possible to override the x = y assignment. So the only way would be to modify Python's built-in classes in question. Adding an override-property to int and float would probably do in my application. Is that possible somehow?
i am scraping multiple websites so i am using one function for each website script, so each function returns 4 values, i want to print them in dataframe and write them in csv but i am facing this problem, i may be asking something too odd or basic but please help
Either i will have to write whole script in one block and that will look very nasty to handle so if i could find a way around, this is just a sample of problem i am facing..
def a1(x):
z=x+1
r = x+2
print(z, r)
def a2(x):
y=x+4
t=x+3
print(y, t)
x = 2
a1(x)
a2(x)
3 4
6 5
data = pd.Dataframe({'first' : [z],
'second' : [r],
'third' : [y],
'fourth' : [t]
})`
data
*error 'z' is not defined*
You may find it convenient to write functions that return a list of dicts.
For example:
rows = [dict(a=1, b=2, c=3),
dict(a=4, b=5, c=6)]
df = pd.DataFrame(rows)
The variables are only defined in the local scope of your functions, you'd either need to declare them globally or - the better way - return them so you can use them outside of the function by assigning the return values to new variables
import pandas as pd
def a1(x):
z = x+1
r = x+2
return (z, r)
def a2(x):
y = x+4
t = x+3
return (y, t)
x = 2
z, r = a1(x)
y, t = a2(x)
data = pd.DataFrame({'first' : [z],
'second' : [r],
'third' : [y],
'fourth' : [t]
})
I have a variable and several methods I'd like to call in a sequence where return value of one method is an input of another. Basically a pipeline. Now, is there a way of chaining the calls? In a pseudo code it looks like
def a = [1, 2, 3]
def b = calculation3(calculation2(calculation1(a)))
As you can see it looks very Clojure-like and I'd like to end up with something like (using Clojure syntax)
(-> a
calculation1
calculation2
calculation3)
I was hoping to use the with keyword but it only passes the variable a around not collecting the results and passing them as the input to the next method.
The only working solution I've found so far is 'closure composition' like this. But that seems to be too heavy handed to me.
def a = [1, 2, 3]
def b = (class1.&calculation1 >> class1.&calculation2 >> class1.&calculation3)(a)
Any idea?
You could write a function to do it:
def chain(a, Closure... fns) {
fns.toList().inject(a) { v, c -> c(v) }
}
Then call this in your code:
chain(a, class1.&calculation1,class1.&calculation2,class1.&calculation3)
I gave a bit more thought and have come up with something like this:
def compose = {...c ->
{result ->
c.each {
result = it(result)
}
result
}
}
def a = [1, 2, 3]
def b = compose(class1.&calculation1, class1.&calculation2, class1.&calculation3)(a)
Still it's not as nice as Clojure's threading.
Use inject
def pipe(fn, ...fns) {
{ args ->
fns.inject(fn(args)) { acc, value ->
value(acc)
}
}
}
Not that the first function is allowed to take a variable number of arguments while the following parameters are not.
In the below code, x.test() returns [1,2].
So y = [1,2].
Yet f([1,2]) prints 1, but f(y) prints 2.
How do I write f(y) so it prints 1?
Perversely, f(z) prints 1, even though z = y.
def f = { Object... args -> println args.size(); };
class Test { Object[] test() { return [1,2]; } }
def x = new Test();
def y = x.test();
def z = [1,2];
f([1,2]); // 1
f(y); // 2
f(z); // 1
The problem is that y and z, while they appear to be the same, are actually of different types. y is an Object[] while z is an ArrayList<Integer>. Groovy handles arrays and lists differently, automatically coercing the former into a varargs parameter list, but not the latter.
println y.getClass(); // class [Ljava.lang.Object
println z.getClass(); // class java.util.ArrayList
As for a solution to your problem, either change your test() to return a List instead of an array:
class Test { List test() { return [1,2]; } }
or manually coerce the array into a list when you pass it to f:
f(y as List); // 1
The expression [1,2] in Groovy denotes an ArrayList with two members, Integer.valueOf(1) and Integer.valueOf(2). Thus when you call f([1,2]) Groovy creates a single-element array containing this ArrayList as its only item, and passes that array as the closure argument.
But x.test() is declared to return Object[] so the [1,2] ArrayList will be converted to a two element Object[] by the return. Thus y is already an Object[] and does not need to be boxed up in a varargs array to be passed to f.
You need to turn y back into a list, either by changing the return type of test() or by saying
f(y as List)
Conversely, you can use the spread operator
f(*z) // 2
which will extract the elements of the ArrayList and pass them as individual arguments to the call (which will then be packaged up into a varargs array as usual).
y is an instance of [Ljava.lang.Object whereas [1,2] and z are of instance of ArrayList
The Array has the size() == 2 and the ArrayLists count as one argument, but they contain two elements
Groovy does some type converting for you ;)
For example, the groovy File class has a nice iterator that will filter out just directories and not files:
void eachDir(Closure closure)
When I use eachDir, I have to use the verbose method of creating the collection first and appending to it:
def collection = []
dir1.eachDir { dir ->
collection << dir
}
Any way to get it back to the nice compact collect syntax?
I don't know of any "idiomatic" way of doing this, nice riddle! =D
You can try passing the eachDir, or any similar function, to a function that will collect its iterations:
def collectIterations(fn) {
def col = []
fn {
col << it
}
col
}
And now you can use it as:
def dir = new File('/path/to/some/dir')
def subDirs = collectIterations(dir.&eachDir)
def file = new File('/path/to/some/file')
def lines = collectIterations(file.&eachLine)
(that last example is equivalent to file.readLines())
And only for bonus points, you may define this function as a method in the Closure class:
Closure.metaClass.collectIterations = {->
def col = []
delegate.call {
col << it
}
col
}
def dir = new File('/path/to/some/dir')
def subDirs = dir.&eachDir.collectIterations()
def file = new File('/path/to/some/file')
def lines = file.&eachLine.collectIterations()
Update: On the other hand, you might also do:
def col = []
someDir.eachDir col.&add
Which I think is quite less convoluted, but it's not leveraging the collect method as you requested :)
Not for the specific example that you're talking about. File.eachDir is sort of a weird implementation IMO. It would have been nice if they implemented iterator() on File so that you could use the normal iterator methods on them rather than the custom built ones that just execute a closure.
The easiest way to get a clean one liner that does what you're looking for is to use listFiles instead combined with findAll:
dir1.listFiles().findAll { it.directory }
If you look at the implementation of eachDir, you'll see that it's doing this (and a whole lot more that you don't care about for this instance) under the covers.
For many similar situations, inject is the method that you'd be looking for to have a starting value that you change as you iterate through a collection:
def sum = [1, 2, 3, 4, 5].inject(0) { total, elem -> total + elem }
assert 15 == sum