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

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.



Modified text is an extract of the original Stack Overflow Documentation
Sous licence CC BY-SA 3.0
Non affilié à Stack Overflow