Python.use(better)
■ 概要
スケール課題の仕様を確認したので、これから、関数 scale が完成するまでの過程を紹介します。
《step 1》引数に指定した値と同じ数の文字 "." を出力します。
まず、step 1 の動作を確認します。
def step1(): for e in range(10): print(e, scale(e)) >>> step1() 0 1 . 2 .. 3 ... 4 .... 5 ..... 6 ...... 7 ....... 8 ........ 9 .........
すると、引数に指定した値と同じ数の "." を出力するのが分かります。ここで、for 文の流れを図解すると、次のようになります。
ある条件を満たす間は、for ブロック内にあるコードを繰り返し実行します。このとき、for に続く制御変数 e は、in に続くシーケンス range(10) の各要素を順に参照します。ステップ1の実行結果として、0 から 9 までの数が順に出力されたのは、そのためです。
さらに、ここに至るまでの過程を段階的に示します。まず、
def step1A(n): for e in range(n): print(e) >>> step1A(3) 0 1 2
0 から 3 までの数が順に出力されます。組み込み関数 print を使うと、変数 e の値を出力した後で「改行」されます。そこで、同じ行に出力させるには、
def step1B(n): for e in range(n): print(e, end=" ") print() >>> step1B(3) 0 1 2
関数 print にキーワード引数 end=" " を指定します。すると、改行する代わりに、空白文字「 」を出力します。つまり、変数 e の値を「空白」で区切りながら出力します。さらに、for ブロックの後に print() を続けると、最後に一度だけ改行します。そうしないと、同じ行にプロンプト「>>>」が表示されます。さらに、
>>> step1B(0) >>> print step1B(0) None
scale の引数に 0 を指定すると、何も出力されません。また、関数 print を使ってその値を確認すると、None が出力されます。これは、関数 scale が値を返さないからです。つまり、return 文を明示しない関数の「リターン値」は None と見なされ、これは、関数が値を返さないことを意味します。
何が問題か
次に進む前に、ここで少しコードに手を加えて、その動作を確認します。
def step1C(n): for e in range(n): print(".", end=" ") return(e) >>> print step1C(3) . . . 2
関数 print を使って(変数 e の代わりに)文字列 "." を出力します。また、return 文を使って、関数のリターン値 e を指定します。すると(None の代わりに)数値 2 が表示されます。
《Note》この結果から、for ブロックから抜けても、変数 e の値を参照できることが分かります。つまり、for ブロックの制御変数の有効範囲(スコープ)は、for ブロックではなく、関数ブロックとなります。□
しかし、このままでは別の問題が生じます。それは、文字列 "." に続いて空白文字が出力されることです。そこで新たな戦略を採用して、関数内で文字列を出力する代わりに、これを関数のリターン値として、それを出力するようにします。そこで、関数の本体で、print 文を使って値をひとつずつ出力するのではなく、その値を含む文字列を生成します。
def scale(n):
s = ""
for e in range(n):
s += "."
return s
変数 s の初期値には、空文字列 "" を設定します。ステップ1の実行結果として、引数 n の値が 0 のとき(空文字)何も出力されないのは、そのためです。次に、for 文を使って(指定された回数 n だけ)演算子 += を使って文字列 "." を連結します。すると、長さ n の文字列 s が得られるので、これをリターン値とします。すると、step1 で示した結果が得られます。
Tips
Python では、Smalltalk のように制御構造を自由自在に定義できません。そのため、コンパイラーに組み込みの制御構造の中からやり繰りをする必要があり、冗長な表現を余儀なくされる場面が少なくありません。たとえば、Smalltalk なら timesRepeat: で簡潔に表現できる場面でも、Python では冗長な(かつ「情報隠蔽の原則」に背く)for 文の制御変数が必要になります。そこで、書き手の意図を読み手に伝えたいときには、次のようにして、
def scale(n): s = "" for _ in range(n): s += "." return s
本来は必要のない(構文規則では必要でも)変数 _ であることを強調したコードを記述することがあります(VDM++ の事例が参考になります)。未熟な言語仕様のツケを清算するのがプログラマーの役割という悲しい状況は、残念ながら Python3 でも解消されていません。《ひよ子》
↑TOP