Julia Language
Iterables
Recherche…
Syntaxe
- commencer (itr)
- suivant (itr, s)
- fait (itr, s)
- prendre (itr, n)
- drop (itr, n)
- cycle (itr)
- Produit de base (xs, ys)
Paramètres
Paramètre | Détails |
---|---|
Pour | Toutes les fonctions |
itr | L'itérable à utiliser |
Pour | next et done |
s | Un état itérateur décrivant la position actuelle de l'itération. |
Pour | take et drop |
n | Le nombre d'éléments à prendre ou à laisser. |
Pour | Base.product |
xs | L'itérable de prendre les premiers éléments de paires à partir de. |
ys | L'itérable de prendre des seconds éléments de paires à partir de. |
... | (Notez que le product accepte un nombre quelconque d’arguments; si plus de deux arguments sont fournis, il construira des tuples de longueur supérieure à deux.) |
Nouveau type itérable
Julia, lors de la boucle à travers un objet itérable I
est fait avec la for
la syntaxe:
for i = I # or "for i in I" # body end
Dans les coulisses, ceci est traduit par:
state = start(I) while !done(I, state) (i, state) = next(I, state) # body end
Par conséquent, si vous voulez que I
sois itérable, vous devez définir les méthodes start
, next
et done
pour son type. Supposons que vous définissiez un type Foo
contenant un tableau comme l'un des champs:
type Foo bar::Array{Int,1} end
Nous instancions un objet Foo
en faisant:
julia> I = Foo([1,2,3])
Foo([1,2,3])
julia> I.bar
3-element Array{Int64,1}:
1
2
3
Si nous voulons effectuer une itération dans Foo
, avec chaque bar
éléments renvoyée par chaque itération, nous définissons les méthodes:
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
Notez que puisque ces fonctions appartiennent au module Base
, nous devons d'abord import
leurs noms avant de leur ajouter de nouvelles méthodes.
Une fois les méthodes définies, Foo
est compatible avec l’interface de l’itérateur:
julia> for i in I println(i) end 1 2 3
Combinaison d'Itables Paresseux
La bibliothèque standard est fournie avec une riche collection d’itérables paresseux (et les bibliothèques telles que Iterators.jl en offrent encore plus). Les itérables paresseux peuvent être composés pour créer des itérables plus puissants en temps constant. Les itérables paresseux les plus importants sont Take et Drop , à partir desquels de nombreuses autres fonctions peuvent être créées.
Assez tranché un iterable
Les tableaux peuvent être découpés avec la notation par tranche. Par exemple, les éléments suivants renvoient les éléments 10 à 15 d’un tableau, y compris:
A[10:15]
Cependant, la notation slice ne fonctionne pas avec toutes les itérables. Par exemple, nous ne pouvons pas découper une expression de générateur:
julia> (i^2 for i in 1:10)[3:5]
ERROR: MethodError: no method matching getindex(::Base.Generator{UnitRange{Int64},##1#2}, ::UnitRange{Int64})
Les chaînes de découpage peuvent ne pas avoir le comportement Unicode attendu:
julia> "αααα"[2:3]
ERROR: UnicodeError: invalid character index
in getindex(::String, ::UnitRange{Int64}) at ./strings/string.jl:130
julia> "αααα"[3:4]
"α"
Nous pouvons définir une fonction lazysub(itr, range::UnitRange)
pour effectuer ce type de découpage sur des itérables arbitraires. Ceci est défini en termes de take
et de drop
:
lazysub(itr, r::UnitRange) = take(drop(itr, first(r) - 1), last(r) - first(r) + 1)
L'implémentation fonctionne ici car pour la valeur UnitRange
a:b
, les étapes suivantes sont effectuées:
- laisse tomber les premiers éléments a
a-1
- prend
a
élément,a+1
élément et ainsi de suite jusqu'à ce que l'élémenta+(ba)=b
Au total, ba
éléments sont pris. Nous pouvons confirmer que notre implémentation est correcte dans chaque cas ci-dessus:
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
Déplacer paresseusement une circulaire itérative
La circshift
opération sur les tableaux se déplacera le tableau comme si elle était un cercle, relinéariser il. Par exemple,
julia> circshift(1:10, 3)
10-element Array{Int64,1}:
8
9
10
1
2
3
4
5
6
7
Pouvons-nous faire cela paresseusement pour tous les itérables? Nous pouvons utiliser le cycle
, drop
et take
itérables pour implémenter cette fonctionnalité.
lazycircshift(itr, n) = take(drop(cycle(itr), length(itr) - n), length(itr))
Avec les types paresseux étant plus performants dans de nombreuses situations, cela nous permet de faire des fonctionnalités circshift
sur des types qui ne le seraient pas autrement:
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"
Faire une table de multiplication
Faisons une table de multiplication en utilisant des fonctions itérables paresseuses pour créer une matrice.
Les fonctions clés à utiliser ici sont:
-
Base.product
, qui calcule un produit cartésien . -
prod
, qui calcule un produit régulier (comme dans la multiplication) -
:
, qui crée une gamme -
map
, qui est une fonction d'ordre supérieur appliquant une fonction à chaque élément d'une collection
La solution est la suivante:
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
Listes évaluées par la bouche
Il est possible de créer une liste simple, évaluée paresseusement, en utilisant des types et des fermetures mutables. Une liste évaluée paresseuse est une liste dont les éléments ne sont pas évalués lors de sa construction, mais plutôt lorsqu’on y accède. Les avantages des listes paresseuses évaluées incluent la possibilité d'être infinie.
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()
Ce qui fonctionne effectivement comme le ferait un langage comme Haskell , où toutes les listes sont évaluées paresseusement:
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
En pratique, il est préférable d'utiliser le package Lazy.jl. Cependant, l'implémentation de la liste paresseuse ci-dessus met en lumière des détails importants sur la façon de construire son propre type itérable.