Pythonの再帰関数の値
再帰関数を組んでいる際に、pythonでは関数外の値が関数内でどのように書き換わるのかわからなくなったため検証しました。
前提知識
関数内で関数外で定義された変数がどう取り扱われるかを確認します。
a=10
def replace1():
a=15
replace1()
print(a)
>10
この場合、関数内でのみaが変化します。
al=[10]
def replace3():
al=[15]
replace3()
print(al)
>[10]
この場合も、関数内でのみalが変化しています。
al=[10]
def replace2():
al[0]=15
replace2()
print(al)
>[15]
この場合は、関数内で変更された値が関数外でも変化しています。これはリストがミュータブルなオブジェクトのためです。
ローカル変数への代入しているか、グローバル変数を参照しているかの問題でした。
a=10
def replace1():
global a
a=15
replace1()
print(a)
>15
コメントにてglobal変数の定義を行うと代入可能と指摘いただきました。実際に関数内から関数外の変数に代入できています。クラス変数として定義する方法もあるようです。(コメント参照ください)
再帰関数にて変数の取り扱いの検証
ここからは再帰関数にて変数の取り扱いを試していきます。
ここで用いた再帰関数は3-1が0になるまで何回計算できるかカウントするものです。
ex1
count=0
a=3
def saiki1(a):
if(a==0): return
a=a-1
count += 1
saiki1(a)
print(a, count)
>UnboundLocalError: local variable 'count' referenced before assignment
念のためやってみましたが関数内でcountが定義されていないためエラーとなります。
関数内でglobal count
と定義するとエラーにはならずcountの正答が得られます
ex2
count=0
a=3
def saiki2(a):
count=0
if(a==0): return
a=a-1
count += 1
saiki2(a)
print(a, count)
>2 1
>1 1
>0 1
>3 0
この場合、関数内では変化していますが、関数外では最初に定義した値になっています。
なおこの場合は再帰内でcount=0とリセットしているため正しい答えとなっていません。
ex3
count=0
a=3
def saiki3(a, count):
if(a==0): return
a=a-1
count += 1
print(a, count)
saiki3(a,count)
saiki3(a, count)
print(a, count)
>2 1
>1 2
>0 3
>3 0
再帰関数の引数にcountを加えた場合、関数内では正しい答えとなっていますが、関数外ではそれを戻り値として受け取っていないため、最初に定義した値となっています。
ex4
count=0
a=3
def saiki4(a, count):
if(a==0): return a, count
a=a-1
count += 1
print(a, count)
saiki4(a, count)
return a, count
a, count= saiki4(a, count)
print(a, count)
>2 1
>1 2
>0 3
>2 1
次にaとcountを戻り値に設定した場合です。この場合は複雑ですが、8行目でsaiki4を実行した時点で戻り値を受け取っていないため、関数内で最初に計算した結果の2 1が出力されています。
ex5(正当)
count=0
a=3
def saiki5(a, count):
if(a==0): return a, count
a=a-1
count += 1
print(a, count)
a,count= saiki5(a, count)
return a, count
a, count= saiki5(a, count)
print(a, count)
>2 1
>1 2
>0 3
>0 3
この場合は関数内でa, countを受け取っているため正当を出力することができています。
なお8,9行目をまとめて return saiki5(a, count)
と書いても同じ結果が得られます。
ex6(正当)
count=[0]
a=3
def saiki6(a, count):
if(a==0): return a
a=a-1
count[0] += 1
print(a, count)
a = saiki6(a, count)
return a
a= saiki6(a, count)
print(a, count)
>2 [1]
>1 [2]
>0 [3]
>3 [3]
最後にcountをリストにして、その要素のみを書き換えた場合を確認します。この場合は countを戻り値としていませんが、関数外で定義した値が関数内で書き変わっています。
なおt= count[0]+1 count=[t]
などとするとリストのオブジェクトが変わってしまうので最後の結果は3 [0]
が出力されます。
追記
コメントにてグローバル変数で行う方法を教えていただきました。
参考