Szukaj…


Składnia

  • start (itr)
  • dalej (itr, s)
  • zrobione (itr, s)
  • weź (itr, n)
  • drop (itr, n)
  • cykl (itr)
  • Base.product (xs, ys)

Parametry

Parametr Detale
Dla Wszystkie funkcje
itr Iterowalny w obsłudze.
Dla next i done
s Stan iteratora opisujący bieżącą pozycję iteracji.
Dla take i drop
n Liczba elementów do pobrania lub upuszczenia.
Dla Base.product
xs Iterowalne do wzięcia pierwszych elementów par.
ys Iterowalne pobieranie drugich elementów par.
... (Zauważ, że product akceptuje dowolną liczbę argumentów; jeśli podano więcej niż dwa, utworzy krotki o długości większej niż dwa).

Nowy typ iterowalny

W Julia, gdy zapętlenie poprzez iterowalny obiektu I odbywa się za pomocą for składni:

for i = I   # or  "for i in I"
    # body
end

Za kulisami jest to tłumaczone na:

state = start(I)
while !done(I, state)
    (i, state) = next(I, state)
    # body
end

Dlatego jeśli chcesz, I był iterowalny, musisz zdefiniować metody start , next i done dla tego typu. Załóżmy, że zdefiniujesz typ Foo zawierający tablicę jako jedno z pól:

type Foo
    bar::Array{Int,1}
end

Tworzymy instancję obiektu Foo , wykonując:

julia> I = Foo([1,2,3])
Foo([1,2,3])

julia> I.bar
3-element Array{Int64,1}:
 1
 2
 3

Jeśli chcemy iterować przez Foo , a każdy bar elementów jest zwracany przez każdą iterację, definiujemy metody:

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

Zauważ, że ponieważ te funkcje należą do modułu Base , musimy najpierw import ich nazwy przed dodaniem do nich nowych metod.

Po zdefiniowaniu metod Foo jest kompatybilny z interfejsem iteratora:

julia> for i in I
           println(i)
       end

1
2
3

Łączenie Lazy Iterables

Standardowa biblioteka zawiera bogatą kolekcję leniwych iteracji (a biblioteki takie jak Iterators.jl zapewniają jeszcze więcej). Leniwe iteracje można komponować, aby tworzyć mocniejsze iteracje w stałym czasie. Najważniejszymi leniwymi iterami są wzięcia i upuszczenia , z których można utworzyć wiele innych funkcji.

Lazily pokroić iterowalny

Tablice można kroić w notację plastra. Na przykład następujące zwraca 10 do 15 elementów tablicy, w tym:

A[10:15]

Notacja wycinka nie działa jednak ze wszystkimi iteracjami. Na przykład nie możemy pokroić wyrażenia generatora:

julia> (i^2 for i in 1:10)[3:5]
ERROR: MethodError: no method matching getindex(::Base.Generator{UnitRange{Int64},##1#2}, ::UnitRange{Int64})

Krojenie łańcuchów może nie mieć oczekiwanego zachowania Unicode:

julia> "αααα"[2:3]
ERROR: UnicodeError: invalid character index
 in getindex(::String, ::UnitRange{Int64}) at ./strings/string.jl:130

julia> "αααα"[3:4]
"α"

Możemy zdefiniować funkcję lazysub(itr, range::UnitRange) do wykonywania tego rodzaju lazysub(itr, range::UnitRange) na dowolnych iteracjach. Jest to definiowane jako take i drop :

lazysub(itr, r::UnitRange) = take(drop(itr, first(r) - 1), last(r) - first(r) + 1)

Implementacja tutaj działa, ponieważ dla wartości UnitRange a:b wykonywane są następujące kroki:

  • upuszcza pierwsze elementy a-1
  • wykonuje a elementu TH, a+1 tego elementu, i tak dalej, aż do a+(ba)=b -tego elementu

Łącznie pobierane są elementy ba . Możemy potwierdzić, że nasze wdrożenie jest poprawne w każdym przypadku powyżej:

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

Leniwie przesuwa iterowalny cyklicznie

circshift operacji na macierzy przesuwa tablicy, jak gdyby były koło, następnie relinearize go. Na przykład,

julia> circshift(1:10, 3)
10-element Array{Int64,1}:
  8
  9
 10
  1
  2
  3
  4
  5
  6
  7

Czy możemy to zrobić leniwie dla wszystkich iteracji? Możemy użyć cycle , drop i take iterable do wdrożenia tej funkcji.

lazycircshift(itr, n) = take(drop(cycle(itr), length(itr) - n), length(itr))

Oprócz tego, że leniwe typy są bardziej wydajne w wielu sytuacjach, pozwala nam to circshift funkcje podobne do circshift na typach, które w innym przypadku nie byłyby obsługiwane:

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

Tworzenie tabliczki mnożenia

Stwórzmy tabliczkę mnożenia używając leniwych iterowalnych funkcji do stworzenia macierzy.

Kluczowe funkcje, których można tu użyć, to:

  • Base.product , który oblicza iloczyn kartezjański .
  • prod , który oblicza zwykły produkt (jak w mnożeniu)
  • : , który tworzy zakres
  • map , która jest funkcją wyższego rzędu stosującą funkcję do każdego elementu kolekcji

Rozwiązaniem jest:

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

Listy leniwie oceniane

Możliwe jest utworzenie prostej leniwie ocenianej listy przy użyciu zmiennych typów i zamknięć . Leniwie oceniana lista to lista, której elementy nie są oceniane podczas konstruowania, ale raczej przy dostępie. Korzyści z leniwie ocenianych list obejmują możliwość bycia nieskończonym.

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()

Co rzeczywiście działa tak, jak w języku takim jak Haskell , w którym wszystkie listy są leniwie oceniane:

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

W praktyce lepiej jest użyć pakietu Lazy.jl. Jednak implementacja powyższej leniwej listy rzuca światło na ważne szczegóły dotyczące tego, jak zbudować własny typ iterowalny.



Modified text is an extract of the original Stack Overflow Documentation
Licencjonowany na podstawie CC BY-SA 3.0
Nie związany z Stack Overflow