Hi
C# has a convenient way to create iterators using the yield return
statement. The package ResumableFunctions provides the same functionality for the Julia language by introducing the @resumable
and the @yield
macros. These macros can be used to replace the Task
switching functions produce
and consume
which were deprecated in Julia v0.6. Channels
are the preferred way for inter-task communication in julia v0.6+, but their performance is subpar for iterator applications.
using ResumableFunctions
@resumable function fibonnaci(n::Int)
a = 0
b = 1
for i in 1:n-1
@yield a
a, b = b, a+b
end
a
end
for fib in fibonnaci(10)
println(fib)
end
Enjoy!
Ben
20 Likes
I apologize for being late to the ResumableFunctions vs PyGen party, but for me, the only time I really needed PyGen (because there is no other simple alternative) is when making recursive generators, basically traversing tree-like structures without having to generate it all a priori. So I tested the following pieces of codes in Julia using PyGen and ResumableFunctions and in Python and here are the results:
a = [[1,2,3],[4,5,6]]
@resumable function g(x)
if isa(x,Number)
@yield x
else
for i in x
for j in g(i)
@yield j
end
end
end
end
for i in g(a)
println(i)
end
#=
1
false
2
false
3
false
nothing
4
false
5
false
6
false
nothing
nothing
=#
a = [[1,2,3],[4,5,6]]
@pygen function f(x)
if isa(x,Number)
yield(x)
else
for i in x
for j in f(i)
yield(j)
end
end
end
end
for i in f(a)
println(i)
end
#=
1
2
3
4
5
6
=#
import numbers
a = [[1,2,3],[4,5,6]]
def f(x):
if isinstance(x, numbers.Number):
yield x
else:
for i in x:
for j in f(i):
yield j
for i in f(a):
print i
"""
1
2
3
4
5
6
"""
Clearly ResumableFunctions
generates extra entries not in the other 2, can that be treated?
Hi @mohamed82008
I can explain the behaviour:
- the extra
false
entries are a side effect of the transformation of a for
loop in a while
loop
- the extra
nothing
is the normal return of the function (this is also the default behaviour in C#)
Can you open an issue in the ResumableFunctions
repository?
The first one can be directly solved. The second one I have to look at the Iterator
interface to see if I can fix it.
This is really a very nice use case and I hope I can find a solution;)
Ben
Interesting, I will open an issue then. Thanks for your effort btw, that’s an awesome piece of work!
A complete explanation of this weird but logical behaviour can be found in this issue.
The correct sequence can be created by 3 minor modifications:
using ResumableFunctions
@resumable function g(x)
if isa(x,Number)
return x # If x is a number the @resumable function is only called once
else
for i in x
for j in g(i)
j == nothing || @yield j # No explicit return is defined, so we have to eliminate the default returns
end
end
end
end
a = [[1,2,3],[4,5,6]]
for i in g(a)
i==nothing || println(i) # Eliminate the default return
end
1 Like