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 das a+(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"
0,5,0

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.



Modified text is an extract of the original Stack Overflow Documentation
Lizenziert unter CC BY-SA 3.0
Nicht angeschlossen an Stack Overflow