見出し画像

京大Python教科書 平方根を求める (私の)もっさりしたコードを書き換えてみる、そしてエラーと闘う


前回のつづき。
というか、京大のPythonはどこいった感。

前回の「もっさりしたコード」を書き換えてみようかと思う。



もっさりしたコード

そもそも、その「もっさりしたコード」がどんなコードかというと、こんなコードである。

def root_digit(square, first, step):
    r = first
    for i in range(1, 10, 1):
        if square < ((r + step) * (r + step)):
            return r
        r += step

def root(square):
    r = 0
    step = 1
    for i in range(1, 10, 1):
        r = root_digit(square, r, step)
        step = step / 10
        print('{:.15f}'.format(r))
    return r

root2 = root(2)
print('{:.15f}'.format(root2))

もっさりしている。
そもそも変数多いし。


関数「root_digit」を再帰呼び出しにしてみる

まずは、最初の関数である「root_digit」を再帰呼び出しに書き換えてみる。
すると、こうなった。

def root_digit(square, r, step):
    if square < ((r + step) * (r + step)):
        return r

    root_digit(square, r + step, step)

def root(square):
    r = 0.0
    step = 1.0
    for i in range(1, 10, 1):
        r = root_digit(2, r, step)
        step = step / 10
        print('{:.15f}'.format(r))
    return r

root2 = root(2)
print('{:.15f}'.format(root2))

どこをどうしたかというと。

def root_digit(square, first, step):
    r = first
    for i in range(1, 10, 1):
        if square < ((r + step) * (r + step)):
            return r
        r += step
        root_digit(square, r + step, step)

取り消し線の部分を消し去って、
太字部分に置き換えたわけである。
鬱陶しかった変数「r」はなくなって、
さらにうざったい「range」もいらなくなった。

「for」文でクルクル回るのではなく、
「r」に「step」を足しながら自分を呼び出す

これを、2乗した値がsquareを超えるまで繰り返す

では、実行してみよう。


エラーになった

~/python/kyoto $ python root-11.py
Traceback (most recent call last):
  File "/data/data/com.termux/files/home/python/kyoto/root-11.py", line 16, in <module>
    root2 = root(2)
            ^^^^^^^
  File "/data/data/com.termux/files/home/python/kyoto/root-11.py", line 13, in root
    print('{:.15f}'.format(r))
          ^^^^^^^^^^^^^^^^^^^
TypeError: unsupported format string passed to NoneType.__format__
~/python/kyoto $

うっ。
またエラーかい。

def root(square):
    r = 0.0
    step = 1.0
    for i in range(1, 10, 1):
        r = root_digit(2, r, step)
        step = step / 10
        print('{:.15f}'.format(r))
    return r

この太字のところで怒られている。
なんと言って怒られているのかというと。

TypeError: unsupported format string passed to NoneType.__format__

また Type エラー。

「NoneType」は「__format__」をサポートしていません

NoteTypeならそうなるやろ。型がわからんかったら、どうやって文字列化してええのかわからん。それはわかる。問題は、

何故「NoneType」になっているのか

ということだ。そんなことをした覚えは、ない。

問題の「print('{:.15f}'.format(r))」というコードは、変数「r」を、15桁の小数で表示しようとしている。では「r」がどうなっているのかというと、次の太字のところで更新している。ちゃんと設定できてるやないの。なんで 「NoneType」になるのよ。

def root(square):
    r = 0.0
    step = 1.0
    for i in range(1, 10, 1):
        r = root_digit(2, r, step)
        step = step / 10
        print('{:.15f}'.format(r))
    return r

七転八倒したが、よくわからない。
「NoneType」でネット検索してみた。

あった。

戻り値(return)のない関数の実行結果は「値None, タイプNoneType」になる

なんと。
だがしかし、もっともか。
だとすると・・・。

あ。ここか。

def root_digit(square, r, step):
    if square < ((r + step) * (r + step)):
        return r

    root_digit(square, r + step, step)

この太字のところで「return」がないやん。


修正してみた

def root_digit(square, r, step):
    if square <= ((r + step) * (r + step)):
        return r

    return root_digit(square, r + step, step)

def root(square):
    r = 0.0
    step = 1.0
    for i in range(1, 10, 1):
        r = root_digit(2, r, step)
        step = step / 10
        print('{:.15f}'.format(r))
    return r

root2 = root(2)
print('{:.15f}'.format(root2))

そして、実行してみる。

~/python/kyoto $ python root-12.py
1.000000000000000
1.400000000000000
1.410000000000000
1.414000000000000
1.414200000000000
1.414210000000000
1.414213000000000
1.414213500000000
1.414213560000000
1.414213560000000
~/python/kyoto $

よしよし。


関数「root」も再帰呼び出しにしてみた

