2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Julia勉強中:Juliaでループ時の変数スコープでハマった話

Last updated at Posted at 2020-12-28

Juliaが良さそうなので年末年始に少しでも知識を付けようと勉強中(1日目)。とりあえず書き方に慣れないと話にならないので、AtCoder過去問で練習していたら、初手で死亡したのでメモ。

Julia Version

$ julia --version
julia version 1.4.1

お題

前の値と次の値を比較して up とか down とか判定しろという問題。こんなの1分で解かないといけない。

え、その変数見えへんの?

とりあえず標準入力を1行1行取り出し、数値にして前の値と比較するという愚直なプログラム。**次の行を見る前に現在の値を前の値として記憶しておく。**問題の本質では無いので break 条件についてはとりあえず考慮しない。

using Printf

aprev = 0  # aprevを宣言

while true
    snext = readline()
    anext = parse(Int, snext)
    df = anext - aprev
    if df > 0
        @printf("up %d\n", df)
    elseif df < 0
        @printf("down %d\n", abs(df))
    else
        println("stay")
    end
    aprev = anext  # aprevに現在の値を記憶
end

別に変哲もないプログラムでしょう。でもこれコンパイルエラーになります。

$ julia 2019b.jl 
1
ERROR: LoadError: UndefVarError: aprev not defined
Stacktrace:
 [1] top-level scope at /home/morita/workspace/jlexp/2019b.jl:29
 [2] include(::Module, ::String) at ./Base.jl:377
 [3] exec_options(::Base.JLOptions) at ./client.jl:288
 [4] _start() at ./client.jl:484
in expression starting at /home/morita/workspace/jlexp/2019b.jl:26

は?aprev not definedて何でなの。

localスコープからglobal変数は見えません

多分スコープの問題だろうと調べものをしたらこのような記事が。https://marui.hatenablog.com/entry/2019/08/09/115410

どうも問題はトップレベルで書かれた行はglobal変数として扱われ、それはlocal変数から見えない、という事が落ちな模様。気持ち悪いのは、whileforなどのループは新しいlocalスコープを作るということ!公式サイトに具体例もちゃんと書かれていました。 https://docs.julialang.org/en/v1.1/manual/variables-and-scoping/#Local-Scope-1

公式の説明↓
https://docs.julialang.org/en/v1.4/manual/variables-and-scoping/#man-scope-table-1

There are two main types of scopes in Julia, global scope and local scope. The latter can be nested. The constructs introducing scope blocks are ... (while, forなど)

つまり、whileforなどは、localスコープのコンストラクタという事。また、localスコープはネストされるので、親forループ内で宣言されたlocal変数は、子forループ内でも参照できます。

解決方法

1つの方法は、forループの外の変数を global 宣言してあげる事です。

aprev = 0

while true
    global aprev # global宣言
    snext = readline()
    anext = parse(Int, snext)
    df = anext - aprev

    ...(snip)...
end

でも global 変数って何か気持ち悪いですよね。他の解決方法としては、ちゃんと関数として宣言してあげる事です。

using Printf

function f()
    aprev = 0
    
    while true
        snext = readline()
        anext = parse(Int, snext)
        df = anext - aprev
        if df > 0
            @printf("up %d\n", df)
        elseif df < 0
            @printf("down %d\n", abs(df))
        else
            println("stay")
        end
        aprev = anext
    end
end

f()

これで動きます。

まとめ

関数くらいはちゃんと書くか。

2
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?