(2020年8月15日:Julia 1.5で、REPLではlocalスコープ内からglobal変数を参照できるようになりました。Julia 0.6以前の挙動に戻ったことになります)
Juliaの変数スコープはMatlabやC言語と少し違うので混乱した話。(2019年8月25日:global/localスコープの説明が公式ドキュメントと違ったので修正しました)
たとえば、1から10までの整数の和を求めたいとき、C言語では以下のようにやるかと思います。
#include <stdio.h> int main(int argc, char *argv[]) { int total = 0; int x; for (x=1; x<=10; x++) { total += x; } printf("Total: %d\n", total); return(0); }
total
をfor
文の内側から更新することができます。
あるいはPython 3のREPLでも以下のようになります。for
の内側から、外側にあるtotal
にアクセスできていることが分かります。
>>> total = 0 >>> for x in range(1, 11): ... total += x ... >>> total 55
同じことをJulia 1.1でやってみます。ここでは(立ち上げたばかりのREPLなど)トップレベルでの実行を想定しています。
julia> total = 0; julia> for x = 1:10 total += x; end ERROR: UndefVarError: total not defined Stacktrace: [1] top-level scope at ./REPL[2]:2 [inlined] [2] top-level scope at ./none:0
公式ドキュメントによると、for
文は新しいlocalスコープを作るため、内部(localスコープ)から外側(globalスコープ)の変数を見ることができないとのことです。回避するにはglobal
を使うらしく、
total = 0; for x = 1:10 global total += x; end println("Total: $(total)")
とすれば期待通りの動作になります。global
というキーワードを付けてあげれば上位にあるglobalスコープの変数にもアクセスできるのだな、ふむふむ、という感じです。
ただ、問題はここからで、二重のループにするとそういったことが起こりません。for y
の中から、その外側にあるはずのsubtotal
を更新できます。
for x = 1:10 subtotal = 0; for y = 1:10 subtotal += y; end end
これは(Local Scopesに書いてありますが)、localスコープ内に作られたlocalスコープからは上位の変数にアクセスできるからなのだそうです。外側のfor
がlocalスコープを作ったので、その内側のfor
文内部から見ると上位がlocalスコープなので、for y
からfor x
内の変数にアクセスできるのです。
同じものを関数にした場合はどうなるでしょうか。
function testsum() total = 0; for x = 1:10 total += x; end return(total); end
この場合は、function
が新しいlocalスコープを作るので、その内側に作られたlocalスコープのfor
から見て上位にあるtotal
にはアクセスできます。
global
はキモい(し、不具合の温床になる)のでできるだけ使わない方がよさそう、と考えると、Julia言語としては「できるだけ関数にしよう」という方向にユーザーを誘導していますのでるのではないかと感じます。そういえば、関数化した方が実行も速くなることが多いです。(追記:公式スタイルガイドに「スクリプトではなく関数にせよ」と書いてありました)
同じような操作であっても言語ごとに少しずつ違うので、細かいところで「どうなっていたっけ?」とドキュメントを参照する必要がありますが、こういうところから言語設計者の考え方に思いをはせるのもプログラミングの楽しいところです。