Julia Language
Iterables
Zoeken…
Syntaxis
- start (ITR)
- volgende (itr, s)
- gedaan (itr, s)
- nemen (itr, n)
- neerzetten (itr, n)
- cycle (ITR)
- Base.product (xs, ys)
parameters
| Parameter | Details |
|---|---|
| Voor | Alle functies |
itr | De iterabele om op te werken. |
| Voor | next en done |
s | Een iterator die de huidige positie van de iteratie beschrijft. |
| Voor | take en drop |
n | Het aantal elementen dat moet worden verwijderd of verwijderd. |
| Voor | Base.product |
xs | De iterabele om eerste elementen van paren van te nemen. |
ys | De iterabele om tweede elementen van paren van te nemen. |
| ... | (Merk op dat het product willekeurig aantal argumenten accepteert; als er meer dan twee worden opgegeven, worden er tuples met een lengte van meer dan twee geconstrueerd.) |
Nieuw iterabel type
In Julia, toen het doorlussen van een iterable object I is gedaan met de for syntax:
for i = I # or "for i in I"
# body
end
Achter de schermen wordt dit vertaald naar:
state = start(I)
while !done(I, state)
(i, state) = next(I, state)
# body
end
Daarom, als je wilt dat I een iterabel ben, moet je de start , next en done methoden definiëren voor het type. Stel dat u een type Foo met een array definieert als een van de velden:
type Foo
bar::Array{Int,1}
end
We instantiëren een Foo object door het volgende te doen:
julia> I = Foo([1,2,3])
Foo([1,2,3])
julia> I.bar
3-element Array{Int64,1}:
1
2
3
Willen we parafraseren door middel van Foo , met elk element bar wordt geretourneerd door elke iteratie, definiëren we de methoden:
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
Aangezien deze functies tot de Base behoren, moeten we eerst hun namen import voordat we er nieuwe methoden aan toevoegen.
Nadat de methoden zijn gedefinieerd, is Foo compatibel met de iterator-interface:
julia> for i in I
println(i)
end
1
2
3
Lazy Iterables combineren
De standaardbibliotheek wordt geleverd met een rijke verzameling luie iterables (en bibliotheken zoals Iterators.jl bieden nog meer). Lazy iterables kunnen worden samengesteld om krachtigere iterables in constante tijd te creëren. De belangrijkste luie eters zijn take and drop , waaruit vele andere functies kunnen worden gemaakt.
Snijd een iterabel
Arrays kunnen worden gesneden met plaknotatie. Het volgende retourneert bijvoorbeeld de 10e tot 15e elementen van een array, inclusief:
A[10:15]
Segmentnotatie werkt echter niet met alle iterables. We kunnen bijvoorbeeld geen generator-expressie segmenteren:
julia> (i^2 for i in 1:10)[3:5]
ERROR: MethodError: no method matching getindex(::Base.Generator{UnitRange{Int64},##1#2}, ::UnitRange{Int64})
Het snijden van strings kan niet het verwachte Unicode-gedrag:
julia> "αααα"[2:3]
ERROR: UnicodeError: invalid character index
in getindex(::String, ::UnitRange{Int64}) at ./strings/string.jl:130
julia> "αααα"[3:4]
"α"
We kunnen een functie lazysub(itr, range::UnitRange) om dit soort segmentatie op willekeurige iterables uit te voeren. Dit wordt gedefinieerd in termen van take and drop :
lazysub(itr, r::UnitRange) = take(drop(itr, first(r) - 1), last(r) - first(r) + 1)
De implementatie werkt hier omdat voor UnitRange waarde a:b de volgende stappen worden uitgevoerd:
- laat de eerste
a-1elementen vallen - neemt het
a-element,a+1-element, enzovoort, tot heta+(ba)=b-element
In totaal worden ba elementen genomen. We kunnen bevestigen dat onze implementatie in elk afzonderlijk geval correct is:
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
Verplaats een iterabele circulair lui
De circshift bewerking op arrays zal de array verschuiven alsof het een cirkel is en deze vervolgens opnieuw definiëren. Bijvoorbeeld,
julia> circshift(1:10, 3)
10-element Array{Int64,1}:
8
9
10
1
2
3
4
5
6
7
Kunnen we dit lui doen voor alle iterables? We kunnen het gebruiken cycle , drop , en take iterables om deze functionaliteit te implementeren.
lazycircshift(itr, n) = take(drop(cycle(itr), length(itr) - n), length(itr))
Samen met luie types die in veel situaties beter presteren, laat dit ons circshift achtige functionaliteit uitvoeren op types die het anders niet zouden ondersteunen:
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"
Een vermenigvuldigingstabel maken
Laten we een vermenigvuldigingstabel maken met behulp van luie itereerbare functies om een matrix te maken.
De belangrijkste functies die u hier kunt gebruiken zijn:
-
Base.product, dat een Cartesiaans product berekent. -
prod, die een regulier product berekent (zoals bij vermenigvuldiging) -
:, waarmee een bereik wordt gemaakt -
map, een hogere orde functie die een functie toepast op elk element van een verzameling
De oplossing is:
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 geëvalueerde lijsten
Het is mogelijk om een eenvoudige, lui geëvalueerde lijst te maken met behulp van veranderlijke typen en sluitingen . Een lui geëvalueerde lijst is een lijst waarvan de elementen niet worden geëvalueerd wanneer deze wordt samengesteld, maar eerder wanneer deze wordt geopend. Voordelen van lui geëvalueerde lijsten omvatten de mogelijkheid om oneindig te zijn.
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()
Dat werkt inderdaad zoals het zou doen in een taal als Haskell , waar alle lijsten lui worden geëvalueerd:
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
In de praktijk is het beter om het Lazy.jl- pakket te gebruiken. De implementatie van de luie lijst hierboven werpt echter belangrijke details op over hoe je een eigen iterabel type kunt bouwen.