Julia Language
Iterables
Sök…
Syntax
- start (itr)
- nästa (itr, s)
- gjort (itr, s)
- ta (itr, n)
- släpp (itr, n)
- cykel (itr)
- Base.product (xs, ys)
parametrar
Parameter | detaljer |
---|---|
För | Alla funktioner |
itr | Det iterable att arbeta på. |
För | next och done |
s | Ett iteratortillstånd som beskriver iterationens aktuella position. |
För | take och drop |
n | Antalet element som ska tas eller släppas. |
För | Base.product |
xs | Det iterable att ta första delar av par från. |
ys | Det iterable att ta andra element av par från. |
... | (Observera att product accepterar valfritt antal argument; om mer än två tillhandahålls kommer den att konstruera tuplor med längd större än två.) |
Ny iterable typ
I Julia när looping genom ett iterable objekt I
görs med for
syntax:
for i = I # or "for i in I" # body end
Bakom kulisserna översätts detta till:
state = start(I) while !done(I, state) (i, state) = next(I, state) # body end
Därför, om du vill att I
ska vara en iterable, måste du definiera start
, next
och done
metoder för dess typ. Anta att du definierar en Foo
typ som innehåller en matris som ett av fälten:
type Foo bar::Array{Int,1} end
Vi instanserar ett Foo
objekt genom att göra:
julia> I = Foo([1,2,3])
Foo([1,2,3])
julia> I.bar
3-element Array{Int64,1}:
1
2
3
Om vi vill iterera genom Foo
, med varje element bar
som returneras av varje iteration, vi definierar metoder:
import Base: start, next, done start(I::Foo) = 1 next(I::Foo, state) = (I.bar[state], state+1) function done(I::Foo, state) if state == length(I.bar) return true end return false end
Observera att eftersom dessa funktioner hör till Base
-modulen, måste vi först import
deras namn innan du lägger nya metoder för dem.
När metoderna har definierats är Foo
kompatibel med iteratorgränssnittet:
julia> for i in I println(i) end 1 2 3
Kombinera Lazy Iterables
Standardbiblioteket har en rik samling av lata iterables (och bibliotek som Iterators.jl ger ännu mer). Lazy iterables kan komponeras för att skapa mer kraftfulla iterables i konstant tid. De viktigaste lata iterables är take and drop , från vilka många andra funktioner kan skapas.
Lazily skiva en iterable
Matriser kan skivas med skivanotation. Till exempel returnerar följande de 10: e till 15: e elementen i en matris, inklusive:
A[10:15]
Skivanotationen fungerar dock inte med alla iterables. Vi kan till exempel inte skära ett generatoruttryck:
julia> (i^2 for i in 1:10)[3:5]
ERROR: MethodError: no method matching getindex(::Base.Generator{UnitRange{Int64},##1#2}, ::UnitRange{Int64})
Skivning strängar kanske inte har den förväntade Unicode beteende:
julia> "αααα"[2:3]
ERROR: UnicodeError: invalid character index
in getindex(::String, ::UnitRange{Int64}) at ./strings/string.jl:130
julia> "αααα"[3:4]
"α"
Vi kan definiera en funktion lazysub(itr, range::UnitRange)
att göra denna typ av skärning på godtyckliga iterables. Detta definieras i termer av take
and drop
:
lazysub(itr, r::UnitRange) = take(drop(itr, first(r) - 1), last(r) - first(r) + 1)
Implementeringen här fungerar eftersom för UnitRange
värde a:b
utförs följande steg:
- tappar de första
a-1
elementen - tar det
a
a elementet,a+1
e element, och så vidare, tillsa+(ba)=b
e elementet
Totalt tas ba
element. Vi kan bekräfta att vår implementering är korrekt i båda fallen ovan:
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
Lazily växla en iterable cirkulärt
circshift
operationen på matriser kommer att förskjuta matrisen som om det var en cirkel och sedan återlansera den. Till exempel,
julia> circshift(1:10, 3)
10-element Array{Int64,1}:
8
9
10
1
2
3
4
5
6
7
Kan vi göra detta lätt för alla iterables? Vi kan använda cycle
, drop
och take
iterables för att implementera denna funktionalitet.
lazycircshift(itr, n) = take(drop(cycle(itr), length(itr) - n), length(itr))
Tillsammans med att lata typer är mer framträdande i många situationer, gör det möjligt för oss att göra circshift
funktioner på typer som annars inte skulle stödja det:
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"
Att göra en multiplikationstabell
Låt oss göra en multiplikationstabell med lata iterbara funktioner för att skapa en matris.
De viktigaste funktionerna som används här är:
-
Base.product
, som beräknar en kartesisk produkt . -
prod
, som beräknar en vanlig produkt (som i multiplikation) -
:
, vilket skapar ett intervall -
map
, som är en högre ordningsfunktion som tillämpar en funktion på varje element i en samling
Lösningen är:
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
Lazily-utvärderade listor
Det är möjligt att göra en enkel lata-utvärderad lista med hjälp av muterbara typer och stängningar . En lata utvärderad lista är en lista vars element inte utvärderas när den är konstruerad, utan snarare när den öppnas. Fördelarna med lata utvärderade listor inkluderar möjligheten att vara oändliga.
import Base: getindex
type Lazy
thunk
value
Lazy(thunk) = new(thunk)
end
evaluate!(lazy::Lazy) = (lazy.value = lazy.thunk(); lazy.value)
getindex(lazy::Lazy) = isdefined(lazy, :value) ? lazy.value : evaluate!(lazy)
import Base: first, tail, start, next, done, iteratorsize, HasLength, SizeUnknown
abstract List
immutable Cons <: List
head
tail::Lazy
end
immutable Nil <: List end
macro cons(x, y)
quote
Cons($(esc(x)), Lazy(() -> $(esc(y))))
end
end
first(xs::Cons) = xs.head
tail(xs::Cons) = xs.tail[]
start(xs::Cons) = xs
next(::Cons, xs) = first(xs), tail(xs)
done(::List, ::Cons) = false
done(::List, ::Nil) = true
iteratorsize(::Nil) = HasLength()
iteratorsize(::Cons) = SizeUnknown()
Vilket verkligen fungerar som på ett språk som Haskell , där alla listor bedöms latent:
julia> xs = @cons(1, ys)
Cons(1,Lazy(false,#3,#undef))
julia> ys = @cons(2, xs)
Cons(2,Lazy(false,#5,#undef))
julia> [take(xs, 5)...]
5-element Array{Int64,1}:
1
2
1
2
1
I praktiken är det bättre att använda paket Lazy.jl. Implementeringen av den lata listan ovan kastar emellertid ljus i viktiga detaljer om hur man konstruerar sin egen iterable typ.