Пытаюсь найти способ сконструировать "генератор" Джулии.

Я новичок в Юля. Я в основном программирую на python.

в python, если вам нужно перебрать множество значений, типично построить так называемый генератор для сохранения использования памяти. Вот один пример кода:

def generator(N):
    for i in range(N):
        yield i

я использую @time в IJulia, чтобы увидеть использование памяти. Вот мой пример кода:

[Update]: добавьте код для generator метод
(The generator способ)

function generator(N::Int)
    for i in 1:N
        produce(i)
    end
end

(генератор версия)

function fun_gener()
    sum = 0
    g = @task generator(100000)
    for i in g
        sum += i
    end
    sum
 end

@time fun_gener()
прошедшее время: 0.420731828 секунд (выделено 6507600 байт)

(массив версия)

function fun_arry()
    sum = 0
    c = [1:100000]
    for i in c
        sum += i
    end
    sum
end

@time fun_arry()
прошедшее время: 0.000629629 секунд (Выделено 800144 байта)

может кто-нибудь сказать мне, почему @task в этом случае потребуется больше места? И если я хочу сохранить использование памяти как дело с большим набором значений, что я могу сделать?

3 ответов


рекомендую "навороченного итераторы" blogpost Карла Фогеля, который обсуждает протокол итератора Джулии, задачи и совместные процедуры в некоторых деталях.

см. также task-aka-coroutines в документах julia.


в этом случае вы должны использовать тип диапазон (который определяет итератор протокол):

julia> function fun_arry()
           sum = 0
           c = 1:100000  # remove the brackets, makes this a Range
           for i in c
               sum += i
           end
           sum
       end
fun_arry (generic function with 1 method)

julia> fun_arry()  # warm up
5000050000

julia> @time fun_arry()
elapsed time: 8.965e-6 seconds (192 bytes allocated)
5000050000

быстрее и меньше выделенной памяти (так же, как xrange в python 2).

фрагмент из блога:

от https://github.com/JuliaLang/julia/blob/master/base/range.jl, Вот как определяется протокол итератора диапазона:

start(r::Ranges) = 0
next{T}(r::Range{T}, i) = (oftype(T, r.start + i*step(r)), i+1)
next{T}(r::Range1{T}, i) = (oftype(T, r.start + i), i+1)
done(r::Ranges, i) = (length(r) <= i)

обратите внимание, что следующий метод вычисляет значение итератора в состоянии i. Это отличается от итератора массива, который просто считывает элемент a[i] из памяти.

итераторы, которые используют отложенную оценку, как это может имейте важные преимущества представления. Если мы хотим перебирать целые числа от 1 до 10 000, перебор массива означает, что мы должны выделить около 80 МБ для его хранения. Диапазон требует только 16 байт; тот же размер, что и диапазон от 1 до 100 000 или от 1 до 100 000 000.


вы можете написать метод генератора (использование задач):

julia> function generator(n)
          for i in 1:n      # Note: we're using a Range here!
              produce(i)
          end
       end
generator (generic function with 2 methods)

julia> for x in Task(() -> generator(3))
          println(x)
       end
1
2
3

Примечание: Если вы замените диапазон этим, производительность будет намного хуже (и выделит больше памяти):

julia> @time fun_arry()
elapsed time: 0.699122659 seconds (9 MB allocated)
5000050000

этот вопрос был задан (и ответил) довольно давно. Поскольку этот вопрос занимает высокое место в поиске google, я хотел бы упомянуть, что и вопрос, и ответ устарели.

В настоящее время, я бы предложил проверить https://github.com/BenLauwens/ResumableFunctions.jl для библиотеки Julia с макросом, который реализует генераторы доходности, подобные Python.

using ResumableFunctions

@resumable function fibonnaci(n::Int) :: Int
  a = 0
  b = 1
  for i in 1:n-1
    @yield a
    a, b = b, a+b
  end
  a
end

for fib in fibonnaci(10)
  println(fib)
end

поскольку сфера его применения гораздо более ограничены, чем полный сопрограммы, это тоже заказ величины более эффективной, чем нажатие значений в канал, так как он может скомпилировать генератор в FSM. (Каналы заменили старую функцию Product (), упомянутую в вопросе и предыдущих ответах).

С учетом сказанного, я бы все равно предложил нажать на канал в качестве первого подхода, если производительность не является проблемой, потому что resumablefunctions иногда могут быть привередливыми при компиляции вашей функции и иногда могут поражать какое-то худшее поведение. В частности, потому что это макрос, который компилируется в FSM, а не в функцию, в настоящее время вам нужно аннотировать типы всех переменных в Resumablefunction, чтобы получить хорошую производительность, в отличие от функций vanilla Julia, где это обрабатывается JIT при первом вызове функции.


думаю, что Task был заменен Channel(). Использование в терминах генератора Фибоначчи Бена Лаувенса:

fibonacci(n) = Channel(ctype=Int) do c
    a = 1
    b = 1
    for i in 1:n
        push!(c, a)
        a, b = b, a + b
    end
end

его можно использовать используя

for a in fibonacci(10)
    println(a)
end
1                                                                                                                                                                                                                                                     
1                                                                                                                                                                                                                                                     
2                                                                                                                                                                                                                                                     
3                                                                                                                                                                                                                                                     
5                                                                                                                                                                                                                                                     
8                                                                                                                                                                                                                                                     
13                                                                                                                                                                                                                                                    
21                                                                                                                                                                                                                                                    
34                                                                                                                                                                                                                                                    
55