Julia Language
iterabili
Ricerca…
Sintassi
- avviare (ITR)
- next (itr, s)
- fatto (itr, s)
- prendere (itr, n)
- drop (itr, n)
- Ciclo (ITR)
- Base.product (xs, ys)
Parametri
Parametro | Dettagli |
---|---|
Per | Tutte le funzioni |
itr | L'iterabile su cui operare. |
Per | next e done |
s | Uno stato iteratore che descrive la posizione corrente dell'iterazione. |
Per | take e drop |
n | Il numero di elementi da prendere o rilasciare. |
Per | Base.product |
xs | L'iterabile per prendere i primi elementi di coppie da. |
ys | L'iterabile per prendere i secondi elementi di coppie da. |
... | (Nota che il product accetta un numero qualsiasi di argomenti, se ne vengono forniti più di due, costruirà tuple di lunghezza maggiore di due.) |
Nuovo tipo iterabile
In Julia, quando eseguo il ciclo su un oggetto iterabile, I
finito con la sintassi for
:
for i = I # or "for i in I" # body end
Dietro le quinte, questo è tradotto in:
state = start(I) while !done(I, state) (i, state) = next(I, state) # body end
Pertanto, se si desidera I
sia un iterable, è necessario definire i metodi start
, next
e done
per il suo tipo. Supponiamo di definire un tipo Foo
contenente un array come uno dei campi:
type Foo bar::Array{Int,1} end
Istanziamo un oggetto Foo
facendo:
julia> I = Foo([1,2,3])
Foo([1,2,3])
julia> I.bar
3-element Array{Int64,1}:
1
2
3
Se vogliamo iterare attraverso Foo
, con ogni bar
elementi che viene restituita da ogni iterazione, definiamo i metodi:
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
Nota che poiché queste funzioni appartengono al modulo Base
, dobbiamo prima import
loro nomi prima di aggiungere nuovi metodi.
Dopo aver definito i metodi, Foo
è compatibile con l'interfaccia iteratore:
julia> for i in I println(i) end 1 2 3
Combinare Iterables pigri
La libreria standard include una ricca collezione di file iterabili pigri (e le librerie come Iterators.jl forniscono ancora di più). È possibile creare file Lazy iterables per creare iterables più potenti in un tempo costante. I più importanti iterables pigri sono take and drop , da cui è possibile creare molte altre funzioni.
Affetta un po 'iterabile
Le matrici possono essere affettate con la notazione di sezione. Ad esempio, il seguente restituisce il decimo al quindicesimo elemento di un array, inclusi:
A[10:15]
Tuttavia, la notazione di sezione non funziona con tutti gli iterabili. Ad esempio, non possiamo suddividere un'espressione di generatore:
julia> (i^2 for i in 1:10)[3:5]
ERROR: MethodError: no method matching getindex(::Base.Generator{UnitRange{Int64},##1#2}, ::UnitRange{Int64})
La segmentazione delle stringhe potrebbe non avere il comportamento Unicode previsto:
julia> "αααα"[2:3]
ERROR: UnicodeError: invalid character index
in getindex(::String, ::UnitRange{Int64}) at ./strings/string.jl:130
julia> "αααα"[3:4]
"α"
Possiamo definire una funzione lazysub(itr, range::UnitRange)
per fare questo tipo di slicing su iterables arbitrari. Questo è definito in termini di take
and drop
:
lazysub(itr, r::UnitRange) = take(drop(itr, first(r) - 1), last(r) - first(r) + 1)
L'implementazione qui funziona perché per il valore UnitRange
a:b
, vengono eseguiti i seguenti passaggi:
- elimina i primi elementi
a-1
- ritiene
a
elemento esimo,a+1
-esimo elemento, e così via, fino a che laa+(ba)=b
esimo elemento
In totale, vengono presi gli elementi ba
. Possiamo confermare che la nostra implementazione è corretta in ogni caso sopra:
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
Pigramente spostare un iterable circolare
L'operazione circshift
sugli array sposterà la matrice come se fosse un cerchio, quindi la ricollocherà. Per esempio,
julia> circshift(1:10, 3)
10-element Array{Int64,1}:
8
9
10
1
2
3
4
5
6
7
Possiamo farlo pigramente per tutti gli iterabili? Possiamo usare il cycle
, drop
e take
iterabili per implementare questa funzionalità.
lazycircshift(itr, n) = take(drop(cycle(itr), length(itr) - n), length(itr))
Insieme con i tipi pigri che sono più performanti in molte situazioni, questo ci permette di fare circshift
funzionalità simile a quella di circshift
su tipi che altrimenti non la supportano:
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"
Fare una tabella di moltiplicazione
Creiamo una tabella di moltiplicazione usando le funzioni iterabili pigre per creare una matrice.
Le funzioni chiave da usare qui sono:
-
Base.product
, che calcola un prodotto cartesiano . -
prod
, che calcola un prodotto normale (come in moltiplicazione) -
:
, che crea un intervallo -
map
, che è una funzione di ordine superiore che applica una funzione a ciascun elemento di una raccolta
La soluzione è:
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
Liste con valutazione lenta
È possibile creare un semplice elenco ponderato usando tipi e chiusure mutevoli. Un elenco ponderato è un elenco i cui elementi non vengono valutati al momento della sua costruzione, ma piuttosto quando vi si accede. I vantaggi delle liste valutate pigramente includono la possibilità di essere infiniti.
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()
Che funziona davvero come farebbe in una lingua come Haskell , dove tutte le liste sono ponderate:
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 pratica, è meglio usare il pacchetto Lazy.jl. Tuttavia, l'implementazione dell'elenco pigro di cui sopra mette le luci in dettagli importanti su come costruire il proprio tipo iterabile.