结合懒惰的 Iterables

标准库附带了大量惰性迭代(以及 Iterators.jl 等库提供了更多)。可以组合延迟迭代以在恒定时间内创建更强大的迭代。最重要的延迟迭代是取放 ,从中可以创建许多其他函数。

懒惰切片一个可迭代的

可以使用切片表示法切割数组。例如,以下内容返回数组的第 10 个到第 15 个元素,包括:

A[10:15]

但是,切片表示法不适用于所有迭代。例如,我们不能切片生成器表达式:

julia> (i^2 for i in 1:10)[3:5]
ERROR: MethodError: no method matching getindex(::Base.Generator{UnitRange{Int64},##1#2}, ::UnitRange{Int64})

切片字符串可能没有预期的 Unicode 行为:

julia> "αααα"[2:3]
ERROR: UnicodeError: invalid character index
 in getindex(::String, ::UnitRange{Int64}) at ./strings/string.jl:130

julia> "αααα"[3:4]
"α"

我们可以定义一个函数 lazysub(itr, range::UnitRange) 来对任意迭代进行这种切片。这是根据 takedrop 定义的:

lazysub(itr, r::UnitRange) = take(drop(itr, first(r) - 1), last(r) - first(r) + 1)

这里的实现有效,因为对于 UnitRangea:b,执行以下步骤:

  • 放下第一个 a-1 元素
  • ath 元素,a+1th 元素,等等,直到 a+(b-a)=bth 元素

总共采用了 b-a 元素。我们可以在上面的每种情况下确认我们的实施是否正确

julia> collect(lazysub("αααα", 2:3))
2-element Array{Char,1}:
 'α'
 'α'

julia> collect(lazysub((i^2 for i in 1:10), 3:5))
3-element Array{Int64,1}:
  9
 16
 25

懒惰地循环移动

对数组的 circshift 操作将使数组移位,就像它是一个圆,然后重新线性化它。例如,

julia> circshift(1:10, 3)
10-element Array{Int64,1}:
  8
  9
 10
  1
  2
  3
  4
  5
  6
  7

我们可以懒洋洋地为所有迭代事件做这件事吗?我们可以使用 cycledroptake iterables 来实现此功能。

lazycircshift(itr, n) = take(drop(cycle(itr), length(itr) - n), length(itr))

随着懒惰类型在许多情况下的性能更高,这使我们可以在不支持它的类型上执行类似 circshift 的功能:

julia> circshift("Hello, World!", 3)
ERROR: MethodError: no method matching circshift(::String, ::Int64)
Closest candidates are:
  circshift(::AbstractArray{T,N}, ::Real) at abstractarraymath.jl:162
  circshift(::AbstractArray{T,N}, ::Any) at abstractarraymath.jl:195

julia> String(collect(lazycircshift("Hello, World!", 3)))
"ld!Hello, Wor"

Version >= 0.5.0

制作乘法表

让我们使用惰性可迭代函数创建一个乘法表来创建一个矩阵。

这里使用的关键功能是:

  • Base.product,计算笛卡尔积
  • prod,计算常规产品(如乘法)
  • :,它创造了一个范围
  • map ,这是一个将函数应用于集合的每个元素的高阶函数

解决方案是:

julia> map(prod, Base.product(1:10, 1:10))
10×10 Array{Int64,2}:
  1   2   3   4   5   6   7   8   9   10
  2   4   6   8  10  12  14  16  18   20
  3   6   9  12  15  18  21  24  27   30
  4   8  12  16  20  24  28  32  36   40
  5  10  15  20  25  30  35  40  45   50
  6  12  18  24  30  36  42  48  54   60
  7  14  21  28  35  42  49  56  63   70
  8  16  24  32  40  48  56  64  72   80
  9  18  27  36  45  54  63  72  81   90
 10  20  30  40  50  60  70  80  90  100