Julia Language
Iterables
Suche…
Syntax
- start (itr)
- nächstes (itr, s)
- fertig (itr, s)
- nimm (itr, n)
- drop (itr, n)
- Zyklus (itr)
- Basisprodukt (xs, ys)
Parameter
Parameter | Einzelheiten |
---|---|
Zum | Alle Funktionen |
itr | Die iterable zu operieren. |
Zum | next und done |
s | Ein Iteratorstatus, der die aktuelle Position der Iteration beschreibt. |
Zum | take und drop |
n | Die Anzahl der Elemente, die genommen oder fallen sollen. |
Zum | Base.product |
xs | Das iterable, um erste Elemente von Paaren zu nehmen. |
ys | Das iterable, um zweite Elemente von Paaren zu nehmen. |
... | (Beachten Sie, dass das product eine beliebige Anzahl von Argumenten akzeptiert. Wenn mehr als zwei angegeben werden, werden Tupel mit einer Länge von mehr als zwei erstellt.) |
Neuer iterierbarer Typ
In Julia, wenn I
ein iterierbares Objekt durchläuft, habe I
die for
Syntax:
for i = I # or "for i in I" # body end
Hinter den Kulissen heißt das:
state = start(I) while !done(I, state) (i, state) = next(I, state) # body end
Deshalb, wenn Sie wollen , I
ein iterable sein, müssen Sie definieren start
, next
und done
Methoden für seine Art. Angenommen, Sie definieren einen Typ Foo
, der ein Array enthält, als eines der Felder:
type Foo bar::Array{Int,1} end
Wir instantiieren ein Foo
Objekt, indem wir Folgendes tun:
julia> I = Foo([1,2,3])
Foo([1,2,3])
julia> I.bar
3-element Array{Int64,1}:
1
2
3
Wenn wir durch iterieren wollen Foo
, wobei jedes Element bar
von jeder Iteration zurückgegeben werden, definieren wir die 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
Beachten Sie, dass , da diese Funktionen in denen gehören Base
müssen wir zuerst import
, um sie vor dem Hinzufügen von neuen Methoden , um ihre Namen.
Nachdem die Methoden definiert sind, ist Foo
mit der Iteratorschnittstelle kompatibel:
julia> for i in I println(i) end 1 2 3
Lazy Iterables kombinieren
Die Standardbibliothek verfügt über eine umfangreiche Sammlung von Lazy-Iterables (und Bibliotheken wie Iterators.jl bieten noch mehr). Lazy iterables können erstellt werden, um in konstanter Zeit leistungsfähigere iterables zu erstellen. Die wichtigsten faulen Iterables sind take and drop , aus denen viele andere Funktionen erstellt werden können.
Lazy Slice eine iterable
Arrays können mit Slice-Notation geschnitten werden. Das Folgende gibt beispielsweise das 10. bis 15. Element eines Arrays inklusive zurück:
A[10:15]
Die Slice-Notation funktioniert jedoch nicht mit allen Iterables. Zum Beispiel können wir keinen Generatorausdruck schneiden:
julia> (i^2 for i in 1:10)[3:5]
ERROR: MethodError: no method matching getindex(::Base.Generator{UnitRange{Int64},##1#2}, ::UnitRange{Int64})
Das Aufteilen von Zeichenfolgen weist möglicherweise nicht das erwartete Unicode-Verhalten auf:
julia> "αααα"[2:3]
ERROR: UnicodeError: invalid character index
in getindex(::String, ::UnitRange{Int64}) at ./strings/string.jl:130
julia> "αααα"[3:4]
"α"
Wir können eine Funktion lazysub(itr, range::UnitRange)
, um diese Art von Slicing auf beliebigen lazysub(itr, range::UnitRange)
. Dies ist in Bezug auf take
und drop
:
lazysub(itr, r::UnitRange) = take(drop(itr, first(r) - 1), last(r) - first(r) + 1)
Die Implementierung funktioniert hier, da für den UnitRange
Wert a:b
die folgenden Schritte ausgeführt werden:
- löscht die ersten
a-1
Elemente - nimmt das
a
te Element,a+1
-Element usw., bis dasa+(ba)=b
te Element
Insgesamt werden ba
Elemente genommen. Wir können bestätigen, dass unsere Implementierung jeweils oben korrekt ist:
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
Verschieben Sie eine iterable Zirkel
Die circshift
Operation von Arrays verschiebt das Array wie einen Kreis und zeigt es dann erneut an. Zum Beispiel,
julia> circshift(1:10, 3)
10-element Array{Int64,1}:
8
9
10
1
2
3
4
5
6
7
Können wir das faul für alle Iterationen machen? Wir können den cycle
, das drop
und take
Durchlaufen von iterablen Funktionen verwenden, um diese Funktionalität zu implementieren.
lazycircshift(itr, n) = take(drop(cycle(itr), length(itr) - n), length(itr))
Neben lazy-Typen, die in vielen Situationen leistungsfähiger sind, können wir damit eine circshift
Funktion für Typen circshift
, die sie sonst nicht unterstützen würden:
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"
Multiplikationstabelle erstellen
Lassen Sie uns eine Multiplikationstabelle erstellen, indem Sie faul iterierbare Funktionen verwenden, um eine Matrix zu erstellen.
Die wichtigsten Funktionen, die hier verwendet werden können, sind:
-
Base.product
, das ein kartesisches Produkt berechnet. -
prod
, der ein reguläres Produkt berechnet (wie bei der Multiplikation) -
:
, wodurch ein Bereich entsteht -
map
, eine Funktion höherer Ordnung, die auf jedes Element einer Sammlung eine Funktion anwendet
Die Lösung ist:
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
Faul bewertete Listen
Es ist möglich, eine einfache, faul bewertete Liste mit veränderlichen Typen und Schließungen zu erstellen . Eine Liste mit langsamer Auswertung ist eine Liste, deren Elemente nicht beim Erstellen, sondern beim Zugriff ausgewertet werden. Die Vorteile von faul bewerteten Listen beinhalten die Möglichkeit, unendlich zu sein.
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()
Was tatsächlich so funktioniert wie in einer Sprache wie Haskell , in der alle Listen faul bewertet werden:
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 der Praxis ist es besser, das Lazy.jl- Paket zu verwenden. Die Implementierung der Lazy-Liste oben beleuchtet jedoch wichtige Details zum Konstruieren des eigenen iterierbaren Typs.