Buscar..


Sintaxis

  • empezar (itr)
  • siguiente (itr, s)
  • hecho (itr, s)
  • tomar (itr, n)
  • soltar (itr, n)
  • ciclo (itr)
  • Producto base (xs, ys)

Parámetros

Parámetro Detalles
por Todas las funciones
itr Lo iterable para operar.
por next y done
s Un estado de iterador que describe la posición actual de la iteración.
por take y drop
n El número de elementos a tomar o soltar.
por Base.product
xs Lo iterable para sacar los primeros elementos de las parejas.
ys Lo iterable para tomar segundos elementos de pares.
... (Tenga en cuenta que el product acepta cualquier número de argumentos; si se proporcionan más de dos, se construirán tuplas de longitud mayor que dos).

Nuevo tipo iterable

En Julia, cuando se realiza un bucle a través de un objeto iterable I se hace con la sintaxis for :

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

Detrás de escena, esto se traduce a:

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

Por lo tanto, si desea I sea ​​un iterable, debe definir los métodos de start , next y done para su tipo. Supongamos que define un tipo Foo contiene una matriz como uno de los campos:

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

Creamos una instancia de un objeto Foo haciendo:

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

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

Si queremos iterar a través de Foo , con cada bar elementos que devuelve cada iteración, definimos los métodos:

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

Tenga en cuenta que dado que estas funciones pertenecen al módulo Base , primero debemos import sus nombres antes de agregarles nuevos métodos.

Después de definir los métodos, Foo es compatible con la interfaz del iterador:

julia> for i in I
           println(i)
       end

1
2
3

Combinando Lazy Iterables

La biblioteca estándar viene con una rica colección de iterables (y bibliotecas como Iterators.jl proporcionan aún más). Los iterables perezosos se pueden componer para crear iterables más potentes en tiempo constante. Los iterables perezosos más importantes son tomar y soltar , a partir de los cuales se pueden crear muchas otras funciones.

Perezamente cortar una iterable

Los arreglos pueden ser cortados con notación de corte. Por ejemplo, lo siguiente devuelve los elementos del 10 al 15 de una matriz, inclusive:

A[10:15]

Sin embargo, la notación de segmento no funciona con todos los iterables. Por ejemplo, no podemos cortar una expresión del generador:

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

Cortar cadenas puede no tener el comportamiento esperado de Unicode:

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

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

Podemos definir una función lazysub(itr, range::UnitRange) para hacer este tipo de corte en iterables arbitrarios. Esto se define en términos de take y drop :

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

La implementación aquí funciona porque para el valor de UnitRange a:b , se realizan los siguientes pasos:

  • Deja caer los primeros elementos a a-1
  • toma la a ésimo elemento, a+1 -ésimo elemento, y así sucesivamente, hasta que el a+(ba)=b -ésimo elemento

En total, se toman elementos ba . Podemos confirmar que nuestra implementación es correcta en cada caso anterior:

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

Cambia perezosamente un iterable circularmente

La operación de cambio de circshift en los arreglos cambiará el arreglo como si fuera un círculo, y luego lo volverá a alinear. Por ejemplo,

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

¿Podemos hacer esto perezosamente para todos los iterables? Podemos usar el cycle , drop y take iterables para implementar esta funcionalidad.

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

Además de que los tipos perezosos son más circshift en muchas situaciones, esto nos permite realizar circshift función similar a la de circshift en tipos que de otro modo no lo circshift :

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

Haciendo una tabla de multiplicar

Hagamos una tabla de multiplicar usando funciones iterables para crear una matriz.

Las funciones clave a utilizar aquí son:

  • Base.product , que calcula un producto cartesiano .
  • prod , que calcula un producto regular (como en la multiplicación)
  • : , lo que crea un rango
  • map , que es una función de orden superior que aplica una función a cada elemento de una colección.

La solucion es:

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

Listas de pereza evaluadas

Es posible hacer una lista simple y perezosamente evaluada utilizando tipos y cierres mutables. Una lista evaluada perezosamente es una lista cuyos elementos no se evalúan cuando se construye, sino cuando se accede a ella. Los beneficios de las listas evaluadas perezosamente incluyen la posibilidad de ser infinitas.

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

Lo que de hecho funciona como lo haría en un lenguaje como Haskell , donde todas las listas se evalúan perezosamente:

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 la práctica, es mejor usar el paquete Lazy.jl. Sin embargo, la implementación de la lista perezosa de arriba arroja luces sobre detalles importantes sobre cómo construir el propio tipo de iterable.



Modified text is an extract of the original Stack Overflow Documentation
Licenciado bajo CC BY-SA 3.0
No afiliado a Stack Overflow