見出し画像

書籍「独学プログラマー Python言語の基本から仕事のやり方まで」サンプルプログラムのバグ



【前書き】
「独学プログラマー」正誤表 に、この記事の内容と URL が追加されました。



【前書き終わり】



2021/07/04 追記:より正確に時刻判定をするバージョンのプログラムを追加しました。
2021/07/06 追記:最後に返した Unix time を覚えているクラスを追加して、改良したバージョンのプログラムを追加しました。


2021/07/06 追記:Python 3.8 以上で使える代入式を使って、簡潔に書いたバージョンのプログラムを追加しました。   

   


以前にツイートした内容ですが、思考トレースをしただけで実際にバグがあるのか確かめてなかったので、確かめてみました。

簡単なチェックルーチンを入れて確かめて見たところ、やはりバグがありました。

第21章 データ構造のチケット行列プログラムを見るときは気をつけてください。

以下にオリジナル版デバッグ版のプログラム(実行例付き)を示します。

オリジナル版

# http://tinyurl.com/jnw56zx

import time
import random

class Queue:
   def __init__(self):
       self.items = []
   
   def is_empty(self):
       return self.items == []
   
   def enqueue(self, item):
       self.items.insert(0, item)
       
   def dequeue(self):
       return self.items.pop()
       
   def size(self):
       return len(self.items)
       
def simulate_line(till_show, max_time):
   pq = Queue()
   tix_sold = []
   
   for i in range(100):
       pq.enqueue("person" + str(i))
       
   t_end = time.time() + till_show
   now = time.time()
   while now < t_end and not pq.is_empty():
       start = time.time()                         # 追加
       print('srart', start, 't_end', t_end)       # 追加
       if start > t_end:                           # 追加
           print('          Bug!! start > t_end')  # 追加
       now = time.time()
       r = random.randint(0, max_time)
       time.sleep(r)
       person = pq.dequeue()
       print(person)
       tix_sold.append(person)
           
   return tix_sold
       
sold = simulate_line(51)
print(sold)



'''

実行結果

srart 1624268054.324934 t_end 1624268059.324929
person0
srart 1624268054.3251135 t_end 1624268059.324929
person1
srart 1624268054.3251638 t_end 1624268059.324929
person2
srart 1624268055.3267157 t_end 1624268059.324929
person3
srart 1624268056.3302474 t_end 1624268059.324929
person4
srart 1624268056.3303168 t_end 1624268059.324929
person5
srart 1624268057.332369 t_end 1624268059.324929
person6
srart 1624268058.3338742 t_end 1624268059.324929
person7
srart 1624268059.3362257 t_end 1624268059.324929
         Bug!! start > t_end
person8
['person0', 'person1', 'person2', 'person3', 'person4', 'person5', 'person6', 'person7', 'person8']

'''



デバッグ版

# http://tinyurl.com/jnw56zx バグ取り版!

import time
import random

class Queue:
   def __init__(self):
       self.items = []
   
   def is_empty(self):
       return self.items == []
   
   def enqueue(self, item):
       self.items.insert(0, item)
       
   def dequeue(self):
       return self.items.pop()
       
   def size(self):
       return len(self.items)
       
def simulate_line(till_show, max_time):
   pq = Queue()
   tix_sold = []
   
   for i in range(100):
       pq.enqueue("person" + str(i))
       
   t_end = time.time() + till_show
   now = time.time()
   while now < t_end and not pq.is_empty():
       start = time.time()                         # 追加
       print('srart', start, 't_end', t_end)       # 追加
       if start > t_end:                           # 追加
           print('          Bug!! start > t_end')  # 追加
#        now = time.time()                          コメントアウトして移動
       r = random.randint(0, max_time)
       time.sleep(r)
       person = pq.dequeue()
       print(person)
       tix_sold.append(person)
       now = time.time()                           # ここが正しい
       
   return tix_sold
       
sold = simulate_line(51)
print(sold)



'''

実行結果

srart 1624269274.7694566 t_end 1624269279.7694538
person0
srart 1624269274.7695878 t_end 1624269279.7694538
person1
srart 1624269275.7710836 t_end 1624269279.7694538
person2
srart 1624269275.7711492 t_end 1624269279.7694538
person3
srart 1624269276.7726862 t_end 1624269279.7694538
person4
srart 1624269277.774291 t_end 1624269279.7694538
person5
srart 1624269277.774371 t_end 1624269279.7694538
person6
srart 1624269277.7744167 t_end 1624269279.7694538
person7
srart 1624269277.7744596 t_end 1624269279.7694538
person8
srart 1624269278.776227 t_end 1624269279.7694538
person9
['person0', 'person1', 'person2', 'person3', 'person4', 'person5', 'person6', 'person7', 'person8', 'person9']

'''


より正確に時刻判定をするバージョンのプログラム

# http://tinyurl.com/jnw56zx 自分ならこうするバージョン!!

import time
import random

class Queue:
   def __init__(self):
       self.items = []
   
   def is_empty(self):
       return self.items == []
   
   def enqueue(self, item):
       self.items.insert(0, item)
       
   def dequeue(self):
       return self.items.pop()
       
   def size(self):
       return len(self.items)
       
def simulate_line(till_show, max_time):
   pq = Queue()
   tix_sold = []
   
   for i in range(100):
       pq.enqueue("person" + str(i))
       
   t_end = time.time() + till_show
#    now = time.time()  コメントアウト now 変数は、そもそも不要! now を使うとタイムラグが生じる!!
   # 現在時刻取得と、その時刻を使った判定の間のタイムラグを取り除いた while処理!!
   while time.time() < t_end and not pq.is_empty():
       start = time.time()                         # 追加 厳密に言うと while と、この行の間にはタイムラグがある!
       print('srart', start, 't_end', t_end)       # 追加
       if start > t_end:                           # 追加
           print('          Bug!! start > t_end')  # 追加
