Tư duy Tính toán

Bài 11a: Vòng lặp while

Trường ĐH Công nghệ – Đại học Quốc gia Hà Nội

Nội dung

  1. Ôn nhanh: vòng lặp for & đệ quy
  2. Hôm nay: vòng lặp while (lặp không giới hạn)
  3. “Bốn câu hỏi” để thiết kế vòng lặp
  4. Lệnh break

Tài liệu đọc: Think Python (2nd ed.), 7.3 – 7.5

1.
Vòng lặp while
lặp đến khi điều kiện sai

Trò chơi đoán số

vì sao cần while?


> python game.py
What number am I thinking of?
          
  • Ban đầu: luôn trả lời 'wrong'.
  • Với if: biết đúng/sai, nhưng chỉ một lượt đoán.
  • Với for: cho hữu hạn lượt đoán (vd: 5).
  • Mục tiêu: cho đoán không giới hạn đến khi đúng → dùng while.

Ôn lại: một lượt đoán


# game.py
the_num = random.randint(1, 100)

def play():
   try:
      guess = input('What number am I thinking of? ')
      num = int(guess)
      if num != the_num:
         print('That is not correct.')
      else: #num == the_num
         print('Correct!')
   except ValueError:
      print('Sorry, you did not enter a number.')
          
  • Chỉ kiểm tra một lần, rồi kết thúc.
  • Có xử lý lỗi nhập liệu với ValueError.

Vòng for

số lượt đoán có giới hạn


# game.py
def play():
   still_playing = True
   for i in range(5): # 5 guesses
      if still_playing:
         try:
            guess = input('What number am I thinking of?')
            num = int(guess)
            if num != the_num:
               print('That is not correct.')
            else: #num == the_num
               print('Correct!')
               still_playing = False
         except ValueError:
            print('Sorry, you did not enter a number.')
          
  • Dùng biến cờ still_playing để quyết định còn chơi hay không.
  • Khởi tạo cờ (Q1: bắt đầu đúng) & đổi cờ khi đoán đúng.

Vòng while

số lượt đoán không giới hạn


# game.py
def play():
   still_playing = True
   while still_playing:
      try:
         guess = input('What number am I thinking of?')
         num = int(guess)
         if num != the_num:
            print('That is not correct.')
         else: #num == the_num
            print('Correct!')
            still_playing = False
      except ValueError:
         print('Sorry, you did not enter a number.')
          
  • Cờ still_playing cho biết có tiếp tục lặp hay không (điều kiện lặp).
  • Đổi cờ khi đạt mục tiêu → vòng lặp dừng đúng lúc.

Cú pháp vòng lặp while

Cú pháp


while <điều_kiện_lặp>:
   <các_câu_lệnh>
                

Ví dụ


while still_playing:
   <statements>
                
  • Kiểm tra <expr>True hay False.
  • Nếu False → dừng vòng lặp, chuyển sang lệnh không thụt lề tiếp theo.
  • Nếu True → chạy thân vòng lặp rồi quay lại kiểm tra.

Thuật ngữ cơ bản


while <điều_kiện_lặp>:
   <các_câu_lệnh>
          
  • Điều kiện lặp (loop condition / guard): <điều_kiện_lặp>
  • Thân vòng lặp (loop body): <các_câu_lệnh>
  • Mục tiêu thiết kế: điều kiện lặp phải tiến dần về False (trừ khi cố ý lặp vô hạn).

Luồng thực thi (flow)

  • Bước 1: kiểm tra điều_kiện_lặp?
  • Nếu True → thực thi body → quay lại bước 1.
  • Nếu False → thoát vòng lặp.
Kết quả điều_kiện_lặp Hành động
True Chạy body rồi kiểm tra lại
False Thoát vòng lặp

Vòng lặp vô hạn

  • Vòng lặp vô hạn là vòng lặp không bao giờ dừng.
  • Nhấn Ctrl + C để hủy chạy chương trình.

# loops.py
def infinite_loop():
   x = 0
   while True:
      print(x)
      x = x + 1
          

>>> import loops
>>> loops.infinite_loop()
0
1
2
...
^C
KeyboardInterrupt
          

Câu hỏi

chương trình sẽ in gì?


a = 8
b = 12
while a != b:
   if a > b:
      a = a - b
   else:
      b = b - a
print(a)
          

Chọn đáp án:

A: Không in gì: vòng lặp vô hạn   B: 8   C: 12   D: 4   E: 0

Thiết kế vòng lặp: “4 câu hỏi”

  • Q1. Bắt đầu đúng chưa? Khởi tạo các biến đúng giá trị.
  • Q2. Có giữ đúng quan hệ giữa các biến không? Mỗi vòng lặp có thể phải cập nhật biến để chúng luôn nhất quán.
  • Q3. Có tiến bộ không? Mỗi vòng lặp phải hướng tới việc làm điều kiện lặp trở thành False.
  • Q4. Dừng đúng chưa? Khi dừng, mục tiêu phải đã đạt.

* “Quan hệ” giữa các biến còn gọi là loop invariant.

Áp dụng “4 câu hỏi”

trò chơi đoán số


