《05》制御構造(1)for 文〈Python 2.x 版〉

実録:はじめてのプログラミング記事一覧
《05》制御構造(1)for 文

《著》小粒ちゃん+α《監修》小泉ひよ子とタマゴ倶楽部
2009年1月20日(火)

今日の進捗

  • Python.use(better) -- セミナー研修テキスト
  • スケール課題を「続・ひよ子のきもち」で公開(続き)
Comment
本人:野中 OmniGraffleに慣れてないせいか思ったより作図に手間取ってます。
担当:本間 作図が必要なときは、下準備も含めて作業時間を見積もっておくといいです。Visioよりはずっと使いやすく、細かい作業をしたり資料を使い回したいときに便利で、OmniGraffleを手放せなくなるからね。(^^)

スケール課題(1)for 文

スケール課題の仕様を確認したので、これから、関数 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,
    print

>>> step1B(3)
0 1 2 

print 文の末尾にカンマ「,」を添えます。すると、改行する代わりに、空白文字「 」を出力します。つまり、変数 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 ".",
    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 でも解消されていません。《ひよ子》

Last updated♪09/03/02