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, tills a+(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"
0.5.0

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.



Modified text is an extract of the original Stack Overflow Documentation
Licensierat under CC BY-SA 3.0
Inte anslutet till Stack Overflow