裏 RjpWiki

Julia ときどき R, Python によるコンピュータプログラム,コンピュータ・サイエンス,統計学

Julia の変数スコープ

2021年01月28日 | ブログラミング

わかりにくいかもしれないので,簡単な例を挙げておこう。

test99.jl という名前のファイルに以下の内容が入っているとする

for i = 1:10
    s = i == 1
    s = s + i
end
println(s)

コマンドラインで

$ julia test99.jl

とすると,

ERROR: LoadError: UndefVarError: s not defined
Stacktrace:
 [1] top-level scope at /Users/foo/Desktop/Julia/test99.jl:5
 [2] include(::Function, ::Module, ::String) at ./Base.jl:380
 [3] include(::Module, ::String) at ./Base.jl:368
 [4] exec_options(::Base.JLOptions) at ./client.jl:296
 [5] _start() at ./client.jl:506
in expression starting at /Users/foo/Desktop/Julia/test99.jl:5

となる。

REPL で,一番最初に

include("test99.jl")

を実行すると,同じエラーが出る(少し違うが)

LoadError: UndefVarError: s not defined
in expression starting at /Users/foo/Desktop/Julia/test99.jl:5

Stacktrace:
 [1] top-level scope at /Users/foo/Desktop/Julia/test99.jl:5
 [2] include(::String) at ./client.jl:457
 [3] top-level scope at In[2]:1
 [4] include_string(::Function, ::Module, ::String, ::String) at ./loading.jl:1091

この後例えば

s = 0

という行を実行させた後,再度

include("test99.jl")

を実行すると

0
┌ Warning: Assignment to `s` in soft scope is ambiguous because a global variable by the same name exists: `s` will be treated as a new local. Disambiguate by using `local s` to suppress this warning or `global s` to assign to the existing global variable.
└ @ nothing /Users/foo/Desktop/Julia/test99.jl:2

となる。

さて,普通のプログラミング言語だとどうなっていたっけ?と思い,まずは R でやってみる。

Julia でのプログラムを以下のように書き換えて test99.R に保存する。

for (i in 1:10) {
    s = i == 1
    s = s + i
}
print(s)

コマンドラインからこれを Rscript で実行すると

$ Rscript test99.R
[1] 10

なんのエラーもなく,期待される答え 10  を表示する。

R か RCommander で

source("test99.R")

としても,同じく 10 を表示する。エラーはない。

次に Python でやってみる。以下のプログラムを test99.py として保存する。

for i in range(1, 11):
    s = i == 1
    s = s + i
print(s)

コマンドラインからは

$ python3 test99.py
10

となるし,REPL でもエラーなく,同じ答え 10 が表示される。

 

我々としては,R や Python の振る舞いの方が普通に思われるが,確かに for ループの中で最初に s にアクセスするときは,「s なんかないよ」というのが理にかなっているのかも知れないなあ。

さて,悩んでいてもどうにもならないので,対策を考える。

for の内と外で s といっても違いがあるということで,

s = 999
for i = 1:10
    s = i == 1
    s = s + i
end
println(s)

としただけでは,まだ期待するような結果にはならない。

999
┌ Warning: Assignment to `s` in soft scope is ambiguous because a global variable by the same name exists: `s` will be treated as a new local. Disambiguate by using `local s` to suppress this warning or `global s` to assign to the existing global variable.
└ @ nothing /Users/aoki/Desktop/Julia/test99.jl:3

ところで,関数もまた変数スコープを持つので,このプログラムを関数定義でくるんでやる。

function func()
    s = 999
    for i = 1:10
        s = i == 1
        s = s + i
    end
    println(s)
end

これではじめて,for の内と外の s が同じものになる。その後で,関数を読んでやれば,期待する答え 10 が表示される。

s = 999初期化は不要だが定義が必要としたものだ。

func()
10

 

 

コメント
  • X
  • Facebookでシェアする
  • はてなブックマークに追加する
  • LINEでシェアする

Julia の変数スコープに注意

2021年01月28日 | ブログラミング

R の fisher.test() において,hybrid=true を選択した場合に使用される rcont2() を Julia に移植した。

Julia は,for ブロック,while ブロックが 新たな変数スコープを持つので,注意が必要な局面がある。

普通は,エラーメッセージで気づくのだが,REPL 環境でやっているとおかしなことになってしまい,ドツボに嵌まることもある。

この関数でも,3 箇所(3 変数)で注意が必要。初期化は必要ない(どのような数値を代入しても無関係)だが,そこでその変数を定義しないと「そんな変数ないよ」エラーになる。試行錯誤・テストのために PEPL で適当な値を設定してしまっていれば,このエラーが出なくなってします。そこで,ドツボにはまる(for や while の外に出たとたんに変数の値が代わってしまうという現象が起きる)。

