算数テスト

算数テストプログラムは、以下のような足し算の問題を次々と出してきます。

6 + 7 =

それぞれの問に対して、ユーザーは答えをタイプします。タイプされた答えが不正解であっても次の問題に進みます。5 問正解すると終了し、それまでかかった時間が表示されます。

import sys
import time
import random

def main():
  maxnum = 10
  n = 0
  t = time.clock()
  
  while n < 5:
    n1 = random.randint(1, maxnum)
    n2 = random.randint(1, maxnum)
    ans = str(n1+n2)

    sys.stdout.write("%d + %d = " % (n1, n2))
    s = sys.stdin.readline()
    if not s:
      break
    if s[:-1] == ans:
      n += 1
    else:
      print "wrong."
  else:
    print "time: %.2f" % (time.clock()-t)
  

if __name__ == "__main__":
  main()
  
実行例:
> python arith.py
3 + 8 = 11
4 + 4 = 8
4 + 3 = 7
6 + 4 = 12
wrong.
1 + 3 = 4
3 + 9 = 12
time: 8.74

プログラムの解説

まず初めに、出題される問題の数の上限値を maxnum に代入しています。ここでは maxnum に 10 を代入していますので、数の上限値は 10 です。n にはユーザーが正解した数を格納します(最初は、正解数は 0 です)。問題を出題する前に t には開始時刻を記録しておきます。そして、終了時点の時刻から、その差を求めれば経過時間が分かります。

s = sys.stdin.readline()

という文で、ユーザーがタイプした答えを読み込みます。このとき s には改行文字が含まれています。ですから、

if s[:-1] == ans:

のところで、s[:-1] のようにして、改行を除いた文字列に対して ans と比較しています。

ユーザーが見事 5 問正解すると、while 文が終了します。このプログラムの while 文には else 節があります。

while n < 5:
  ...
else:
  print "time: %.2f" % (time.clock()-t)

これを次のように書き換えたとします。

while <cond>:
  <stmt1>
else:
  <stmt2>

else 節は、<cond> が偽を返した時に実行されます。もし、<stmt1> の部分で break が実行されてループが終了した場合には、else 節は実行されません。

>>> n = 0
>>> while n < 5:
...   n += 1
...   print n
... else:
...   print "end", n
...
1
2
3
4
5
end 5
>>> n = 0
>>> while n < 5:
...   n += 1
...   print n
...   if n == 3: break
... else:
...   print "end", n
...
1
2
3
>>>

算数テストプログラムの場合には、s が偽の時に break が実行されています。(Windows 環境であれば) Ctrl+Z が入力された時には、s が偽になります。このときには、break を実行してプログラムを途中で終了させています。break を実行した場合には、while 文の else 節は実行されませんので、経過時間は表示されないことになります。

算数テストプログラムの拡張

出題される問題は、全て足し算の問題のみですが、今度は引き算の問題も出題されるようにしてみましょう。

上のプログラムで変更が必要な箇所は、問題を表示する所と、ユーザーのタイプした数が正解かどうか確かめるために、2 つの数を演算している所です。

+ と - 演算子に対して、その文字列表現と、同じ演算処理を行う関数を要素に持つタプルを作成します。そしてそのタプルを要素とするタプルを作成し、そこからランダムにどちらの演算子を使用するかを選択するようにします。

def add(a, b):
  return a + b

def sub(a, b):
  return a - b

ops = (("+", add), ("-", sub))

ops の中からランダムにひとつ選択するには、random.choice() が利用できます。random.choice() はシーケンスの中からランダムな要素を返します。

>>> random.choice((1, 2, 3))
2
>>> random.choice((1, 2, 3, 4, 5))
4

プログラムを以下のように書き換えます。

import sys
import time
import random

def main():
  def add(a, b):
    return a + b

  def sub(a, b):
    return a - b
  
  maxnum = 10
  n = 0
  t = time.clock()
  ops = (("+", add), ("-", sub))
  
  while n < 5:
    op = random.choice(ops)
    n1 = random.randint(1, maxnum)
    n2 = random.randint(1, maxnum)
    ans = str(op[1](n1, n2))

    sys.stdout.write("%d %s %d = " % (n1, op[0], n2))
    s = sys.stdin.readline()
    if not s:
      break
    if s[:-1] == ans:
      n += 1
    else:
      print "wrong."
  else:
    print "time: %.2f" % (time.clock()-t)
  

if __name__ == "__main__":
  main()

実行結果を以下に示します。

> python arith.py
2 - 1 = 1
1 + 3 = 4
9 + 5 = 13
wrong.
6 - 3 = 3
7 - 10 = -3
4 - 5 = -1
time: 11.08

関数 add, sub は def で定義せずに無名関数として定義してもよいです。

ops = (("+", lambda a, b: a+b), ("-", lambda a, b: a-b))

Python ページ