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-1
elementen 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.