Как использовать элемент массива в качестве итератора в Юлии? - PullRequest
3 голосов
/ 26 марта 2020

Я пытаюсь сделать свой код Джулии более понятным (для физиков) и подумал, что было бы неплохо, если бы я мог использовать какой-то итератор векторного типа. Я пытаюсь использовать каждый элемент в качестве итератора. Мое решение до сих пор:

kcut=2
? = Array{Float64}(undef,3)
Ø = [0, 0, 0]

for ?[1] in -kcut:kcut, ?[2] in -kcut:kcut, ?[3] in -kcut:kcut
    if norm(?)^2 <= kcut^2 && ? != Ø
        println(?)
    end
end
println(?)

, которое почти делает то, что должно. Единственная проблема заключается в том, что, как только я закончил, для l oop my M переопределяется в последнюю конфигурацию, которую он принял в l oop, [2,2,2] в этом случае. Похоже, это не тот случай, когда я перебираю обычную переменную, как показано ниже

x=1
for x in 1:10
    println(x)
end
println(x)

, которая говорит мне, что x по-прежнему равен 1 после l oop. Я хотел бы иметь такое же поведение, если это возможно. Кроме того, есть ли способ сделать это без определения M , прежде чем я использую его для итерации?

РЕДАКТИРОВАТЬ: мне нужно M для вывода в виде массива, чтобы я мог сделайте на нем линейную алгебру.

Ответы [ 2 ]

5 голосов
/ 26 марта 2020

Вы можете выполнить итерацию непосредственно на M , например, так:

julia> using LinearAlgebra: norm
julia> kcut=2;
julia> Ø = [0, 0, 0];

julia> for ? in Iterators.product(-kcut:kcut, -kcut:kcut, -kcut:kcut)
           if norm(?)^2 <= kcut^2 && ? != Ø
               # ? is a Tuple now, which should be best in most cases.
               #
               # If you want arrays instead:
               ? = collect(?)

               println(?, "\t", ?)
           end
       end
(0, 0, -2)      [0, 0, -2]
(-1, -1, -1)    [-1, -1, -1]
(0, -1, -1)     [0, -1, -1]
(1, -1, -1)     [1, -1, -1]
(-1, 0, -1)     [-1, 0, -1]
(0, 0, -1)      [0, 0, -1]
(1, 0, -1)      [1, 0, -1]
(-1, 1, -1)     [-1, 1, -1]
...

julia> println(?)
ERROR: UndefVarError: ? not defined
Stacktrace:
 [1] top-level scope at REPL[5]:1

Таким образом, M определяется только в области действия for л oop.

Чтобы понять поведение вашего исходного кода, вы должны иметь базовое c понимание преобразований кода («понижение кода»), которые выполняет Юлия внутренне. В частности, for l oop заменяется в соответствии с правилами интерфейса итерация

В целом, фрагмент выглядит следующим образом:

M = [42]
for M[1] in -k:k
    println(M[1])
end

ведет себя так же, как:

M = [42]
next = iterate(-k:k)
while next !== nothing
    M[1] = i
    println(M[1])
    next = iterate(iter, state)
end

Там, где вы видите, что M должен существовать заранее, чтобы M[1]=i не отказывал, и нет никаких причин, почему то, что происходит внутри l oop тело не сохраняется после него.

3 голосов
/ 26 марта 2020

Вы можете сделать следующее с StaticArrays, чтобы получить «кортежи, которые могут выполнять линейную алгебру»:

julia> using StaticArrays

julia> ⊗(u, v) = (i ⊗ j for i in u for j in v)
⊗ (generic function with 1 method)

julia> ⊗(x::Number, y::Number) = SVector(x, y)
⊗ (generic function with 2 methods)

julia> ⊗(x::SVector{N}, y::Number) where {N} = SVector(x..., y)
⊗ (generic function with 3 methods)

julia> collect((1:3) ⊗ (10:12) ⊗ (100:101))
18-element Array{SArray{Tuple{3},Int64,1,3},1}:
 [1, 10, 100]
 [1, 10, 101]
 [1, 11, 100]
 [1, 11, 101]
 [1, 12, 100]
 [1, 12, 101]
 [2, 10, 100]
 [2, 10, 101]
 [2, 11, 100]
 [2, 11, 101]
 [2, 12, 100]
 [2, 12, 101]
 [3, 10, 100]
 [3, 10, 101]
 [3, 11, 100]
 [3, 11, 101]
 [3, 12, 100]
 [3, 12, 101]

julia> using LinearAlgebra: norm

julia> for M in (1:3) ⊗ (10:12) ⊗ (100:101)
           println(norm(M))
       end
100.50373127401788
101.49876846543509
100.60815076324582
101.6021653312566
100.72239075796404
101.7152889196113
100.5186549850325
101.51354589413178
100.62305898749054
101.61692772368194
100.73728207570423
101.73003489628813
100.54352291420865
101.5381701627521
100.64790112068906
101.64152694642087
100.7620960480676
101.75460677532

Но я не уверен, что оно того стоит. В идеале, \otimes для SVector s и Number s будет создавать ленивую структуру данных, которая создает только SVector s соответствующего размера при итерации (вместо разделения, как здесь). Мне просто лень писать это сейчас.

Лучший вариант (но чуть менее математический синтаксис) - перегрузить ⨂(spaces...), чтобы сделать все сразу:

julia> ⨂(spaces::NTuple{N}) where {N} = (SVector{N}(t) for t in Iterators.product(spaces...))
⨂ (generic function with 1 method)

julia> ⨂(spaces...) = ⨂(spaces)
⨂ (generic function with 2 methods)

julia> collect(⨂(1:3, 10:11))
3×2 Array{SArray{Tuple{2},Int64,1,2},2}:
 [1, 10]  [1, 11]
 [2, 10]  [2, 11]
 [3, 10]  [3, 11]

julia> collect(⨂(1:3, 10:11, 100:101))
3×2×2 Array{SArray{Tuple{3},Int64,1,3},3}:
[:, :, 1] =
 [1, 10, 100]  [1, 11, 100]
 [2, 10, 100]  [2, 11, 100]
 [3, 10, 100]  [3, 11, 100]

[:, :, 2] =
 [1, 10, 101]  [1, 11, 101]
 [2, 10, 101]  [2, 11, 101]
 [3, 10, 101]  [3, 11, 101]

Это собирает в другую форму, хотя я бы посчитал это еще более уместным.

...