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

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.



Modified text is an extract of the original Stack Overflow Documentation
Autorizzato sotto CC BY-SA 3.0
Non affiliato con Stack Overflow