#        now = time.time()  コメントアウト!
       r = random.randint(0, max_time)
       time.sleep(r)
       person = pq.dequeue()
       print(person)
       tix_sold.append(person)
           
   return tix_sold
       
sold = simulate_line(51)
print(sold)



'''

実行結果

srart 1625409701.672647 t_end 1625409706.6726441
person0
srart 1625409702.6742492 t_end 1625409706.6726441
person1
srart 1625409703.6760354 t_end 1625409706.6726441
person2
srart 1625409703.6761217 t_end 1625409706.6726441
person3
srart 1625409703.676187 t_end 1625409706.6726441
person4
srart 1625409704.677724 t_end 1625409706.6726441
person5
srart 1625409705.6791947 t_end 1625409706.6726441
person6
srart 1625409705.6792817 t_end 1625409706.6726441
person7
srart 1625409705.679381 t_end 1625409706.6726441
person8
srart 1625409705.679449 t_end 1625409706.6726441
person9
['person0', 'person1', 'person2', 'person3', 'person4', 'person5', 'person6', 'person7', 'person8', 'person9']

'''


最後に返した Unix time を覚えているクラスを追加して、改良したバージョンのプログラム

# http://tinyurl.com/jnw56zx 自分ならこうするバージョン2!!
   
import time
import random
   
# 最後に返した Unix time を覚えているクラスを追加!! $$新規$$
class nowtime:
   def __init__(self):
       self._puttime = 0.0
   
   def time(self):
       self._puttime =  time.time()
       return self._puttime
   
   def finalac(self):
       return self._puttime
   
class Queue:
   def __init__(self):
       self.items = []
   
   def is_empty(self):
       return self.items == []
   
   def enqueue(self, item):
       self.items.insert(0, item)
       
   def dequeue(self):
       return self.items.pop()
       
   def size(self):
       return len(self.items)
       
def simulate_line(till_show, max_time):
   pq = Queue()
   tix_sold = []
   
   for i in range(100):
       pq.enqueue("person" + str(i))
       
   now = nowtime()  # 追加! $$新規$$
   t_end = time.time() + till_show
#    now = time.time()  コメントアウト now 変数は、そもそも不要! now を使うとタイムラグが生じる!!
   # 現在時刻取得と、その時刻を使った判定の間のタイムラグを取り除いた while処理!!
   while now.time() < t_end and not pq.is_empty():  # now.time() がミソ。。。 $$新規$$
       print('srart time', now.finalac(), 'end time', t_end)  # 追加  now.finalac() がミソ。。。$$新規$$
       if now.finalac() > t_end:  # 追加  now.finalac() がミソ。。。$$新規$$
           print('~~~~~~~~~~ Bug!! start time > end time ~~~~~~~~~~')  # 追加
#        now = time.time()  コメントアウト!
       r = random.randint(0, max_time)
       time.sleep(r)
       person = pq.dequeue()
       print(person)
       tix_sold.append(person)
           
   return tix_sold
       
sold = simulate_line(51)
print(sold)
   
   
    
'''
実行結果
   
srart time 1625541679.8259597 end time 1625541684.8259568
person0
srart time 1625541680.8275962 end time 1625541684.8259568
person1
srart time 1625541681.8307064 end time 1625541684.8259568
person2
srart time 1625541682.8324656 end time 1625541684.8259568
person3
srart time 1625541683.834108 end time 1625541684.8259568
person4
['person0', 'person1', 'person2', 'person3', 'person4']
   
'''


Python 3.8 以上で使える代入式を使って、簡潔に書いたバージョンのプログラム

# http://tinyurl.com/jnw56zx 自分ならこうするバージョン3 (Python 3.8 以上じゃないと動かないよ)!!
   
import time
import random
   
class Queue:
   def __init__(self):
       self.items = []
   
   def is_empty(self):
       return self.items == []
   
   def enqueue(self, item):
       self.items.insert(0, item)
       
   def dequeue(self):
       return self.items.pop()
       
   def size(self):
       return len(self.items)
       
def simulate_line(till_show, max_time):
   pq = Queue()
   tix_sold = []
   
   for i in range(100):
       pq.enqueue("person" + str(i))
       
   t_end = time.time() + till_show
   # 現在時刻取得と、その時刻を使った判定の間のタイムラグを取り除いた 処理!!
   while (now := time.time()) < t_end and not pq.is_empty():  # チェックの為に現在時刻保持!代入式がミソだよ!!
       
       print('srart time', now, 'stop time', t_end)                     # チェック
       if now > t_end:                                                  # チェック
           print('~~~~~~~~~~ Bug!! start time > stop time ~~~~~~~~~~')  # チェック
       
       r = random.randint(0, max_time)
       time.sleep(r)
       person = pq.dequeue()
       print(person)
       tix_sold.append(person)
           
   return tix_sold
       
sold = simulate_line(51)
print(sold)
   
   
    
'''
実行結果
   
srart time 1625557856.3420396 stop time 1625557861.3420382
person0
srart time 1625557856.3433335 stop time 1625557861.3420382
person1
srart time 1625557857.3447928 stop time 1625557861.3420382
person2
srart time 1625557858.3462768 stop time 1625557861.3420382
person3
srart time 1625557858.3463492 stop time 1625557861.3420382
person4
srart time 1625557858.346422 stop time 1625557861.3420382
person5
srart time 1625557859.3479629 stop time 1625557861.3420382
person6
srart time 1625557860.3495026 stop time 1625557861.3420382
person7
['person0', 'person1', 'person2', 'person3', 'person4', 'person5', 'person6', 'person7']
   
'''