前置き
julia言語での話ですが、他の言語でも当てはまるでしょう。
何も考えずに、コードを書いてると次のようなことが起こります。
(備忘録)
↓
複数の変数の定義であれば、
a = b = c = d = 0
このように初期化しておいて、あとからプログラムの進行に応じて値を書き換えて使うことは自然に行うと思いますが、複数の配列について同じことをすると問題が起こります。具体的には
x = y = u = v = Array{Float64}(undef, 2, 2)
のようにすると、x, y, u, vの配列のいずれかの中身を、書き換えると他の3つも変更されます。
それもそのはず、配列は参照渡し1なので当たり前なのですが、
(x, y, u, vというシンボルが、すべて同じインスタンスを参照します)
一瞬「ん?」となりました。
解決策
解決策としては、
x = Array{Float64}(undef, 2, 2)
y = Array{Float64}(undef, 2, 2)
u = Array{Float64}(undef, 2, 2)
v = Array{Float64}(undef, 2, 2)
でも良いのですが、冗長すぎてすこし嫌なので
x, y, u, v = map(_->Array{Float64}(undef, 2, 2), 1:4)
こうします。
★解決!!★(※コメントでご指摘頂いた通り、もっといい方法がありました。→【補足】へ)
ーーー
一つ注意点があります。
mapは配列Vector{...}
を返すので、
x = y = u = v = (map_->Array{Float64}(undef, 2, 2), 1:4)
typeof(x) # 結果:Vector{Matrix{Float64}}
つまり、x, y, u, vすべてに、Vector{Matrix{Float64}}のやつがぶち込まれています。なので、きちんと分割代入をするために、カンマを使ってあげましょう。
脳死でプログラムを書いていると間違えるので気をつけないとですね。
ーーー
【補足】(追記)
より良い方法をコメントでご指摘頂きました。ありがとうございます!
こちらのほうがスマートですね。
x, y, u, v = (similar(arrowmap, Float64) for _=1:4)
使い道
そもそも、なんでこんなことしたいのか?ですが、
グラフに矢印を描画するときに、4つの配列使いたくなるからです。
以下が例です。
この図のようなタイルの色に応じた矢印を描きたいとしたとき、下記のコードで描くことができます。
using Plots, MLStyle
n = 4
function generate_arrows(n)
arrowmap = rand(1:4, n, n)
return arrowmap
end
function putmap(arrowmap)
heatmap(arrowmap, legend=false, aspect_ratio=1)
l = 0.6 # 矢印の長さ
direction = Array{String}(undef, size(arrowmap)) # これは使ってません
x, y, u, v = map(_->Array{Float64}(undef, size(arrowmap)), 1:4)
for i in 1:n
for j in 1:n
direction[i, j], x[i, j], y[i, j], u[i, j], v[i, j] = @match arrowmap[i, j] begin
1 => ("↑", j, i-l/2, 0., l)
2 => ("→", j-l/2, i, l, 0.)
3 => ("↓", j, i+l/2, 0., -l)
4 => ("←", j+l/2, i, -l, 0.)
end
end
end
quiver!(vec(x), vec(y), quiver=(vec(u), vec(v)), color=:white, lw=4)
quiver!(vec(x), vec(y), quiver=(vec(u), vec(v)), color=:red, lw=2)
end
arrowmap = generate_arrows(n)
putmap(arrowmap)