もうひとつは,Julia らしい,面白いプログラミング技法

論理式 && 論理式が真の場合に実行される文

論理式 || 論理式が偽の場合に実行される文

if a < 5
     break
end

なんてのが,一行で書ける(わかりにくくなってしまうかもしれないが,慣れると便利)

using SpecialFunctions

function rcont2(nrowt, ncolt)
  #=============================================================================
  RCONT2 constructs a random two-way contingency table with given sums.
  Licensing:
    This code is distributed under the GNU LGPL license.
  Modified:
    28 Janualy 2021
  Author:
    Original FORTRAN77 version by WM Patefield.
  Reference:
    WM Patefield,
    Algorithm AS 159:
    An Efficient Method of Generating RXC Tables with
    Given Row and Column Totals,
    Applied Statistics,
    Volume 30, Number 1, 1981, pages 91-97.
  Parameters:
    Input, Array{Int64,1}nrowt(nrow), Array{Int64}ncolt(ncol),
      the row and column sum vectors.
    Output, Array{Int64,2}matrix(nrow, ncol), the matrix.
  =============================================================================#

  MAXTOT = 1000000
  nrow = length(nrowt)
  ncol = length(ncolt)
  ntotal = sum(ncolt)
  nrow > 1 || error("The length of the row sum vector must be greater than 1.")
  ncol > 1 || error("The length of the column sum vector must be greater than 1.")
  all(nrowt .> 0) || error("An entry in the row sum vector must be greater than 0.")
  any(ncolt .> 0) || error("An entry in the column sum vector must be greater than 0..")
  ntotal == sum(nrowt) || error("The row and column sum vectors must have the same sum.")
  MAXTOT >= ntotal || error("The sum of the column sum vector entries is too large.")

  # Calculate log-factorials.
  fact = logfactorial.(0:ntotal)
  # Construct a random matrix.
  matrix = zeros(Int64, nrow, ncol)
  jwork = zeros(Int64, ncol)
  jwork[1:ncol-1] = ncolt[1:ncol-1]
  jc = ntotal
  ib = 99999 # 初期化は不要だが,定義は必要
  for l = 1 : nrow - 1
    nrowtl = nrowt[l]
    ia = nrowtl
    ic = jc
    jc -= nrowtl
    for m = 1 : ncol - 1
      id = jwork[m]
      ie = ic
      ic -= id
      ib = ie - ia
      ii = ib - id
      # Test for zero entries in matrix.
      if ie == 0
        ia = 0
        matrix[l, m:ncol] = 0
        break
      end
      # Generate a pseudo-random number.
      r = rand(1)[1]
      # Compute the conditional expected value of MATRIX(L,M).
      done1 = false
      nlm = 99999 # 初期化は不要だが,定義は必要
      while true
        nlm = floor(Int64, ia * id / ie + 0.5)
        iap = ia + 1
        idp = id + 1
        igp = idp - nlm
        ihp = iap - nlm
        nlmp = nlm + 1
        iip = ii + nlmp
        x = exp(fact[iap] + fact[ib+1] + fact[ic+1] + fact[idp] -
          fact[ie+1] - fact[nlmp] - fact[igp] - fact[ihp] - fact[iip])
        r <= x && break
        sumprb = x
        y = x
        nll = nlm
        lsp = false
        lsm = false
        # Increment entry in row L, column M.
        done2 =false # 初期化は不要だが,定義は必要
        while ! lsp
          j = (id - nlm) * (ia - nlm)
          if j == 0
            lsp = true
          else
            nlm += 1
            x *= j / (nlm * (ii + nlm))
            sumprb = sumprb + x
            if r <= sumprb
              done1 = true
              break
            end
          end
          done2 = false
          while !lsm
            # Decrement the entry in row L, column M.
            j = nll * (ii + nll)
            if j == 0
              lsm = true
              break
            end
            nll -= 1
            y = y * j / ((id - nll) * (ia - nll))
            sumprb = sumprb + y
            if r <= sumprb
              nlm = nll
              done2 = true
              break
            end
            !lsp && break
          end
          done2 && break
        end
        (done1 || done2) && break
        r = sumprb * rand(1)[1]
      end
      matrix[l, m] = nlm
      ia -= nlm
      jwork[m] -= nlm
    end
    matrix[l, ncol] = ia
  end
  # Compute the last row.
  matrix[nrow, 1:ncol-1] = jwork[1:ncol-1]
  matrix[nrow, ncol] = ib - matrix[nrow, ncol-1]
  return matrix
end

# 使い方

rcont2([1,2,3], [2,4])

コメント
  • X
  • Facebookでシェアする
  • はてなブックマークに追加する
  • LINEでシェアする

PVアクセスランキング にほんブログ村

PVアクセスランキング にほんブログ村