# game.py
def play():
   still_playing = True
   while still_playing:
      try:
         guess = input('What number am I thinking of?')
         num = int(guess)
         if num != the_num:
            print('That is not correct.')
         else: #num == the_num
            print('Correct!')
            still_playing = False
      except ValueError:
         print('Sorry, you did not enter a number.')
          
  • Q1: Bắt đầu đúng → still_playing = True.
  • Q2: Cập nhật quan hệ → đổi still_playing dựa trên numthe_num.
  • Q3: Có tiến bộ → mỗi lượt cho người chơi cơ hội thắng.
  • Q4: Dừng đúng → khi dừng thì num == the_num.

2.
Ví dụ vòng lặp while
mô phỏng • tài chính • Syracuse

Ví dụ 1

mô phỏng tung xúc xắc


# loops.py
def snake_eyes():
   """Roll a pair of dice until they both come up 1s."""
   snake_eyes = False
   num_tries = 0
   while not snake_eyes:
      r1 = roll()
      r2 = roll()
      print('Rolled: ' + str(r1) + ' and ' + str(r2))
      num_tries = num_tries + 1
      if r1 == 1 and r2 == 1:
         snake_eyes = True
   print('Snake eyes!')
   print('It took ' + str(num_tries) + ' tries.')
          
  • Khởi tạo biến tích lũy num_tries (Q1).
  • Dùng biến cờ snake_eyes để điều khiển dừng (Q2).
  • Có thể đọc while not như “cho tới khi”.
  • Báo cáo giá trị tích lũy khi đạt điều kiện (Q4).

Ví dụ 2

tăng trưởng khoản đầu tư


# loops.py
def print_growth(period, amount):
   """Print one line of table."""
   # ...
def double_investment(initial, rate_pct):
   """Print table of investment growth until doubled."""
   amount = initial
   period = 0
   print_growth(period, amount)
   while amount < 2*initial:
      amount = (1 + rate_pct/100) * amount
      period = period + 1
      print_growth(period, amount)
          
  • Không dùng biến cờ: kiểm tra trực tiếp điều kiện dừng amount < 2*initial.
  • Duy trì hai biến tích lũy: amountperiod.

Ví dụ 3

dãy Syracuse (Collatz)


# loops.py
def syr(start):
   """Returns the Syracuse sequence."""
   x = start
   lst = []
   while x != 1:
      lst.append(x)
      if x%2 == 0:
         x = x//2
      else:
         x = 3*x + 1
   lst.append(x)
   return lst
          
Định nghĩa Quy tắc
f(n) = 1 nếu n là 1
f(n) = n/2 nếu n chẵn
f(n) = 3n+1 nếu n lẻ
  • Tích lũy dãy trong lst.
  • Điều kiện dừng: x != 1 (thoát khi x == 1).

Dãy Syracuse

kiểm tra thiết kế vòng lặp


# loops.py
def syr(start):
   """Returns the Syracuse sequence."""
   x = start
   lst = []
   while x != 1:
      lst.append(x)
      if x%2 == 0:
         x = x//2
      else:
         x = 3*x + 1
   lst.append(x)
   return lst
          
  • Q1: bắt đầu đúng → x = start, lst = [].
  • Q2: quan hệ đúng → mỗi vòng lặp thêm x vào lst, rồi cập nhật x.
  • Q3: có tiến bộ → x thay đổi theo quy tắc chẵn/lẻ.
  • Q4: dừng đúng → khi kết thúc thì lst kết thúc bằng 1.

Khép vòng: đệ quy vs. lặp

  • Đệ quy: mẫu lập trình dùng hàm đệ quy.
  • Lặp (iteration): mẫu lập trình dùng vòng lặp.
  • Cả hai đều nhằm thực hiện lặp lại một công việc.
  • Đệ quy mạnh hơn lặp với for*; và tương đương về sức mạnh với while**.
  • Gợi mở: whilefor khác nhau thế nào về “sức mạnh”?

* Đệ quy có thể biểu diễn lặp không bị chặn trước số lần, còn for thường dựa vào dãy hữu hạn.
** while có thể mô phỏng các mẫu lặp tổng quát.

3.
Lệnh break
dừng vòng lặp sớm

Lệnh break trong vòng lặp

  • Trong thân vòng lặp, break làm dừng ngay lập tức vòng lặp hiện tại.
  • Cẩn thận: break dễ bị lạm dụng → chỉ dùng khi giúp mã rõ ràng hơn.
  • Lưu ý: nếu có vòng lặp lồng nhau, chỉ vòng lặp trong cùng (innermost) bị dừng.
Dùng biến cờ Dùng break

# loops.py
# get input from user and print
still_going = True
while still_going:
   x = input('? ')
   if x == 'quit':
      still_going = False
   else:
      print(x)
                  

# same behavior
while True:
   x = input('? ')
   if x == 'quit':
      break
   print(x)
                  

for có thể chuyển thành while

Vòng lặp for Tương đương bằng while

for x in iterable:
    <body>
                  

iterator = iter(iterable)
while True:
    try:
        x = next(iterator)
    except StopIteration:
        break
    <body>
                  
  • Mọi tính toán làm được với for đều làm được với while
  • Vì vậy, while mạnh hơn (tổng quát hơn) for.
  • Dù vậy, for vẫn rất hữu ích vì ngắn gọn và dễ đọc.