結合懶惰的 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)
來對任意迭代進行這種切片。這是根據 take
和 drop
定義的:
lazysub(itr, r::UnitRange) = take(drop(itr, first(r) - 1), last(r) - first(r) + 1)
這裡的實現有效,因為對於 UnitRange
值 a:b
,執行以下步驟:
- 放下第一個
a-1
元素 - 取
a
th 元素,a+1
th 元素,等等,直到a+(b-a)=b
th 元素
總共採用了 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
我們可以懶洋洋地為所有迭代事件做這件事嗎?我們可以使用 cycle
,drop
和 take
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
製作乘法表
讓我們使用惰性可迭代函式建立一個乘法表來建立一個矩陣。
這裡使用的關鍵功能是:
解決方案是:
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