def root_digit(square, r, step):
    if square <= ((r + step) * (r + step)):
        return r

    return root_digit(square, r + step, step)

def root(square, r, step):
    r = root_digit(square, r, step)
    print('{:.15f}'.format(r))

    return root(square, r, step / 10)

root2 = root(2, 0.0, 1.0)
print('{:.15f}'.format(root2))

どこをどうしたかというのは、さっきと同じで

def root(square):
    r = 0.0
    step = 1.0
    for i in range(1, 10, 1):
        r = root_digit(2, r, step)
        step = step / 10
        print('{:.15f}'.format(r))
    return r
    return root(square, r, step / 10)

取り消し線の部分を消し去って、
太字部分に置き換えたわけである。
鬱陶しかった変数「r」も「step」もなくなって、
さらにうざったい「range」もいらなくなった。

「for」文でクルクル回るのではなく、
「step」を1桁ずつ落としながら自分を呼び出す

ということをしている。
今度はちゃんと「return」で値を返してるしね!


またエラーになった

そして。
またまたエラーになったんであった。

~/python/kyoto $ python root-13.py
1.000000000000000
1.400000000000000
1.410000000000000
1.414000000000000
1.414200000000000
1.414210000000000
1.414213000000000
1.414213500000000
1.414213560000000
1.414213562000000
1.414213562300000
1.414213562370000
1.414213562373000
1.414213562373000
1.414213562373090
1.414213562373094
Traceback (most recent call last):
  File "/data/data/com.termux/files/home/python/kyoto/root-13.py", line 13, in <module>
    root2 = root(2, 0.0, 1.0)
            ^^^^^^^^^^^^^^^^^
  File "/data/data/com.termux/files/home/python/kyoto/root-13.py", line 11, in root
    return root(square, r, step / 10)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/data/data/com.termux/files/home/python/kyoto/root-13.py", line 11, in root
    return root(square, r, step / 10)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/data/data/com.termux/files/home/python/kyoto/root-13.py", line 11, in root
    return root(square, r, step / 10)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^
  [Previous line repeated 13 more times]
  File "/data/data/com.termux/files/home/python/kyoto/root-13.py", line 8, in root
    r = root_digit(square, r, step)
        ^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/data/data/com.termux/files/home/python/kyoto/root-13.py", line 5, in root_digit
    return root_digit(square, r + step, step)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/data/data/com.termux/files/home/python/kyoto/root-13.py", line 5, in root_digit
    return root_digit(square, r + step, step)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/data/data/com.termux/files/home/python/kyoto/root-13.py", line 5, in root_digit
    return root_digit(square, r + step, step)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  [Previous line repeated 978 more times]
  File "/data/data/com.termux/files/home/python/kyoto/root-13.py", line 2, in root_digit
    if square <= ((r + step) * (r + step)):
       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
RecursionError: maximum recursion depth exceeded in comparison
~/python/kyoto $

長いんだけど、最後のエラーメッセージを読めば一目瞭然。

RecursionError: maximum recursion depth exceeded in comparison

最大再帰深度を超えました

その手前にこういうメッセージもあったりして。

[Previous line repeated 978 more times]

前の行が978回以上繰り返されました

978回とな。
ええっと。

def root(square, r, step):
    r = root_digit(square, r, step)
    print('{:.15f}'.format(r))
    return root(square, r, step / 10)

うーむ。
これじゃあ、再帰呼出に打ち止めがないやん。
無限再帰呼出になっとる。

再度修正。

def root(square, r, step, n):
    r = root_digit(square, r, step)
    print('{:.15f}'.format(r))
    if n <= 0:
        return r
    return root(square, r, step / 10, n-1)

この太字の部分を付け足したわけで。
最大の桁数を限定した。
コードはこんな感じ。

def root_digit(square, r, step):
    if square <= ((r + step) * (r + step)):
        return r

    return root_digit(square, r + step, step)

def root(square, r, step, n):
    r = root_digit(square, r, step)
    print('{:.15f}'.format(r))

    if n <= 0:
        return r

    return root(square, r, step / 10, n-1)

root2 = root(2, 0.0, 1.0, 10)
print('{:.15f}'.format(root2))

これでどう?

~/python/kyoto $ python root-16.py
1.000000000000000
1.400000000000000
1.410000000000000
1.414000000000000
1.414200000000000
1.414210000000000
1.414213000000000
1.414213500000000
1.414213560000000
1.414213562000000
1.414213562300000
1.414213562300000
time is 0.00 sec
~/python/kyoto $

OK、OK。

再帰呼出って意味嫌ってたんだけど、案外いいもんだな。関数型プログラミング言語って、もしかしてスタックとかも気にしなくてもいいようになってたりするんかな。


この記事が気に入ったらサポートをしてみませんか?