从python3到Julia,我希望能够将快速迭代器编写为带有yield / yield语法的函数或类似的函数。
Julia的宏似乎暗示可以构建一个将这种“生成器”函数转换为julia迭代器的宏。 [甚至看起来你很容易内联以函数样式编写的迭代器,这是Iterators.jl包也试图为其特定的迭代器提供的一个特性https://github.com/JuliaCollections/Iterators.jl#the-itr-macro-for-automatic-inlining-in-for-loops]
举一个例子来说明我的想法:
AppCompatTextView
其中@asiterator function myiterator(as::Array)
b = 1
for (a1, a2) in zip(as, as[2:end])
try
@produce a1[1] + a2[2] + b
catch exc
end
end
end
for i in myiterator([(1,2), (3,1), 3, 4, (1,1)])
@show i
end
理想情况下应该以尽可能低的开销创建一个快速迭代器。当然,这只是一个具体的例子。理想情况下,我想拥有适用于所有或几乎所有发电机功能的东西。
目前推荐的将生成器函数转换为迭代器的方法是通过Julia的任务,至少据我所知。然而,它们似乎也比纯粹的迭代器慢。例如,如果您可以使用myiterator
,imap
等简单的迭代器(由Iterators.jl
包提供)来表达您的函数,那么这似乎是非常可取的。
在julia理论上是否有可能将生成器类型的宏转换为灵活的快速迭代器?
Extra-Point-Question:如果可能的话,是否会有一个通用宏来内联这样的迭代器?
答案 0 :(得分:3)
此表单的一些迭代器可以这样写:
myiterator(as) = (a1[1] + a2[2] + 1 for (a1, a2) in zip(as, as[2:end]))
此代码可以(可能)内联。
为了完全概括这一点,理论上可以编写一个宏,将其参数转换为延续传递样式(CPS),从而可以暂停和重新启动执行,给出类似迭代器的东西。定界延续特别适用于此(https://en.wikipedia.org/wiki/Delimited_continuation)。结果是一个大的匿名函数嵌套,它可能比任务切换更快,但不一定,因为在一天结束时它需要堆分配相似数量的状态。
我碰巧在这里有一个这样的转变的例子(虽然在femtolisp,而不是朱莉娅):https://github.com/JeffBezanson/femtolisp/blob/master/examples/cps.lsp
这以一个define-generator
宏结束,完成您描述的内容。但我不确定为Julia做这件事是否值得。
答案 1 :(得分:2)
Python风格的生成器 - 在Julia中最接近于从任务中屈服 - 涉及相当大的固有开销。您必须切换任务,这是非常重要的,并且不能由编译器直接消除。这就是为什么Julia的迭代器基于转换一个通常不可变的简单状态值的函数,以及另一个。长话短说:不,我不相信这种转变可以自动完成。
答案 2 :(得分:1)
在思考了很多如何将python生成器转换为Julia而不失去太多性能之后,我实现并测试了一个更高级函数的库,它们以连续样式实现类似Python的/类似任务的生成器。 https://github.com/schlichtanders/Continuables.jl
基本上,我们的想法是将Python的yield
/ Julia的produce
视为我们从外部获取的函数作为额外参数。我把它称为cont
继续。查看范围重新实现的实例
crange(n::Integer) = cont -> begin
for i in 1:n
cont(i)
end
end
您可以通过以下代码简单地总结所有整数
function sum_continuable(continuable)
a = Ref(0)
continuable() do i
a.x += i
end
a.x
end
# which simplifies with the macro Continuables.@Ref to
@Ref function sum_continuable(continuable)
a = Ref(0)
continuable() do i
a += i
end
a
end
sum_continuable(crange(4)) # 10
正如您所希望的那样,您可以使用可持续性,就像您在python中使用生成器或在julia中使用任务一样。使用do
符号代替for
循环是您必须习惯的一件事。
这个想法让你真的很开心。唯一不能使用这种想法实现的标准方法是zip
。所有其他标准的高级工具都可以像您希望的那样工作。
在某些情况下,性能比任务快得多,甚至比迭代器更快(特别是Continuables.cmap
的天真实现比Iterators.imap
快几个数量级)。查看github存储库https://github.com/schlichtanders/Continuables.jl的Readme.md以获取更多详细信息。
编辑:要更直接地回答我自己的问题,不需要宏@asiterator
,只需直接使用延续样式。
mycontinuable(as::Array) = cont -> begin
b = 1
for (a1, a2) in zip(as, as[2:end])
try
cont(a1[1] + a2[2] + b)
catch exc
end
end
end
mycontinuable([(1,2), (3,1), 3, 4, (1,1)]) do i
@show i
end