<Lua> グローバル変数とローカル変数

Last modified: 2019-07-21

ここではLuaにおけるグローバル変数とローカル変数についてとそのコード例を扱う。

解説

グローバル変数

[変数名] = [値]

の形のコードを記述すると、その名前の変数が初めて出てきた場合に新しく変数が作られてその値が初期値となる。この際に変数の内容はプログラム内のどこからでも読み書きでき、グローバル変数と呼ばれる。

既に作られているグローバル変数に対して上の形のコードを記述した場合にはその変数の値が書き換えられる。

グローバル変数はその性質からプログラムを分かりにくくするなどのデメリットもあるので、必要以上に多用せず、かつ適切な用途で用いる。

ブロックとローカル変数

doendで囲まれた部分はブロックと呼ばれる。これを単純に付けただけでは特に処理が分岐したりループしたりといった変化はないが、この中で

local [変数名] (= [値])

の形のコードを記述するとendまでの間だけ有効な変数が作られる(endに達すると寿命となって、その後読み書きできなくなる)。

変数名の後ろに= [値]部分が無ければ、内容は(代入操作を行うまでは)変数が未定義状態に参照しようとした場合と同様にnilという特殊な値となる。

また、自分で定義した関数の中でlocalを記述して作られた変数は関数の中だけで参照できる(関数の定義については別の記事で扱う)。こうした変数はローカル変数と呼ばれる。

一度ローカル変数として作られた変数は

[(ローカル変数として作られた)変数名] = [値]

の形で値を入れることができる(グローバル変数にはならない)。

ブロックの中には更に内側のブロックが記述できるが、内側のブロックの中で作られたローカル変数は外側のブロックからは参照できず、外側のブロックの中で作られたローカル変数は内側のブロックからも参照できる

do
  local a
  do
    local b
    -- ここではaもbも参照可
  end
  -- ここではaのみ参照可
end
-- aもbも参照不可

変数の名前が衝突した場合の優先順位

変数名が衝突(同一の変数が複数存在)した場合、参照の優先順位は最も内側のブロックのローカル変数が一番高く、グローバルな変数が一番低い。

同一の変数名の

  • グローバル変数
  • 複数段階のブロックのローカル変数

がある場合、一番内側のブロックの中で変数を参照すると一番内側のブロックの中のローカル変数が用いられる。

下の3つのaは名前が同じだが内部的には別のものとして扱われる。

a = 1
do
  local a = 2
  do
    local a = 3
    -- ここでは "a" は最も内側の "3"
  end
  -- ここでは "a" は "2"
end
-- ここでは "a" は "1"

また、複数の関数の中にそれぞれ同名のローカル変数がある場合も別のものとして扱われる。

コード例

下のコードは、グローバル変数とローカル変数の挙動を確かめる目的によって、プログラム内でdoendが記述されているところや代入の操作を行う場面でそれを示すためにそのコード自体を一緒に出力していたり、プログラムの構造に合わせて変数値表示の場面で字下げしていたりする。

[任意]ファイル名:003_global_and_local.lua エンコーディング:UTF-8

#! /usr/bin/lua
-- -*- coding: utf-8 -*-

--[[--  --  --  --  --  --  --  --  --  --  --  --  --  --  --  --  --  --  --
動作確認バージョン: 5.3, JIT2.1
--  --  --  --  --  --  --  --  --  --  --  --  --  --  --  --  --  --  --  --]]

-----
----- グローバル変数とローカル変数
-----

-- 初めて使用した名前に対して「local」を付けないとグローバル変数になり
-- どこからでも読み書きできる
print('> a = 10')
a = 10
print(('  (aの値は %d です)'):format(a))

-- 「do」から「end」まではブロックで
-- この間にlocal文を未使用の名前に実行した変数はローカル変数となり
-- 作られたブロックが終わると寿命になって消える
-- 関数の中やループ内のブロックでも同様
print('> do')
do
    -- このブロック内でのみ有効な変数xを作成
    print('>   local x')
    local x  -- この時点では取り出しても「nil」

    -- local文を実行した名前の変数への書き込み
    print('>   x = "これはローカル変数x"')
    x = 'これはローカル変数x'

    -- xの内容を確認
    print(('    (xの内容は "%s" です)'):format(x))

    -- グローバル変数はここからでも参照できる
    print(('    (aの値は %d です)'):format(a))

    -- local文で初期値を入れる形
    print('>   local a = 20')
    local a = 20

    -- aの内容を確認
    print(('    (aの値は %d です)'):format(a))

    print('> end')
    -- ここで「これはローカル変数x」のxは消える
end

-- 外側でxを参照しようとしても「nil」になるだけ
print(('  (xの内容は "%s" です)'):format(x))

print('> do')
do
    -- ここでもxは作成/代入されていないので「nil」になるだけ
    print(('    (xの内容は "%s" です)'):format(x))

    print('>   local x = "別のブロックにあるローカル変数x"')
    local x = '別のブロックにあるローカル変数x'
    print(('    (aの値は %d です)'):format(a))
    print('>   local a = 30')
    local a = 30
    print(('    (aの値は %d です)'):format(a))

    -- ブロックの中にブロックがある場合、その中でローカル変数を宣言すると
    -- 内側のブロック内のみで参照できる
    print('>   do')
    do
        -- このブロック内ではまだローカルなxは作られていないため
        -- 1つ外側のブロックのxが用いられる
        print(('      (xの内容は "%s" です)'):format(x))

        -- 外側のブロックに同名のローカル変数がある場合は
        -- 内側のブロックのものが優先される
        print('>     local a = 40')
        local a = 40
        print(('      (aの値は %d です)'):format(a))

        print('>   end')
    end
    -- ここでは「別のブロックに...」のxが生きているのでその内容が表示される
    print(('    (xの内容は "%s" です)'):format(x))
    -- 内側のブロックで「40」を入れたaとは別のaなので、以前の「30」のまま
    print(('    (aの値は %d です)'):format(a))

    print('> end')
    -- ここでは「別のブロックに...」のxは消える
end

-- ここも「nil」になるだけ
print(('  (xの内容は "%s" です)'):format(x))

-- 終了

下は実行例。

$ lua [003_global_and_local.luaの場所]
> a = 10
  (aの値は 10 です)
> do
>   local x
>   x = "これはローカル変数x"
    (xの内容は "これはローカル変数x" です)
    (aの値は 10 です)
>   local a = 20
    (aの値は 20 です)
> end
  (xの内容は "nil" です)
> do
    (xの内容は "nil" です)
>   local x = "別のブロックにあるローカル変数x"
    (aの値は 10 です)
>   local a = 30
    (aの値は 30 です)
>   do
      (xの内容は "別のブロックにあるローカル変数x" です)
>     local a = 40
      (aの値は 40 です)
>   end
    (xの内容は "別のブロックにあるローカル変数x" です)
    (aの値は 30 です)
> end
  (xの内容は "nil" です)
  • 使用したバージョン
    • Lua 5.3.3
    • LuaJIT 2.1.0 Beta 3