Есть ли ленивый `фильтр` в Julia?

#julia

#джулия

Вопрос:

В Python можно использовать if понимание списка для фильтрации элементов. В Julia есть ли ленивый filter эквивалент?

 for x in filter(x->x<2, 1:3)
println(x)
end
  

работает и печатает только, 1 но filter(x->x<2, 1:3) с большим нетерпением, и поэтому может быть нежелательно для миллиардов записей.

Ответ №1:

Вы можете сделать это точно так же, как в Python:

 julia> function f()
           for x in (i for i in 1:10^9 if i == 10^9)
               println(x)
           end
       end
f (generic function with 1 method)

julia> @time f()
1000000000
  3.293702 seconds (139.87 k allocations: 7.107 MiB)

julia> @time f()
1000000000
  3.224707 seconds (11 allocations: 352 bytes)
  

и вы видите, что он не выделяет. Но быстрее просто выполнить тест фильтра внутри цикла без использования генератора:

 julia> function g()
           for x in 1:10^9
               x == 10^9 amp;amp; println(x)
           end
       end
g (generic function with 1 method)

julia> @time g()
1000000000
  2.098305 seconds (53.49 k allocations: 2.894 MiB)

julia> @time g()
1000000000
  2.094018 seconds (11 allocations: 352 bytes)
  

Редактировать Наконец-то вы можете использовать Iterators.filter :

 julia> function h()
          for x in Iterators.filter(==(10^9), 1:10^9)
              println(x)
          end
       end
h (generic function with 1 method)

julia>

julia> @time h()
1000000000
  0.390966 seconds (127.96 k allocations: 6.599 MiB)

julia> @time h()
1000000000
  0.311650 seconds (12 allocations: 688 bytes)
  

который в этом случае будет самым быстрым (см. Также https://docs.julialang.org/en/latest/base/iterators/#Iteration-utilities-1 ).

Возможно, вы также захотите проверить https://github.com/JuliaCollections/IterTools.jl.

ПРАВКА 2

Иногда Julia более мощная, чем вы могли бы подумать. Проверьте это:

 julia> function g2()
          for x in 1:1_000_000_000
              x == 1_000_000_000 amp;amp; println(x)
          end
       end
g2 (generic function with 1 method)

julia>

julia> @time g2()
1000000000
  0.029332 seconds (62.91 k allocations: 3.244 MiB)

julia> @time g2()
1000000000
  0.000636 seconds (11 allocations: 352 bytes)
  

и мы видим, что компилятор, по сути, скомпилировал все наши вычисления.

По сути — в предыдущем примере было запущено постоянное распространение и заменено 10^9 на 1_000_000_000 в Iterators.filter примере.

Поэтому мы должны разработать более умный тест. Вот оно:

 julia> using BenchmarkTools

julia> function f_rand(x)
           s = 0.0
           for v in (v for v in x if 0.1 < v < 0.2)
               s  = v
           end
           s
       end
f_rand (generic function with 1 method)

julia> function g_rand(x)
           s = 0.0
           for v in x
               if 0.1 < v < 0.2
                   s  = v
               end
           end
           s
       end
g_rand (generic function with 1 method)

julia> function h_rand(x)
           s = 0.0
           for v in Iterators.filter(v -> 0.1 < v < 0.2, x)
               s  = v
           end
           s
       end
h_rand (generic function with 1 method)

julia> x = rand(10^6);

julia> @btime f_rand($x)
  2.032 ms (0 allocations: 0 bytes)
14922.291597613703

julia> @btime g_rand($x)
  1.804 ms (0 allocations: 0 bytes)
14922.291597613703

julia> @btime h_rand($x)
  2.035 ms (0 allocations: 0 bytes)
14922.291597613703
  

И теперь мы получаем то, что я изначально ожидал (простой цикл с if является самым быстрым).

Комментарии:

1. Есть идеи, почему Iterator.filter это намного быстрее?

2. А — оптимизация компилятора срабатывает. Я подробно остановлюсь на них в ответе.

3. Меня выбросило, for x in (i for i in 1:10^9 if i == 10^9) я думал, что это будет нормально for x for i in 1:10^9 if i == 10^9; #do stuff; end . Но это потрясающе!

4. «И теперь мы получаем то, что я изначально ожидал (простой цикл с if является самым быстрым)». Интересно посмотреть, насколько близки эти цифры. Вы уверены, что разница статистически значима?

5.Вы можете протестировать себя :), также изменив размер x (другое оборудование может дать немного разные результаты), но, учитывая, что мои тесты g_rand будут самыми быстрыми, f_rand и h_rand сопоставимы (для больших x h_rand кажется, немного быстрее).