Поиск…


Синтаксис

  • начать (ITR)
  • next (itr, s)
  • done (itr, s)
  • take (itr, n)
  • drop (itr, n)
  • цикл (ITR)
  • Base.product (xs, ys)

параметры

параметр подробности
За Все функции
itr Итерируемый для работы.
За next и done
s Состояние итератора, описывающее текущую позицию итерации.
За take и drop
n Количество элементов, которые нужно взять или удалить.
За Base.product
xs Итерируемое, чтобы взять первые элементы пар из.
ys Итерабельность для принятия вторых элементов пар из.
... (Обратите внимание, что product принимает любое количество аргументов, если предусмотрено более двух, он построит кортежи длиной более двух.)

Новый тип итерации

В Julia, когда цикл через итерируемый объект I выполняется с синтаксисом for :

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

За кулисами это переводится на:

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

Поэтому, если вы хотите , I быть итерацию, вам нужно определить start , next и done методы его типа. Предположим, вы определяете тип Foo содержащий массив как одно из полей:

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

Мы создаем объект Foo , выполняя:

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

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

Если мы хотим итерации через Foo , каждая bar элементов возвращается каждой итерацией, мы определяем методы:

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

Обратите внимание: поскольку эти функции принадлежат модулю Base , мы должны сначала import их имена, прежде чем добавлять к ним новые методы.

После определения методов Foo совместим с интерфейсом итератора:

julia> for i in I
           println(i)
       end

1
2
3

Объединение ленивых итераций

Стандартная библиотека поставляется с богатой коллекцией ленивых итераций (и библиотеки, такие как Iterators.jl, обеспечивают еще больше). Lazy iterables может быть составлен для создания более мощных итераций в постоянное время. Самые важные ленивые итерации - это выбор и удаление, из которых можно создать множество других функций.

Ленько нарежьте итерируемый

Массивы могут быть нарезаны нотной записью. Например, следующее возвращает 10-15 элементов массива, включая:

A[10:15]

Однако нотация среза не работает со всеми повторами. Например, мы не можем разрезать выражение генератора:

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

Строки для нарезки могут не иметь ожидаемого поведения в Юникоде:

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

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

Мы можем определить функцию lazysub(itr, range::UnitRange) чтобы сделать этот вид нарезки на произвольных итерациях. Это определяется в терминах take and drop :

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

Реализация здесь работает, потому что для значения UnitRange a:b выполняются следующие шаги:

  • сбрасывает первые a-1 элементы
  • берет a й элемент, a+1 й элемент и т. д., пока a+(ba)=b й элемент

Всего берутся элементы ba . Мы можем подтвердить, что наша реализация верна в каждом случае выше:

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

Ленько сдвиньте итерируемый кругооборот

Операция circshift на массивах будет перемещать массив, как если бы это был круг, а затем переиздавать его. Например,

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

Можем ли мы сделать это лениво для всех повторений? Мы можем использовать cycle , drop и take итерации для реализации этой функции.

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

Наряду с ленивыми типами, которые более circshift во многих ситуациях, это позволяет нам circshift функцию 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

Создание таблицы умножения

Давайте создадим таблицу умножения, используя ленивые итерационные функции для создания матрицы.

Ключевыми функциями для использования здесь являются:

  • Base.product , который вычисляет декартово произведение .
  • prod , который вычисляет регулярное произведение (как при умножении)
  • : , который создает диапазон
  • map , которая является функцией более высокого порядка, применяющей функцию к каждому элементу коллекции

Решение:

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

Лено-оцененные списки

Можно сделать простой лениво-оцененный список, используя изменяемые типы и замыкания . Ленько-оцененный список - это список, чьи элементы не оцениваются при его построении, а скорее при его доступе. Преимущества лениво оцениваемых списков включают возможность бесконечности.

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

Что действительно работает так, как на языке Haskell , где все списки лениво оцениваются:

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

На практике лучше использовать пакет Lazy.jl. Тем не менее, реализация ленивого списка выше проливает свет на важные детали о том, как построить свой собственный итерируемый тип.



Modified text is an extract of the original Stack Overflow Documentation
Лицензировано согласно CC BY-SA 3.0
Не связан с Stack Overflow