Author Archives: Phở Code

Python – Kiểu String

Trong bài các kiểu dữ liệu cơ bản chúng ta đã học về kiểu string, trong bài này chúng ta sẽ tìm hiểu sâu hơn về kiểu dữ liệu này. String là một trong những kiểu dữ liệu quan trọng trong các ngôn ngữ lập trình, vì thế nên mình quyết định dành hẳn một bài để nói về string.

Khởi tạo string

Trong Python, để tạo một string, chúng ta có thể dùng một dấu nháy đơn, một dấu nháy kép hoặc ba dấu nháy kép. Khi dùng ba dấu nháy kép, chúng ta có thể viết string trên nhiều dòng mà không cần dùng ký tự thoát.


a = "proximity alert"
b = 'evacuation'
c = """
requiem 
for
a 
tower
"""

print (a)
print (b)
print (c)

Trong ví dụ trên, chúng ta khởi tạo 3 string và in ra màn hình.

proximity alert
evacuation

requiem 
for 
a 
tower

Dùng dấu nháy bên strong string

Để tạo string thì chúng ta dùng các dấu nháy, nhưng nếu chúng ta muốn dùng chính các dấu nháy đó bên trong string thì sao? Có hai cách, một là dùng kí tự thoát, hai là trộn lẫn giữa dấu nháy cần dùng với dấu nháy khởi tạo string.


print ("There are many stars.")
print ("He said, \"Which one is your favourite?\"")

print ('There are many stars.')
print ('He said, "Which one is your favourite?"')

Cách thứ nhất là dùng kí tự thoát \ các kí tự điều khiển sẽ bị vô hiệu hóa và sẽ được in ra như các kí tự bình thường. Cách thứ hai là chúng ta trộn, nếu muốn sử dụng dấu nháy kép trong string thì chúng ta dùng dấu nháy đơn để bao bọc lấy string và ngược lại.

There are many stars.
He said, "Which one is your favourite?"
There are many stars.
He said, "Which one is your favourite?"

Độ dài của string

Phương thức len() trả về số lượng chữ cái trong string, kể cả dấu cách.


s1 = "Eagle"
s2 = "Eagle\n"
s3 = "Eagle  "

print (len(s1))
print (len(s2))
print (len(s3))

Ví dụ trên in ra độ dài của 3 string.

s1 = "Eagle"
s2 = "Eagle\n"
s3 = "Eagle  "

Ở đây, string thứ 2 có kí tự xuống dòng, string thứ 3 có 2 dấu cách và chúng đều được tính.

5
6
7

Loại bỏ khoảng trắng thừa trong string

Đôi khi các khoảng trắng thừa ở đầu string hay cuối string nhìn khá là khó chịu và bạn muốn loại bỏ chúng đi. Python cung cấp các phương thức strip(), rstrip() và lstrip() để làm công việc này. Bản thân các phương thức này không loại bỏ các kí tự trong string gốc mà chúng sẽ copy string gốc sang một nơi khác, sau đó loại bỏ khoảng trắng ở các string này rồi trả string mới này cho chúng ta.


s = " Eagle  "

s2 = s.rstrip()
s3 = s.lstrip()
s4 = s.strip()

print (s, len(s))
print (s2, len(s2))
print (s3, len(s3))
print (s4, len(s4))

Ở ví dụ trên, chúng ta áp dụng cả 3 phương thức trên vào string s. String có 1 khoảng trắng thừa ở đâu câu và 2 khoảng trắng thừa ở cuối câu.

s2 = s.rstrip()

Phương thức rstrip() loại bỏ khoảng trắng thừa ở cuối string.

s3 = s.lstrip()

Phương thức lstrip() loại bỏ khoảng trắng thừa ở đầu string.

s4 = s.strip()

Phương thức strip() loại bỏ khoảng trắng thừa ở cả đầu và cuối string.

 Eagle   8
 Eagle 6
Eagle   7
Eagle 5

Chúng ta in kết quả ra màn hình. Lưu ý là python tự động cách dòng các thành phần trong hàm print.

Các ký tự thoát

Đây là các kí tự đặc biệt dùng để điều khiển string.


print ("Incompatible, it don't matter though\n'cos someone's bound to hear my cry")
print ("Speak out if you do\nYou're not easy to find")

Kí tự \n mang ý nghĩa xuống dòng, kí tự nào đứng đằng sau kí tự này sẽ tự động xuống dòng.

Incompatible, it don't matter though
'cos someone's bound to hear my cry
Speak out if you do
You're not easy to find

Tiếp theo là kí tự backspace \b.

print ("Python\b\b\booo") # prints Pytooo

Kí tự này đưa con trỏ về trước 1 kí tự. Trong ví dụ trên, chúng ta có 3 kí tự \b nên con trỏ được đưa lùi về 3 kí tự nên 3 kí tự hon được thay thế bằng 3 kí tự ooo.

print ("Towering\tinferno") # prints Towering        inferno

Kí tự \t có tác dụng in ra một dấu tab.

Nếu chúng ta đưa r vào trước dấu nháy. Các ký tự thoát trong string sẽ không được sử dụng và chúng sẽ được in ra như các kí tự bình thường.

print (r"Another world\n")
Another world\n

Ngoài ra Python còn nhiều kiểu kí tự thoát khác nữa, bạn có thể tham khảo tại đây.

So sánh string

Chúng ta có thể so sánh 2 string bằng các toán tử ==, !=… và kết quả trả về là một giá trị boolean.

print ("12" == "12")
print ("17" == "9")
print ("aa" == "ab")

print ("abc" != "bce")
print ("efg" != "efg")

Trong ví dụ trên, chúng ta so sánh 2 string.

print ("12" == "12")

Hai string trên bằng nhau, kết quả là True.

print ("aa" == "ab")

Hai string trên khác nhau, hai kí tự đầu tiên bằng nhau nhưng kí tự thứ hai khác nhau, trong đó kí tự a lớn hơn kí tự b. Kết quả trả về là False.

print ("abc" != "bce")

Hai string này khác nhau hoàn toàn. Kết quả trả về là True.

True
False
False
True
False

Truy xuất phần tử trong string

Cũng như tuple, list… chúng ta có thể truy xuất các phần tử trong string.


s = "Eagle"

print (s[0])
print (s[4])
print (s[-1])
print (s[-2])

print ("****************")

print (s[0:4])
print (s[1:3])
print (s[:])

Ở trên chúng ta truy xuất thông qua chỉ số.

print (s[0])
print (s[4])

Chỉ số ở đây được đánh số từ 0, nếu string có 5 kí tự thì chúng được đánh số từ 0..4.

print (s[-1])
print (s[-2])

Nếu chúng ta đưa vào chỉ số là số âm, chúng ta lấy được các phần tử từ cuối string.

print (s[0:4])

Chúng ta có thể đưa các phạm vi lấy từ đâu đến đâu vào để lấy một tập các phần tử chứ không chỉ lấy được các phần tử riêng biệt.

print (s[:])

Nếu chúng ta đưa vào chỉ số như phía trên thì sẽ lấy toàn bộ string.

E
e
e
l
****************
Eagl
ag
Eagle

Chúng ta có thể dùng vòng lặp để duyệt qua string.

s = "Pho Code"

for i in s:
  print (i,)

Đoạn code trên in toàn bộ các phần tử trong string ra màn hình.

P h o C o d e

Tìm string con

Các phương thức find(), rfind(), index() và rindex() là các phương thức dùng để tìm string con. Kết quả trả về là vị trí của kí tự đầu tiên của string con được tìm thấy trong string cha. Phương thức find() và index() tìm từ đầu string, phương thức rfind() rindex() tìm từ cuối string.

Sự khác nhau của 2 phương thức find() và index() là nếu không tìm thấy string con thì phương thức đầu tiên trả về -1, còn phương thức thứ 2 trả về một ValueError exception. Exception là một khái niệm quan trọng và chúng ta sẽ tìm hiểu ở các bài cuối.

find(str, beg=0, end=len(string))
rfind(str, beg=0, end=len(string))
index(str, beg=0, end=len(string))
rindex(str, beg=0, end=len(string))

Trong đoạn code trên, các phương thức này nhận vào 3 tham số, tham số đầu tiên str là string con cần tìm, tham số thứ 2 và thứ 3 là vị trí bắt đầu tìm và vị trí kết thúc để tìm trong string cha. Chúng ta có thể không đưa 2 tham số này vào và các phương thức trên sẽ mặc định tìm từ vị trí đầu tiên là 0 và vị trí cuối cùng là độ dài của string cha.

a = "I saw a wolf in the forest. A lone wolf."

print (a.find("wolf"))
print (a.find("wolf", 10, 20))
print (a.find("wolf", 15))

print (a.rfind("wolf"))

Trong ví dụ trên, chúng ta có một string và chúng ta thử tìm string con từ các khoảng vị trí khác nhau.

print (a.find("wolf"))

Dòng trên tìm vị trí xuất hiện đầu tiên của string “wolf”.

print (a.find("wolf", 10, 20))

Dòng trên tìm vị trí xuất hiện đầu tiên của string “wolf” từ vị trí 10 đến 20 trong string gốc, kết quả trả về -1 vì không tìm thấy.

print (a.find("wolf", 15))

Dòng trên chúng ta cũng tìm string “wolf” từ vị trí 15 đến cuối string và tìm ra vị trí thứ hai mà string này xuất hiện trong string cha.

print (a.rfind("wolf"))

Phương thức rfind() tìm string gốc từ cuối string cha, do đó cũng tìm ra vị trí thứ hai của string này trong string cha.

8
-1
35
35

Tiếp theo chúng ta sẽ tìm hiểu về phương thức index().

a = "I saw a wolf in the forest. A lone wolf."

print (a.index("wolf"))
print (a.rindex("wolf"))

try:
    print (a.rindex("fox"))
except ValueError as e:
    print ("Could not find substring")

Như đã nói ở trên, điểm khác nhau của phương thức này với phương thức find() là cách chúng gửi giá trị trả về khi không tìm thấy.

print (a.index("wolf"))
print (a.rindex("wolf"))

Hai dòng trên tìm vị trí bắt đầu của string “wolf” từ vị trí đầu và cuối string cha.

try:
    print (a.rindex("fox"))
except ValueError as e:
    print ("Could not find substring")

Ở đây chúng ta thử tìm string “fox” và tất nhiên là không tìm thấy, phương thức này trả về một ValueError exception.

8
35
Could not find substring

Toán tử trên string

Ví dụ dưới đây sử dụng toán tử nhân * và toán tử nối chuỗi.


print ("eagle " * 5)

print ("eagle " "falcon")

print ("eagle " + "and " + "falcon")

Toán tử * sẽ lặp lại string n lần. Trong ví dụ trên là 5 lần. Hai string được đặt sát nhau sẽ tự động  ngầm nối vào nhau. Bạn cũng có thể dùng dấu + để nối chuỗi một cách rõ ràng.

eagle eagle eagle eagle eagle 
eagle falcon
eagle and falcon

Phương thức len() có tác dụng tính số chữ cái trong string.


var = 'eagle'

print (var, "has", len(var), "characters")

Ví dụ trên tính số lượng các kí tự có trong string.

eagle has 5 characters

Trong một số ngôn ngữ khác như Java, C#, kiểu string và kiểu số có thể được ngầm nối thành một string, chẳng hạn trong Java, bạn có thể gõ System.out.println("Number: " + 12) để in chuỗi “Number: 12” ra màn hình. Python không cho phép điều này, bạn phải chuyển đổi kiểu dữ liệu một cách rõ ràng.


print (int("12") + 12)
print ("There are " + str(22) + " oranges.")
print (float('22.33') + 22.55)

Chúng ta có thể dùng phương thức int() để chuyển một string thành một số, float() để chuyển một string thành một số thực, hoặc hàm str() để chuyển một số thành một string,

Thay thế string

Phương thức replace() sẽ thay thế một chuỗi con trong chuỗi cha thành một chuỗi mới.

replace(old, new [, max])

Mặc định thì phương thức này sẽ thay thế tất cả các chuỗi con mà nó tìm thấy được. Nhưng bạn cũng có thể giới hạn phạm vi tìm kiếm.

a = "I saw a wolf in the forest. A lonely wolf."

b = a.replace("wolf", "fox")
print (b)

c = a.replace("wolf", "fox", 1)
print (c)

Ở ví dụ trên chúng ta thay thế string “wolf” thành “fox”.

b = a.replace("wolf", "fox")

Dòng trên sẽ thay thế tất cả các string “wolf” thành “fox”.

c = a.replace("wolf", "fox", 1)

Dòng này thì chỉ thay thế string “wolf” đầu tiên mà nó tìm thấy.

I saw a fox in the forest. A lonely fox.
I saw a fox in the forest. A lonely wolf.

Tách, nối string

Chúng ta có thể tách một string bằng phương thức split() hoặc rsplit(). Kết quả trả về là một đối tượng kiểu list có các phần tử là các kí tự được tách ra từ string gốc, string gốc được tách dựa trên các kí tự phân cách mà chúng ta cung cấp. Ngoài ra 2 phương thức trên còn có 1 tham số thứ 2 là số lượng lần tách tối đa.

nums = "1,5,6,8,2,3,1,9"

k = nums.split(",")
print (k)

l = nums.split(",", 5)
print (l)

m = nums.rsplit(",", 3)
print (m)

Trong ví dụ trên, chúng ta sử dụng kí tự phân cách là dấu phẩy “,” để tách string thành các string con.

k = nums.split(",")

String gốc của chúng ta có 8 kí tự, nên list trả về sẽ có 8 phần tử.

l = nums.split(",", 5)

Dòng code trên quy định số lần tách tối đa là 5 lần, do đó list trả về của chúng ta sẽ có 6 phần tử.

m = nums.rsplit(",", 3)

Ở đây chúng ta tách string gốc thành 4 phần tử nhưng quá trình tách bắt đầu từ cuối string.

['1', '5', '6', '8', '2', '3', '1', '9']
['1', '5', '6', '8', '2', '3,1,9']
['1,5,6,8,2', '3', '1', '9']

Chúng ta có thể nối các string bằng hàm join(). Các string được nối lại cũng có thể có các kí tự phân tách.

nums = "1,5,6,8,2,3,1,9"

n = nums.split(",")
print (n)

m = ':'.join(n)
print (m)

Trong ví dụ trên chúng ta tách string nums ra dựa trên dấu phẩy “,”, sau đó nối các phần tử mới trong list thành một string và cách nhau bởi dấu hai chấm “:”.

m = ':'.join(n)

Phương thức join() sẽ tạo ra một string mới từ một list các string con, cách nhau bởi dấu “:”.

['1', '5', '6', '8', '2', '3', '1', '9']
1:5:6:8:2:3:1:9

Ngoài phương thức split() dùng để tách chuỗi, chúng ta có một phương thức khác có chức năng tương tự là partition(), phương thức này chỉ cắt chuỗi 1 lần khi nó gặp kí tự phân cách, do đó nó cũng giống như là khi dùng split(str, 1) vậy. Thế nên kết quả trả về của phương thức này luôn là một list chứa 3 phần tử, phần tử đầu tiên là đoạn string nằm phía trước kí tự phân tách, phần tử thứ 2 là chính bản thân kí tự phân tách, phần tử thứ 3 là đoạn string nằm phía sau kí tự phân tách.

s = "1 + 2 + 3 = 6"

a = s.partition("=")

print (a)

Đoạn code trên ví dụ về phương thức partition().

a = s.partition("=")
('1 + 2 + 3 ', '=', ' 6')

Chữ HOA và chữ thường

Python có các phương thức để làm việc với từng trường hợp.


a = "PhoCode"

print (a.upper())
print (a.lower())
print (a.swapcase())
print (a.title()) 

Ví dụ trên sử dụng bốn phương thức.

print (a.upper())

Phương thức upper() sẽ chuyển toàn bộ các kí tự trong string thành viết HOA.

print (a.lower())

Phương thức lower() sẽ chuyển toàn bộ các kí tự trong string thành viết thường.

print (a.swapcase())

Phương thức swapcase() đảo ngược kiểu HOA thành kiểu thường và ngược lại.

print (a.title())

Phương thức title() sẽ viết HOA chữ cái đầu tiên, các chữ cái còn lại sẽ chuyển thành kiểu thường.

PHOCODE
phocode
pHOcODE
Phocode

Một số thao tác khác trên string


sentence = "There are 22 apples"

alphas = 0
digits = 0
spaces = 0

for i in sentence:
   if i.isalpha():
      alphas += 1
   if i.isdigit():
      digits += 1
   if i.isspace():
      spaces += 1

print ("There are", len(sentence), "characters")
print ("There are", alphas, "alphabetic characters")
print ("There are", digits, "digits")
print ("There are", spaces, "spaces")

Ở đây chúng ta sử dụng các phương thức isalpha(), isdigit(), isspace() để xác định xem kí tự nào là chữ cái, chữ số hay dấu cách.

There are 19 characters
There are 14 alphabetic characters
There are 2 digits
There are 3 spaces

Ví dụ dưới đây chúng ta sẽ căn chỉnh lề khi in string ra màn hình.


print ("Ajax Amsterdam" " - " + "Inter Milano " "2:3")
print ("Real Madridi" " - " "AC Milano " "3:3")
print ("Dortmund" " - " "Sparta Praha " "2:1")

Bạn đã biết là chúng ta có thể nối chuỗi bằng cách đặt hai chuỗi sát nhau hoặc dùng toán tử +.

Ajax Amsterdam - Inter Milano 2:3
Real Madridi - AC Milano 3:3
Dortmund - Sparta Praha 2:1

Chúng ta sẽ căn chỉnh lề cho đẹp hơn.


teams = { 
      0: ("Ajax Amsterdam", "Inter Milano"),
      1: ("Real Madrid", "AC Milano"),
      2: ("Dortmund", "Sparta Praha")
}

results = ("2:3", "3:3", "2:1")


for i in teams:
   print (teams[i][0].ljust(16) + "-".ljust(5) + \
       teams[i][1].ljust(16) + results[i].ljust(3))

Ở ví dụ trên chúng ta có một kiểu từ điển và một tuple. Phương thức ljust() sẽ đưa các string về lề trái, phương thức rjust() để đưa các string về lề phải. Chúng ta có thể quy định số lượng kí tự của mỗi string, nếu string không đủ số lượng thì các dấu cách sẽ tự động được thêm vào.

Ajax Amsterdam  -    Inter Milano    2:3
Real Madrid     -    AC Milano       3:3
Dortmund        -    Sparta Praha    2:1

Định dạng string

Định dạng chuỗi tức là bạn đưa các tham số/biến vào bên trong string và tùy theo các tham số này mang giá trị gì mà string sẽ hiển thị ra giá trị đó. Định dạng kiểu này rất được hay dùng không chỉ trong Python mà các ngôn ngữ lập trình khác. Các tham số định dạng sử dụng toán tử %.


print ('There are %d oranges in the basket' % 32)

Ví dụ trên chúng ta sử dụng định dạng %d, đây là định dạng cho số nguyên. Bên trong chuỗi, chúng ta ghi %d tại vị trí mà chúng ta muốn hiển thị tham số, sau khi kết thúc chuỗi, chúng ta thêm % vào sau chuỗi và các giá trị và chúng ta muốn đưa vào.

There are 32 oranges in the basket

Nếu chúng ta dùng nhiều tham số bên trong chuỗi thì ở sau chuỗi các giá trị đưa vào phải nằm trong cặp dấu (), tức là giống như một tuple vậy.


print ('There are %d oranges and %d apples in the basket' % (12, 23))
There are 12 oranges and 23 apples in the basket

Ví dụ dưới đây dùng tham số được định dạng kiểu số thực và kiểu string.


print ('Height: %f %s' % (172.3, 'cm'))

%f dùng cho số thực, còn %s là một string.

Height: 172.300000 cm

Mặc định định dạng số thực có 6 chữ số thập phân theo sau. Chúng ta có thể quy định số lượng chữ số này.


print ('Height: %.1f %s' % (172.3, 'cm'))

Bằng cách thêm dấu . và số phần thập phân vào giữa %f. Python sẽ tự động loại bỏ các chữ số 0 thừa cho chúng ta.

Height: 172.3 cm

Dưới đây là một số kiểu định dạng khác.


# hexadecimal
print ("%x" % 300)
print ("%#x" % 300)

# octal
print ("%o" % 300)

# scientific
print ("%e" % 300000)

%x sẽ chuyển một số từ hệ bất kì sang hệ thập lục phân, nếu thêm kí tự # vào giữa %x thì được thêm cặp chữ 0x 😀 Định dạng %o là hiển thị số ở hệ bát phân (octal). %e thì hiển thị theo định dạng số mũ (thật ra mình cũng chẳng biết kiểu định dạng này 😀 )

12c
0x12c
454
3.000000e+05

Ví dụ dưới đây chúng ta sẽ in ra các chữ số trên 3 cột.


for x in range(1,11):
    print ('%d %d %d' % (x, x*x, x*x*x))
1 1 1
2 4 8
3 9 27
4 16 64
5 25 125
6 36 216
7 49 343
8 64 512
9 81 729
10 100 1000

Như bạn thấy, các cột không được đều nhau nhìn không đẹp. Chúng ta sẽ thêm các đặc tả độ rộng vào. Bạn xem ví dụ để biết cách làm.


for x in range(1,11):
    print ('%2d %03d %4d' % (x, x*x, x*x*x))

Nếu như chúng ta ghi là %2d thì sẽ có thêm một dấu cách vào cho đủ 2 kí tự, nếu ghi là 03 thì các số 0 sẽ được thêm vào cho đủ 3 kí tự…

 1 001    1
 2 004    8
 3 009   27
 4 016   64
 5 025  125
 6 036  216
 7 049  343
 8 064  512
 9 081  729
10 100 1000

Python – Câu lệnh điều kiện và vòng lặp

Trong phần này chúng ta sẽ tìm hiểu về câu lệnh điều kiện và vòng lặp.

Trong lập trình có những trường hợp bạn phải tùy theo dữ liệu mà có những kiểu xử lý khác nhau. Ví dụ như bài toán tìm nghiệm của phương trình bậc hai, bạn sẽ phải đưa ra 3 kết quả khác nhau khi delta khác nhau. Khi đó bạn phải dùng đến câu lệnh điều kiện. Câu lệnh điều kiện đánh giá xem một trường hợp là đúng (true) hay sai (false), để dẫn tới các quyết định khác nhau.

Mặc định trong Python thì các giá trị không phải kiểu boolean mà có giá trị khác 0 hay khác rỗng đều có thể chuyển thành boolean mang giá trị True, còn nếu bằng 0 hay rỗng thì mang giá trị False.

Câu lệnh If

if (biểu thức):
    các câu lệnh

Câu lệnh if kiểm tra xem biểu thức mang giá trị True hay False, nếu true thì thực thi các câu lệnh.

number = 8
if (number % 2 == 0):
    print (number, "is an even number")

Trong ví dụ trên, chúng ta kiểm tra xem number có phải là số chẵn hay không.

Number is an even number

Câu lệnh If..elif..else

if (biểu thức 1):
    các câu lệnh
elif (biểu thức 2):
    các câu lệnh
elif (biểu thức n):
    các câu lệnh
else:
    các câu lệnh

Nếu bạn có nhiều trường hợp cần xử lý, chẳng hạn như tìm nghiệm phương trình bậc 2 có tới 3 trường hợp của delta, thì bạn sử dụng cú pháp if..elif..else.

time = 10
if (time < 10):
    print ("Good morning")
elif (time < 20):
    print ("Good day")
else:
    print ("Good evening")

Đoạn code trên kiểm tra nếu time < 10 thì in ra Good morning, nếu time < 20 thì in ra Good day, còn nếu không phải 2 trường hợp trên thì in ra Good evening.

Good day

Ngoài ra bạn cũng có thể lồng các câu lệnh if vào nhau. Xem ví dụ.

var1 = 100
if var1:
   print("1 - Got a true expression value")
   print (var1)
else:
   print("1 - Got a false expression value")
   print(var1)

var2 = 0
if var2:
   print("2 - Got a true expression value")
   print(var2)
else:
   print("2 - Got a false expression value")
   print(var2)
1 - Got a true expression value
100
2 - Got a false expression value
0

Bây giờ chúng ta sẽ tìm hiểu về vòng lặp.

Khi Python dịch một chương trình thì nó sẽ dịch các câu lệnh một cách có thứ tự từ trên xuống dưới. Đôi khi bạn muốn làm một công việc có tính chất lặp đi lặp lại, chẳng hạn như in ra các số chẵn từ 0→1000. Bạn không thể ngồi gõ 1000 câu lệnh if..print được. Bạn sẽ phải dùng dến vòng lặp.

Vòng lặp while

Vòng lặp while thường dùng khi bạn chưa biết trước số lượng vòng lặp cần dùng.

while (biểu thức):
    các câu lệnh

Ý nghĩa của câu lệnh này là trong khi điều kiện còn đúng thì thực hiện các câu lệnh.

x = 0
while (x < 1000):
    x = x + 1
print (x)

Trong đoạn code trên, trong khi x vẫn còn < 1000 thì tăng x lên 1.

1000

Bạn có thể dùng while…else trong Python, cú pháp này có tác dụng thực thi các câu lệnh sau khi rời khỏi vòng lặp.

count = 0
while count < 5:
   print (count, " is  less than 5")
   count = count + 1
else:
   print (count, " is not less than 5")
0 is less than 5
1 is less than 5
2 is less than 5
3 is less than 5
4 is less than 5
5 is not less than 5

Vòng lặp for

Vòng lặp for thường được dùng khi bạn đã biết trước số lượng vòng lặp cần thực hiện.

for biến lặp in tập hợp:
    các câu lệnh

Biến lặp có thể là bất cứ biến nào. Bạn chỉ cần đưa vào một cái tên là Python sẽ tự ngầm hiểu kiểu dữ liệu. Còn tập hợp có thể là bất kỳ kiểu tập hợp nào mà chúng ta đã học trong bài trước, hoặc cũng có thể là một string.

for letter in 'Python':   
   print('Current Letter :', letter)

fruits = ['banana', 'apple',  'mango']</span>
for fruit in fruits:    
   print ('Current fruit :', fruit)
Current Letter : P
Current Letter : y
Current Letter : t
Current Letter : h
Current Letter : o
Current Letter : n
Current fruit : banana
Current fruit : apple
Current fruit : mango

Bạn cũng có thể dùng vòng lặp for theo chỉ số.

fruits = ['banana','apple',  'mango']
for index in range(len(fruits)):
   print ('Current fruit :', fruits[index])
Current fruit : banana
Current fruit : apple
Current fruit : mango

Cũng giống như vòng lặp while, bạn cũng có thể dùng else cho vòng lặp for.

for num in range(10,20):
   for i in range(2,num):
      if num%i == 0:      
         j=num/i         
         print('%d equals %d * %d' % (num,i,j))
         break
   else:               
      print (num, 'is a prime number')

Ví dụ trên tìm ước số của các số khác 1 và chính nó của từ 10 đến 20. Nếu không tìm thấy thì thông báo số đó là số nguyên tố. Và cũng giống như các câu lệnh điều kiện, bạn cũng có thể lồng các vòng lặp vào nhau.

10 equals 2 * 5
11 is a prime number
12 equals 2 * 6
13 is a prime number
14 equals 2 * 7
15 equals 3 * 5
16 equals 2 * 8
17 is a prime number
18 equals 2 * 9
19 is a prime number

Câu lệnh Break

Khi bạn muốn dừng vòng lặp giữa chừng thì dùng câu lệnh break.

for letter in 'Python':
   if letter == 'h':
      break
   print ('Current Letter :', letter)
  
var = 10                
while var > 0:           
   print ('Current variable value :', var)
   var = var -1
   if var == 5:
      break

Trong đoạn code trên, ở vòng lặp for chúng ta sẽ dừng vòng lặp nếu tìm thấy kí tự ‘h’ trong chuỗi “Python”, ở vòng lặp while thì sẽ dừng vòng lặp khi biến var giảm bằng 5.

Current Letter : P
Current Letter : y
Current Letter : t
Current variable value : 10
Current variable value : 9
Current variable value : 8
Current variable value : 7
Current variable value : 6

Câu lệnh Continue

Câu lệnh continue có tác dụng nhảy sang lần lặp kế tiếp. Các câu lệnh phía sau contine sẽ không được thực thi.

for letter in 'Python':     
   if letter == 'h':
      continue
   print ('Current Letter :', letter)

var = 10                    
while var > 0:              
   var = var -1
   if var == 5:
      continue
   print ('Current variable value :', var)

Trong ví dụ trên, ở vòng lặp for, chúng ta cho lặp tiếp khi tìm thấy kí tự ‘h’ mà không in ký tự đó ra màn hình. Còn ở vòng lặp while thì chúng ta cho lặp tiếp khi biến var bằng 5.

Current Letter : P
Current Letter : y
Current Letter : t
Current Letter : o
Current Letter : n
Current variable value : 9
Current variable value : 8
Current variable value : 7
Current variable value : 6
Current variable value : 4
Current variable value : 3
Current variable value : 2
Current variable value : 1
Current variable value : 0

Python – Các kiểu dữ liệu cơ bản

Trong phần này chúng ta sẽ cùng tìm hiểu về các kiểu dữ liệu cơ bản trong Python.

Kiểu Boolean

Lâu nay người ta thường dịch boolean là kiểu “luận lý”, đối với dân kỹ thuật thì cái từ này nghe hơi khó hiểu, nên mình xin mạn phép dịch là kiểu “đúng sai” cho đơn giản 🙂 Tức là kiểu giá trị này chỉ có hai giá trị là đúng (True) và sai (False), nhưng thực ra thì đối với những người đã lập trình lâu năm thì họ thường gọi là kiểu boolean (hoặc kiểu bool) luôn cho tiện. Hầu như tất cả các ngôn ngữ lập trình đều hỗ trợ kiểu này.

Ví dụ.


import random

male = False
male = bool(random.randint(0, 1))

print (male)

Đoạn code trên có sử dụng module random để tạo số ngẫu nhiên.

 import random

Để sử dụng module này thì chúng ta thêm dòng import random vào đầu chương trình.

 male = bool(random.randint(0, 1))

Và ở đây chúng ta sẽ sử dụng phương thức randint(x, y), phương thức này sẽ trả về giá trị ngẫu nhiên từ x→y, ở đây là 0→1. Sau đó chúng ta chuyển kiểu dữ liệu từ kiểu int sang kiểu bool bằng cách bao bọc lấy phương thức này bằng bool(). Nếu giá trị trả về là 0 thì sẽ được chuyển thành False, ngược lại là True.

Đoạn code dưới đây sẽ cho chúng ta biết các dạng dữ liệu khác khi được chuyển sang kiểu bool sẽ có giá trị nào.


print (bool(True))
print (bool(False))
print (bool("text"))
print (bool(""))
print (bool(' '))
print (bool(0))
print (bool())
print (bool(3))
print (bool(None))
True
False
True
False
True
False
False
True
False

Kiểu None

Đây là một kiểu đặc biệt trong Python. Ý nghĩa của kiểu này là không có giá trị gì cả, không tồn tại, rỗng…v..v


def function():
   pass

print (function())

Trong đoạn code trên, chúng ta định nghĩa một hàm. Chúng ta sẽ tìm hiểu về hàm ở các bài sau. Hàm này cũng không trả về giá trị gì cả nên nó sẽ tự động ngầm trả về giá trị None.

None

Kiểu số

Trong Python, kiểu số có 3 loại là số nguyên, số thực và số phức.


# number of baskets
baskets = 16

# number of apples in a basket
apples_in_basket = 24

# we get the total number of apples
total = baskets * apples_in_basket


print ("There are total of", total, "apples")

Trong ví dụ trên, chúng ta đếm số lượng táo dùng phép nhân trên kiểu số nguyên.

There are total of 384 apples

Ví dụ dưới đây mô tả số thực trong Python.

# 100m is 0.1 km

distance = 0.1

# 9.87s is 9.87/60*60 h

time = 9.87 / 3600

speed = distance / time

print ("The average speed of" \
      " a sprinter is " , speed, "km/h")

Chúng ta tính vận tốc chạy của vận động viên theo công thức vận tốc = quãng đường / thời gian.

print ("The average speed of" \
      " a sprinter is " , speed, "km/h")

Đoạn code trên sử dụng dấu \. Dấu này có tác dụng nối 2 chuỗi ghi trên hai dòng làm 1.

The average speed of a sprinter is  36.4741641337 km/h

String

String là kiểu dữ liệu lưu trữ văn bản.

Chúng ta có thể tạo ra một string bằng dấu nháy đơn, nháy kép hay 3 dấu nháy kép. Khi dùng 3 dấu nháy kép, chúng ta cũng có thể ghi một chuỗi trên nhiều dòng mà không cần dùng dấu \.

a = "proximity alert"
b = 'evacuation'
c = """
requiem 
for
a 
tower
"""

print (a)
print (b)
print (c)

Trong ví dụ trên chúng ta gán 3 chuỗi vào 3 biến a, b, c rồi in ra màn hình.

proximity alert
evacuation

requiem 
for 
a 
tower

Trong một chuỗi chúng ta có thể dùng các ký tự thoát. Ký tự thoát là các ký tự đặc biệt dùng cho nhiều mục đích khác nhau. Xem ví dụ.

print ("Incompatible, it don't matter though\n'cos someone's bound to hear my cry")
print ("Speak out if you do\nYou're not easy to find")

Ký tự \n là ký tự xuống dòng, các đoạn text sau ký tự này sẽ tự động xuống dòng.

Incompatible, it don't matter though
'cos someone's bound to hear my cry
Speak out if you do
You're not easy to find

Tiếp theo chúng ta tìm hiểu về ký tự xóa.

print ("Python\b\b\booo") # prints Pytooo

Ký tự \b xóa 1 ký tự, trong ví dụ trên, chúng ta dùng 3 ký tự \b, do đó 3 ký tự “hon” sẽ bị xóa để nhường chỗ cho 3 ký tự “ooo“.

print ("Towering\tinferno") # prints Towering        inferno

Dòng code trên ví dụ về ký tự tab \t, nó hoạt động giống như khi bạn bấm phim Tab vậy.

"Johnie's dog"
'Johnie\'s dog'

Đôi khi bạn ghi chuỗi trong cặp dấu nháy đơn, và bản thân bên trong chuỗi này bạn cũng cần dùng một dấu nháy đơn khác, lúc này bạn phải thêm một dấu \ trước dấu nháy đơn đó, nếu không trình biên dịch sẽ báo lỗi.

Nếu bạn không muốn sử dụng các ký tự thoát thì bạn thêm r vào trước chuỗi của mình. Các ký tự thoát sẽ được in ra như các ký tự bình thường.

print (r"Another world\nhas come")
Another world\nhas come

Như ở trên dòng chữ Another world\n sẽ được in ra.

Tiếp theo chúng ta sẽ tìm hiểu về cách nhân chuỗi và nối chuỗi.

print ("eagle " * 5)

print ("eagle " "falcon")

print ("eagle " + "and " + "falcon")

Phép nhân * có thể được dùng cho một chuỗi, lúc này nội dung chuỗi sẽ được lặp lại n lần, trong đoạn code trên chữ “eagle” được lặp lại 5 lần. Hai chuỗi để sát nhau sẽ ngầm tự động được nối vào. Và nếu bạn muốn nối chuỗi một cách rõ ràng hơn thì bạn có thể dùng toán tử +.

eagle eagle eagle eagle eagle 
eagle falcon
eagle and falcon

Kiểu Tuple

Kiểu Tuple là kiểu tập hợp nhiều phần tử, kiểu này lưu trữ các phần tử một cách có thứ tự và có thể lưu nhiều kiểu giá trị khác nhau trong một tuple. Giá trị trong tuple không thể thay đổi được.

fruits = ("oranges", "apples", "bananas")
fruits = "oranges", "apples", "bananas"
print (fruits)

Ở trên là hai cách tạo tuple.

first = (1, 2, 3)
second = (4, 5, 6)

print ("len(first) : ", len(first))
print ("max(first) : ", max(first))
print ("min(first) : ", min(first))
print ("first + second :", first + second)
print ("first * 3 : ", first * 3)
print ("1 in first : ", 1 in first)
print ("5 not in second : ", 5 not in second)

Đoạn code trên ví dụ về các thao tác với một tuple. Hàm len() có tác dụng lấy số lượng phần tử trong một tuple. Hàm max() và min() lấy giá trị lớn nhất và nhỏ nhất của tuple. Toán tử + gộp 2 tuple với nhau. Toán tử * tạo ra thêm n tuple. Toán tử in tìm xem một giá trị có nằm trong một tuple hay không.

len(first) :  3
max(first) :  3
min(first) :  1
first + second : (1, 2, 3, 4, 5, 6)
first * 3 :  (1, 2, 3, 1, 2, 3, 1, 2, 3)
1 in first :  True
5 not in second :  False

Tiếp theo chúng ta tìm hiểu về cách truy xuất phần tử.

five = (1, 2, 3, 4, 5)

print ("five[0] : ", five[0])
print ("five[-1] : ", five[-1])
print ("five[-2] : ", five[-2])
print ("five[:] : ", five[:])
print ("five[0:4] : ", five[0:4])
print ("five[1:2] : ", five[1:2])
print ("five[:2] : ", five[:2])
print ("five[:-1] : ", five[:-1])
print ("five[:9] : ", five[:9])

Để lấy một phần tử nào đó trong tuple, chúng ta sử dụng cặp dấu [], và cũng giống như các ngôn ngữ lập trình khác, chỉ số trong một tuple bắt đầu từ 0. Tức là nếu trong tuple có 5 phần tử, thì các phần tử được đánh số từ 0..4. Bạn cũng có thể dùng chỉ số là số nguyên âm, và Python sẽ tự động lấy lùi, chẳng hạn nếu bạn đưa chỉ số là -1, bạn sẽ được phần tử thứ 4. Bạn cũng có thể lấy các đoạn giá trị cố định dùng dấu hai chấm, ví dụ lấy các phần tử là 1, 2, 3, 4 là [1:4], ngoài ra nếu bạn không đưa chỉ số bắt đầu hay chỉ số kết thúc, bạn sẽ lấy được tất cả các phần tử về trước hay về sau đó, ví dụ [:3] sẽ cho ra các phần tử 0, 1, 2, 3.

five[0] :  1
five[-1] :  5
five[-2] :  4
five[:] :  (1, 2, 3, 4, 5)
five[0:4] :  (1, 2, 3, 4)
five[1:2] :  (2,)
five[:2] :  (1, 2)
five[:-1] :  (1, 2, 3, 4)
five[:9] :  (1, 2, 3, 4, 5)

Tuple có thể lưu trữ các kiểu dữ liệu khác nhau một cách linh hoạt.

mix = (1, 2, "solaris", (1, 2, 3))

print ("mix[1] :", mix[1])
print ("mix[2] :", mix[2])
print ("mix[3] :", mix[3])
print ("mix[3][0] :", mix[3][0])
print ("mix[3][1] :", mix[3][1])
print ("mix[3][2] :", mix[3][2])

Trong ví dụ trên, tuple của chúng ta có số nguyên, string và cả một tuple khác.

mix[1] : 2
mix[2] : solaris
mix[3] : (1, 2, 3)
mix[3][0] : 1
mix[3][1] : 2
mix[3][2] : 3

Để truy xuất phần tử của tuple bên trong một tuple, bạn dùng hai cặp dấu [].

Kiểu List

Kiểu list cũng là một kiểu lưu các giá trị tuần tự, một list cũng có thể lưu nhiều giá trị khác nhau. Do đó list và tuple có nhiều điểm tương đồng. Điểm khác biệt giữa list và tuple là các phần tử trong list có thể thay đổi giá trị, ngoài ra list có một số phương thức mà tuple không có. Chúng ta sẽ có nguyên một bài để nói riêng về kiểu list.

actors = ["Jack Nicholson", "Antony Hopkins", "Adrien Brody"]

Để tạo một list thì ta dùng cặp ký tự [].

num = [0, 2, 5, 4, 6, 7]

print (num[0])
print (num[2:])
print (len(num))
print (num + [8, 9])

Như đã nói ở trên, chúng ta có thể dùng các thao tác cộng trừ, lấy chỉ số… như của tuple.

0
[5, 4, 6, 7]
6
[0, 2, 5, 4, 6, 7, 8, 9]

Tiếp theo chúng ta tìm hiểu về một số thao tác của riêng list. Bắt đầu là thao tác sắp xếp một list.

numbers = [4, 3, 6, 1, 2, 0, 5 ]

print (numbers)
numbers.sort()
print (numbers)

Để sắp xếp một list thì chúng ta dùng phương thức sort()Lưu ý chúng ta không thể sắp xếp một list chứa nhiều kiểu dữ liệu khác nhau được, chẳng hạn [1, "Hello"] không thể nào được sắp xếp, vì chúng không cùng kiểu, bạn chỉ có thể sắp xếp các phần tử có cùng kiểu dữ liệu.

[4, 3, 6, 1, 2, 0, 5]
[0, 1, 2, 3, 4, 5, 6]

Phương thức reverse() cũng sắp xếp các phần tử nhưng thep thứ tự ngược lại.

numbers.reverse()   #  [5, 4, 3, 2, 1, 0]

Chúng ta có thể đếm một phần tử xuất hiện bao nhiêu lần trong một list bằng phương thức count().

numbers = [0, 0, 2, 3, 3, 3, 3]

print ("zero is here ",  numbers.count(0), "times") 
print ("one is here ",   numbers.count(1), "times")
print ("two is here ",   numbers.count(2), "time")
print ("three is here ", numbers.count(3), "times")

Trong ví dụ trên chúng ta đếm các số 0, 1, 2, 3 xuất hiện bao nhiêu lần.

zero is here  2 times
one is here  0 times
two is here  1 time
three is here  4 times

Tiếp theo chúng ta tìm hiểu cách thêm, xóa phần tử.

names = []

names.append("Frank")
names.append("Alexis")
names.append("Erika")
names.append("Ludmila")

print (names)
names.insert(0, "Adriana")
print (names)
names.remove("Frank")
names.remove("Alexis")
del names[1]
print (names)
del names[0]
print (names)

Để thêm một phần tử chúng ta dùng phương thức append() hoặc insert(), mặc định append() thêm phần tử mới vào cuối list, còn insert() có thể thêm phần tử vào vị trí bất kỳ do chúng ta quy định.

Để xóa một phần tử chúng dùng phương thức remove() hoặc dùng từ khóa del. Phương thức remove() xóa một phần tử có giá trị nhất định. Còn từ khóa del sẽ xóa phần tử ở một vị trí cụ thể.

['Frank', 'Alexis', 'Erika', 'Ludmila']
['Adriana', 'Frank', 'Alexis', 'Erika', 'Ludmila']
['Adriana', 'Ludmila']
['Ludmila']

Tiếp theo chúng ta tìm hiểu cách thay đổi một list.

first = [1, 2, 3]
second = [4, 5, 6]

first.extend(second)
print (first)

first[0] = 11
first[1] = 22
first[2] = 33
print (first)

print (first.pop(5))
print (first)

Chúng ta có dùng phương thức extend() để nhập một list vào một list khác, dùng phương thức pop() để lấy phần tử thứ n.

[1, 2, 3, 4, 5, 6]
[11, 22, 33, 4, 5, 6]
6
[11, 22, 33, 4, 5]

Tiếp theo là cách tìm vị trí xuất hiện của một giá trị nào đó trong list.

numbers = [0, 1, 2, 3, 3, 4, 5]

print (numbers.index(1))
print (numbers.index(3))

Chúng ta dùng phương thức index(), giá trị trả về là vị trí xuất hiện của phần tử mà chúng ta tìm, nếu có nhiều phần tử có giá trị giống nhau thì phương thức này in ra vị trí đầu tiên kể từ vị trí số 0.

1
3

Tiếp theo là cách chuyển đổi kiểu..

first = [1, 2, 3]
second = (4, 5, 6)

print (tuple(first))
print (list(second))

print (first)
print (second)

Chúng ta có thể dùng hàm tuple() để lấy một tuple từ một list, hoặc dùng hàm list() để lấy một list từ một tuple. Ở đây các list hay tuple nguồn không bị thay đổi mà Python sẽ lấy các giá trị gốc để tạo ra một list hay tuple mới rồi mới trả về cho chúng ta.

(1, 2, 3)
[4, 5, 6]
[1, 2, 3]
(4, 5, 6)

Kiểu Set

Set là kiểu tập hợp các phần tử không có thứ tự, không có nhiều hơn 2 phần tử có cùng một giá trị. Các phép toán có thể dùng trên set là phép hợp, giao, hiệu… giống như trong toán học.

set1 = set(['a', 'b', 'c', 'c', 'd'])
set2 = set(['a', 'b', 'x', 'y', 'z'])

print ("set1: " , set1)
print ("set2: " , set2)
print ("intersection: ", set1 & set2)
print ("union: ", set1 | set2)
print ("difference: ", set1 - set2)
print ("symmetric difference: ", set1 ^ set2)

Để tạo một set thì chúng ta dùng hàm set(). Trong ví dụ trên có 2 set. Chúng ta thực hiện phép giao 2 set bằng toán tử &, kết quả được một set mới có chứa các phần tử của chung cả 2 set, nếu chỉ tồn tại trong 1 set thì không lấy. Dùng toán tử | để thực hiện phép hợp, phép hợp lấy tất cả cá phần tử của cả 2 set. Phép hiệu có toán tử là dấu trừ -, set1 – set2 sẽ trả về các phần tử của set1 và các phần tử vừa tồn tại trong set1, vừa tồn tại trong set2. Cuối cùng phép hiệu đối xứng là toán tử ^, phép toán này lấy các phần tử trong set1 và set2, nhưng nếu tồn tại trong cả 2 set thì không lấy.

set1:  set(['a', 'c', 'b', 'd'])
set2:  set(['a', 'x', 'b', 'y', 'z'])
intersection:  set(['a', 'b'])
union:  set(['a', 'c', 'b', 'd', 'y', 'x', 'z'])
difference:  set(['c', 'd'])
symmetric difference:  set(['c', 'd', 'y', 'x', 'z'])

Tiếp theo là một số thao tác trên set.

set1 = set([1, 2])
set1.add(3)
set1.add(4)

set2 = set([1, 2, 3, 4, 6, 7, 8])
set2.remove(8)

print (set1)
print (set2)

print ("Is set1 subset of set2 ? : ", set1.issubset(set2))
print ("Is set1 superset of set2 ? : ", set1.issuperset(set2))

set1.clear()
print (set1)

Phương thức add() sẽ chèn một phần tử vào set. Phương thức remove() xóa một phần tử tại vị trí bất kỳ. Phương thức clear() xóa toàn bộ phần tử trong set. Phương thức issubset() kiểm tra xem một set có phải là set con của một set khác không. Phương thức issuperset() kiểm tra xem một set có phải là set cha của set khác không.

 
set([1, 2, 3, 4]) 
set([1, 2, 3, 4, 6, 7]) 
Is set1 subset of set2 ? : True 
Is set1 superset of set2 ? : False set([]) 

Kiểu từ điển

Kiểu này lưu trữ các phần tử theo dạng các cặp khóa-giá trị (keyvalue). Các chỉ số trong từ điển là các khóa. Do đó các khóa phải khác nhau, chúng ta sẽ có một bài riêng để nói về kiểu này.

words = { 'girl': 'Maedchen', 'house': 'Haus', 'death': 'Tod' }

print (words['house'])

print (words.keys())
print (words.values())
print (words.items())

print (words.pop('girl'))
print (words)
words.clear()
print (words)

Ví dụ trên mô tả sơ lược về cách sử dụng kiểu từ điển.

Haus
['house', 'girl', 'death']
['Haus', 'Maedchen', 'Tod']
[('house', 'Haus'), ('girl', 'Maedchen'), ('death', 'Tod')]
Maedchen
{'house': 'Haus', 'death': 'Tod'}
{}

Python – Các thành phần của ngôn ngữ Python

Ngôn ngữ lập trình cũng như ngôn ngữ tự nhiên của con người, tất cả đều có các thành phần cấu tạo nên nó. Các thành phần của Python gồm có chú thích (comment), biến (variable), giá trị (value), toán tử (operator), từ khóa (keyword), và ký hiệu (delimeter).

Chú thích

Các dòng chú thích được dùng để ghi chú cho người viết code dễ đọc các đoạn code khác, chú thích không được dịch bởi trình thông dịch. Để biến một dòng thành chú thích thì chúng ta thêm dấu # vào trước dòng đó.

>>># Day la ham main
>>># Tac gia Pho Code

def main():
    print ("Comments example")

main()

Tất cả những dòng phía sau dấu # đều không được dịch bởi trình thông dịch.

Biến

Biến là một cái tên dùng để lưu trữ một giá trị nào đó, có thể là một số, một chuỗi kí tự… Khác với các ngôn ngữ lập trình như C, Java… bạn phải khai báo cả kiểu dữ liệu đi kèm, thì trong Python bạn không phải khai báo kiểu dữ liệu, trình thông dịch Python sẽ tự phát hiện kiểu dữ liệu khi bạn gán giá trị cho biến đó.

Biến trong Python được đặt tên bằng chữ cái, chữ số và dấu gạch dưới _, tuy nhiên tên biến không được phép bắt đầu bằng chữ số, còn lại chữ cái và dấu _ thì được phép.

Value
value2
company_name

Trên đây là những tên biến hợp lệ.

12Val
exx$
first-name

Trên đây là những tên biến không hợp lệ.

Tên biến trong Python phân biệt chữ HOA và thường, tức là Price, price, và PRICE là ba biến khác nhau.

number = 10
Number = 11
NUMBER = 12

print (number, Number, NUMBER)

Đoạn code trên gán 3 giá trị số vào 3 biến khác nhau.

10 11 12

Giá trị

Giá trị là các ký tự đại diện cho một đại lượng gắn liền với một biến nào đó.

age = 29
nationality = "Hungarian"

Trên đây agenationality là các biến. 29"Hungarian" là các giá trị.


# literals.py

name1 = "Jane"
age1 = 12

name2 = "Rose"
age2 = 16

"Patrick"
34

"Luke"
23

print (name1, age1)
print (name2, age2)

Nếu chúng ta không gán các giá trị vào biến thì chúng sẽ bị hủy bỏ.

Jane 12
Rose 16

Toán tử

Toán tử chính là các phép tính toán.

+    -    ~    *    **    /    %
<< >>    &    |    ^
and    or    not    in    not in 
is    is not    < >    !=    <>
==    <= >=

Trên đây là danh sách các toán tử trong ngôn ngữ Python. Chúng ta sẽ nói chi tiết về các toán tử ở bài sau.

Khối lệnh

Trong các ngôn ngữ lập trình khác như Pascal, Java… một khối lệnh được nằm trong cặp dấu {} hoặc cặp từ khóa BEGINEND. Còn Python thì dùng khoảng trắng, bạn có thể dùng dấu tab hoặc dấu cách (space) đều được. Trên hướng dẫn của Python khuyến khích dùng 4 dấu cách.

if age > 18:
    print ("adult person")

for i in range(5):
    print (i)

Trong đoạn code trên, sau câu lệnh if là một khối lệnh. Câu lệnh mới trong khối lệnh này phải thụt dòng vào trong. Khi muốn thoát ra khỏi khối lệnh thì thụt ra ngoài 🙂 Giả sử như câu lệnh for cũng thụt vào bằng chiều dài với câu lệnh print thì câu lệnh for nằm cùng khối lệnh với câu lệnh print.

Ký hiệu

Các ký hiệu dùng để bao bọc lấy các biểu thức, điều kiện… Trong các ví dụ trước có hàm print chúng ta phải dùng cặp ký hiệu () để bao bọc lấy nội dung cần được in ra màn hình. Thực ra đối với Python 2.x thì lệnh print không cần cặp ký tự này, nhưng Python 3.x thì phải có cặp ký tự này, lý do là vì trong phiên bản 2.x, print là một câu lệnh nhưng trong 3.x thì print đã được phát triển thành một hàm riêng.

(       )       [       ]       {       }      
,       :       .       `       =       ;
+=      -=      *=      /=      //=     %=
<= |= ^= >>=     <<=     **=
'       "       \       @

Từ khóa

Từ khóa là các từ dành riêng cho Python. Từ khóa thường được dùng để thi hành một lệnh nào đó, hoặc tên của một thành phần trong Python. Chẳng hạn từ khóa if thi hành lệnh so sánh. Từ khóa for để bắt đầu một vòng lặp, từ khóa and là tên của toán tử and… Bạn không được đặt tên biến hay tên hàm trùng với tên của các từ khóa.

and       del       from      not       while
as        elif      global    or        with
assert    else      if        pass      yield
break     except    import    print
class     exec      in        raise
continue  finally   is        return 
def       for       lambda    try

Trên đây là danh sách các từ khóa trong Python. Chúng ta sẽ tìm hiểu sâu hơn về từ khóa trong bài khác.

Python – Trình thông dịch Python

Trong bài này chúng ta sẽ cùng tìm hiểu về cách sử dụng trình thông dịch Python.

Như đã nói ở phần trước, có 2 cách để chạy các đoạn code Python. Một là dùng trực tiếp trong trình thông dịch, hai là chạy trên file script riêng.

C:\Users\PhoCode\python hello_world.py
Hello World!

Ở trên là cách sử dụng bằng cách viết file script.

Trình thông dịch Python

Nhưng trong phần này chúng ta sẽ không viết code trong file riêng mà chúng ta sẽ dùng trình thông dịch Python. Trình thông dịch này rất hữu ích cho việc khám phá thế giới của Python. Mỗi khi chúng ta muốn test một hàm, một module nào đó… cách nhanh nhất là mở trình thông dịch Python lên và test chứ ngồi tạo một file script rồi viết thì mất thời gian quá. Để chạy trình thông dịch Python, bạn mở terminal lên (trong Windows là Command Prompt – cmd) rồi gõ lệnh python.exe.

C:\Users\PhoCode>python.exe 
Python 3.5.1 (v3.5.1:37a07cee5969, Dec 6 2015, 01:38:48) [MSC v.1900 32 bit (Intel)] on win32
Type "copyright", "credits" or "license()" for more information.
>>>

Python sẽ hiện ra câu giới thiệu với tên, phiên bản… ở đây mình dùng là phiên bản 3.5.1. Sau đó là dấu nhắc lệnh >>> đặc trưng của Python. Nếu muốn thoát Python, bạn gõ lệnh quit() hoặc exit().

Bây giờ chúng ta tìm hiểu một số lệnh cơ bản.

>>> credits
    Thanks to CWI, CNRI, BeOpen.com, Zope Corporation and a cast of thousands
    for supporting Python development.  See www.python.org for more information.

Như trong dòng giới thiệu của Python, bạn có thể gõ credits, copyright, hoặc license() để xem một số thông tin. Như trên mình gõ credits, Python hiện ra thông tin của những tổ chức đã góp phần phát triển nên Python.

>>> copyright
Copyright (c) 2001-2011 Python Software Foundation.
All Rights Reserved.

Copyright (c) 2000 BeOpen.com.
All Rights Reserved.

Copyright (c) 1995-2001 Corporation for National Research Initiatives.
All Rights Reserved.

Copyright (c) 1991-1995 Stichting Mathematisch Centrum, Amsterdam.
All Rights Reserved.

Lệnh copyright hiện ra thông tin bản quyền của ngôn ngữ Python.

Lệnh license() hiện ra quy định sử dụng của Python. Khi dùng lệnh này, Python chỉ hiện ra từng đoạn ngắn chứ không hiện hết, bạn bấm Enter để Python hiện ra dần dần hoặc bấm q để thoát.

Lệnh help()

Lệnh help cung cấp các thông tin trợ giúp cho bạn trong việc sử dụng Python.

>>> help
Type help() for interactive help, or help(object) for help about object.
>>> 

Có hai cách để dùng trợ giúp. Bạn có thể gõ help() và Python sẽ chuyển sang chế độ trợ giúp, hoặc gõ help(<tên>) để nhận ngay trợ giúp về tên của một thành phần nào đó trong Python.

Ví dụ, gõ help(True) thì Python sẽ hiện ra thông tin về các đối tượng bool.

Help on bool object:

class bool(int)
 |  bool(x) -> bool
 |  
 |  Returns True when the argument x is true, False otherwise.
 |  The builtins True and False are the only two instances of the class bool.
 |  The class bool is a subclass of the class int, and cannot be subclassed.
 |  
 |  Method resolution order:
 |      bool
 |      int
 |      object
 |  
 |  Methods defined here:
 |  
 |  __and__(...)
 |      x.__and__(y) <==> x&yhelp()

 ...

Nếu phần thông tin trợ giúp quá dài, bạn bấm Enter để python tiếp tục hiện ra. Nếu muốn thoát thì bấm phím q.

Bây giờ chúng ta thử gõ help() để chuyển sang chế độ trợ giúp.

>>> help()

Welcome to Python 2.7!  This is the online help utility.

If this is your first time using Python, you should definitely check out
the tutorial on the Internet at http://docs.python.org/tutorial/.

Enter the name of any module, keyword, or topic to get help on writing
Python programs and using Python modules.  To quit this help utility and
return to the interpreter, just type "quit".

To get a list of available modules, keywords, or topics, type "modules",
"keywords", or "topics".  Each module also comes with a one-line summary
of what it does; to list the modules whose summaries contain a given word
such as "spam", type "modules spam".

help> 

Đoạn giới thiệu về chế độ trợ giúp của Python hiện ra, nếu muốn thoát thì bạn gõ quit.

Tại đây bạn có thể gõ vào tên của bất kỳ thành phần nào có trong Python và Python sẽ in ra đầy đủ thông tin của thành phần đó. Ngoài ra nếu bạn chưa biết hết tên về các thành phần có trong Python, bạn có thể gõ modules và Python sẽ hiện ra tên các module có trong Python, gõ keywords để Python hiện ra danh sách các từ khóa, gõ topics để Python hiện ra danh sách các chủ đề.

help> keywords

Here is a list of the Python keywords.  Enter any keyword to get more help.

and                 elif                if                  print
as                  else                import              raise
assert              except              in                  return
break               exec                is                  try
class               finally             lambda              while
continue            for                 not                 with
def                 from                or                  yield
del                 global              pass

keywords để hiện ra danh sách các từ khóa trong Python. Sau đó bạn có thể gõ một từ khóa nào đó và Python sẽ cung cấp thông tin của từ khóa đó. Modules và topics cũng tương tự.

help> if
The "if" statement
******************
The "if" statement is used for conditional execution:
if_stmt ::= "if" expression ":" suite
 ( "elif" expression ":" suite )*
 ["else" ":" suite]
It selects exactly one of the suites by evaluating the expressions one
by one until one is found to be true (see section *Boolean operations*
for the definition of true and false); then that suite is executed
(and no other part of the "if" statement is executed or evaluated).
If all expressions are false, the suite of the "else" clause, if
present, is executed.
Related help topics: TRUTHVALUE
help>

Ở trên mình gõ từ khóa if và Python hiện ra thông tin của từ khóa này.

Viết code trong Python

Đây mới là sự tiện lợi bậc nhất mà Python đem lại.

>>> 2 + 4
6
>>> 5 * 56
280
>>> 5 - 45
-40
>>> 

Bạn có thể dùng Python làm một chiếc máy tính. Khi bạn gõ một biểu thức nào đó, Python sẽ tính toán ngay và trả lại kết quả cho bạn.

>>> a = 3
>>> b = 4
>>> a**b
81
>>> a == b
False
>>> a < b True >>>

Ngoài ra bạn có thể định nghĩa biến và thực hiện tính toán trên chúng. Lưu ý ** là phép lấy lũy thừa nhé.

>>> class Car:
...   pass
... 
>>> def function():
...   pass
... 
>>> for i in range(5):
...   print (i)
... 
0
1
2
3
4
>>> 

Bạn có thể định nghĩa lớp, hàm, các lệnh điều khiển… Lưu ý là bạn phải nhớ bấm Tab để thụt dòng vì Python dùng dấu thụt dòng này để nhận biết các câu lệnh và khối lệnh. Khi muốn thoát khỏi một khối lệnh thì bạn bấm Enter hai lần.

>>> import os
>>> os.getcwd()
C:\\Python

Ở trên chúng ta báo với Python là chúng ta sẽ sử dụng module os bằng câu lệnh import, sau đó chúng ta dùng phương thức getcwd() để lấy đường dẫn thư mục hiện tại.

Cuối cùng, nếu bạn muốn thoát Python, gõ lệnh quit() hoặc exit(), cái nào cũng được.

Triết lý Python – The Zen of Python

Đây chắc là một bài thơ hay cái gì đó mình cũng không rõ ^^. Để hiện ra đoạn “triết lý” này thì bạn gõ import this.

>>> import this
The Zen of Python, by Tim Peters

Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Readability counts.
Special cases aren't special enough to break the rules.
Although practicality beats purity.
Errors should never pass silently.
Unless explicitly silenced.
In the face of ambiguity, refuse the temptation to guess.
There should be one-- and preferably only one --obvious way to do it.
Although that way may not be obvious at first unless you're Dutch.
Now is better than never.
Although never is often better than *right* now.
If the implementation is hard to explain, it's a bad idea.
If the implementation is easy to explain, it may be a good idea.
Namespaces are one honking great idea -- let's do more of those!

 

Python – Giới thiệu về ngôn ngữ Python

Python là một ngôn ngữ lập trình mạnh mẽ và lý tưởng dành cho người mới bắt đầu học lập trình. Series hướng dẫn lập trình Python này cung cấp những kiến thức từ cơ bản đến nâng cao, sau khi hoàn thành series, bạn sẽ có đủ kiến thức để tiếp tục nghiên cứu sâu hơn về Python, cũng như có thể phát triển các ứng dụng desktop, game, hay thậm chí là phát triển một website với Python. Python phù hợp với tất cả mọi người, từ những lập trình viên chính thống đến những người chỉ học Python để giải trí.

Python

Python logo

Python là một ngôn ngữ lập trình hướng đối tượng, python hoàn toàn tạo kiểu động, cấp phát bộ nhớ động. Mục đích ra đời của Python là cung cấp một ngôn ngữ lập trình có cấu trúc rõ ràng, sáng sủa, thuận tiện cho người mới học lập trình. Python được phát triển bởi Guido và Rossum. Phiên bản đầu tiên được phát hành vào năm 1991. Python được lấy cảm hứng từ ABC, Haskell, Java, Lisp, Icon và Perl. Python là một ngôn ngữ thông dịch, đa nền tảng. Một trong những đặc điểm độc nhất của Python là ngôn ngữ này không dùng đến dấu chấm phẩy, dấu mở-đóng ngoặc {} để kết thúc câu lệnh hay khối lệnh, mà cách duy nhất để nó nhận biết một lệnh là dấu thụt đầu dòng.

Hiện tại Python có hai dòng phiên bản là dòng 2.x và 3.x. Phiên bản Python mà serie này sử dụng là phiên bản 3.5.1. Ngày nay Python được phát triển bởi một cộng đồng tình nguyện trên khắp thế giới, và lẽ dĩ nhiên nó là một phần mềm mã nguồn mở.

Trang chủ của Python nằm tại địa chỉ python.org.

Sử dụng Python

Trước tiên bạn lên trang chủ của Python down về và cài đặt. Sau đó các bạn mở Command Prompt (cmd) lên. Đến đây bạn có 2 cách để biên dịch một chương trình Python. Cách một là chạy trực tiếp Python bằng cách gõ python.exe (hoặc python không cũng được, không cần đuôi .exe), khi đó dòng giới thiệu của Python hiện ra và bạn có thể sử dụng các lệnh của Python trên command prompt.

C:\Users\PhoCode\python.exe
Python 3.5.1 <v3.5.1:37a07cee5969, Dec 6 2015, 01:38:48> [MSC v.1900 32 bit (Intel)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> print ("Hello World")
Hello World
>>>

Hàm print() có tác dụng in một đoạn text ra màn hình. Cách thứ 2 là bạn tạo một file text với đuôi là *.py và viết script trong đó, ví dụ:

print ("Hello World")

Sau đó bạn gõ python.exe kèm theo tên file phía sau, bạn có thể phải chỉ ra cả đường dẫn đến file đó nữa nếu thư mục hiện hành trên Command Prompt của bạn không phải là thư mục chứa file py.

C:\Users\PhoCode\python C:\hello_world.py
Hello World

Thông thường khi viết code thì chúng ta sẽ dùng cách viết code trong file riêng khi làm một chương trình gì đó, còn với cách viết lệnh trực tiếp thì chúng ta sẽ dùng để test các thư viện, hàm…v.v.

Kết thúc bài đầu tiên, bạn đã có biết sơ qua về ngôn ngữ lập trình Python và biết cách làm thế nào để chạy một chương trình Python. Nếu có gì khúc mắc bạn có thể đặt câu hỏi trong phần bình luận.

 

 

Qt 5 C++ – Trò chơi phá gạch

Trong phần này chúng ta viết lại một game khác là game phá gạch (Breakout).

Breakout

Game phá gạch được phát riển bởi Atari vào năm 1976. Trong game, người chơi sẽ di chuyển một thanh đỡ cố gắng đỡ quả bóng bay lên phá vỡ đống gạch phía trên. Khi số gạch đã bị phá hết thì người chơi chiến thắng, nếu người chơi không đỡ được quả bóng làm quả bóng rơi xuống dưới thanh đỡ thì người chơi thua.

Mô tả game

Game của chúng ta có một thanh đỡ (Paddle), một quả bóng (Ball) và 30 viên gạch (Brick). Chúng ta sẽ dùng timer để chạy vòng lặp game. Game này chúng ta làm đơn giản thôi nên không cần thêm hệ thống tính điểm vào làm gì.

#pragma once

#include <QImage>
#include <QRect>

class Paddle {

  public:
    Paddle();
    ~Paddle();

  public:
    void resetState();
    void move();
    void setDx(int);
    QRect getRect();
    QImage & getImage();

  private:
    QImage image;
    QRect rect;
    int dx;
    static const int INITIAL_X = 200;
    static const int INITIAL_Y = 360;
};

Đây là file header của lớp paddle. Hai hằng số INITIAL_XINITIAL_Y là tọa độ khởi đầu của paddle.

#include <iostream>
#include "paddle.h"

Paddle::Paddle() {
    
  dx = 0;    
  image.load("paddle.png");
  image = image.scaled(70, 10);
  rect = image.rect();
  resetState();
}

Paddle::~Paddle() {
    
 std::cout << ("Paddle deleted") << std::endl;
}

void Paddle::setDx(int x) {
  dx = x;
}

void Paddle::move() {
    
    int x = rect.x() + dx;
    int y = rect.top();
    
    rect.moveTo(x, y);
}

void Paddle::resetState() {
    
  rect.moveTo(INITIAL_X, INITIAL_Y);
}

QRect Paddle::getRect() {
    
  return rect;
}

QImage & Paddle::getImage() {
    
  return image;
}

Hướng di chuyển của Paddle là trái-phải.

Paddle::Paddle() {
    
  dx = 0;    
  image.load("paddle.png");
  image = image.scaled(70, 10);
  rect = image.rect(); resetState(); 
}

Load ảnh của paddle, bạn có thể lên google search từ khóa “paddle sprite” để tìm ảnh làm mẫu. Sau khi load ảnh lên thì phải resize về kích thước 70×10 vì ảnh gốc của chúng ta có thể lớn hơn kích thước đó. Các bạn có thể resize về kích thước khác nếu thích.

void Paddle::move() {
    
    int x = rect.x() + dx;
    int y = rect.top();
    
    rect.moveTo(x, y);
}

Phương thức  move() di chuyển paddle. Tốc độ di chuyển của paddle được định nghĩa bởi biến dx.

void Paddle::resetState() {
    
  rect.moveTo(INITIAL_X, INITIAL_Y);
}

Phương thức resetState() sẽ đưa paddle về vị trí khởi đầu.

#pragma once

#include <QImage>
#include <QRect>

class Brick {

  public:
    Brick(int, int);
    ~Brick();

  public:
    bool isDestroyed();
    void setDestroyed(bool);
    QRect getRect();
    void setRect(QRect);
    QImage & getImage();

  private:
    QImage image;
    QRect rect;
    bool destroyed;
};

Đây là file header của lớp Brick. Biến destroyed lưu trữ trạng thái hiện tại của brick là còn hay đã bị phá.

#include <iostream>
#include "brick.h"

Brick::Brick(int x, int y) {
    
  image.load("brick.png");
  image = image.scaled(40, 12);
  destroyed = false;
  rect = image.rect();
  rect.translate(x, y);
}

Brick::~Brick() {

  std::cout << ("Brick deleted") << std::endl;
}

QRect Brick::getRect() {
    
  return rect;
}

void Brick::setRect(QRect rct) {
    
  rect = rct;
}

QImage & Brick::getImage() {
    
  return image;
}

bool Brick::isDestroyed() {
    
  return destroyed;
}

void Brick::setDestroyed(bool destr) {
    
  destroyed = destr;
}
Brick::Brick(int x, int y) {
    
  image.load("brickie.png");
  image = image.scaled(40, 12);
  destroyed = false;
  rect = image.rect();
  rect.translate(x, y);
}

Trong phương thức khởi tạo, chúng ta load ảnh của brick, resize về kích thước 40×12 và thiết lập biến destroyed là false.

bool Brick::isDestroyed() {
    
  return destroyed;
}

Phương thức isDestroyed() trả về trạng thái của brick.

#pragma once

#include <QImage>
#include <QRect>

class Ball {

  public:
    Ball();
    ~Ball();

  public:
    void resetState();
    void autoMove();
    void setXDir(int);
    void setYDir(int);
    int getXDir();
    int getYDir();
    QRect getRect();
    QImage & getImage();
  
  private:
    int xdir;
    int ydir;
    QImage image;
    QRect rect;
    static const int INITIAL_X = 230;
    static const int INITIAL_Y = 355;    
    static const int RIGHT_EDGE = 300;
};

Đây là file header của lớp Ball. Hai biến xdirydir lưu trữ hướng đi của quả bóng, hai biến này có giá trị -1 hoặc 1.

#include <iostream>
#include "ball.h"

Ball::Ball() {

  xdir = 1;
  ydir = -1;

  image.load("ball.png");
  image = image.scaled(12, 12);
  rect = image.rect();
  resetState();
}

Ball::~Ball() {
    
  std::cout << ("Ball deleted") << std::endl;
}

void Ball::autoMove() {
    
  rect.translate(xdir, ydir);

  if (rect.left() == 0) {
    xdir = 1;
  }

  if (rect.right() == RIGHT_EDGE) {
    xdir = -1;
  }

  if (rect.top() == 0) {
    ydir = 1;
  }
}

void Ball::resetState() {
    
  rect.moveTo(INITIAL_X, INITIAL_Y);
}

void Ball::setXDir(int x) {
    
  xdir = x;
}

void Ball::setYDir(int y) {
    
  ydir = y;
}

int Ball::getXDir() {
    
  return xdir;
}

int Ball::getYDir() {
    
  return ydir;
}

QRect Ball::getRect() {
    
  return rect;
}

QImage & Ball::getImage() {
    
  return image;
}
xdir = 1;
ydir = -1;

Chúng ta khởi tạo cho quả bóng đi theo hướng đông bắc.

void Ball::autoMove() {
    
  rect.translate(xdir, ydir);

  if (rect.left() == 0) {
    xdir = 1;
  }

  if (rect.right() == RIGHT_EDGE) {
    xdir = -1;
  }

  if (rect.top() == 0) {
    ydir = 1;
  }
}

Phương thức autoMove() được gọi theo vòng lặp của game. Nếu quả bóng chạm với tường trái, phải hay phía trên thì hướng của nó sẽ được thiết lập là hướng ngược lại, còn nếu chạm với bờ tường phía dưới thì game over.

#pragma once

#include <QWidget>
#include <QKeyEvent>
#include "ball.h"
#include "brick.h"
#include "paddle.h"

class Breakout : public QWidget {
    
  public:
    Breakout(QWidget *parent = 0);
    ~Breakout();

  protected:
    void paintEvent(QPaintEvent *);
    void timerEvent(QTimerEvent *);
    void keyPressEvent(QKeyEvent *);
    void keyReleaseEvent(QKeyEvent *);
    void drawObjects(QPainter *);
    void finishGame(QPainter *, QString);
    void moveObjects();

    void startGame();
    void pauseGame();
    void stopGame();
    void victory();
    void checkCollision();

  private:
    int x;
    int timerId;
    static const int N_OF_BRICKS = 30;
    static const int DELAY = 10;
    static const int BOTTOM_EDGE = 400;
    Ball *ball;
    Paddle *paddle;
    Brick *bricks[N_OF_BRICKS];
    bool gameOver;
    bool gameWon;
    bool gameStarted;
    bool paused;
};

Đây là file header của lớp Breakout, lớp này điều khiển toàn bộ game.

void keyPressEvent(QKeyEvent *);
void keyReleaseEvent(QKeyEvent *);

Paddle sẽ được điều khiển bằng các phím mũi tên. Hai phương thức keyPressEvent()keyReleaseEvent() lắng nghe sự kiện bấm phím từ người dùng.

int x;
int timerId;

Biến x lưu trữ tọa độ x hiện tại của paddle.

static const int N_OF_BRICKS = 30;

N_OF_BRICKS là số lượng các brick.

static const int DELAY = 10;

Hằng số DELAY lưu trữ tốc độ của game.

static const int BOTTOM_EDGE = 400;

BOTTOM_EDGE là giới hạn phía dưới cửa sổ, nếu quả bóng chạm vào thì game over.

bool gameOver;
bool gameWon;
bool gameStarted;
bool paused;

Bốn biến ở trên lưu trữ trạng thái hiện thời của game.

#include <QPainter>
#include <QApplication>
#include "breakout.h"

Breakout::Breakout(QWidget *parent)
    : QWidget(parent) {
  
  x = 0;
  gameOver = false;
  gameWon = false;
  paused = false;
  gameStarted = false;
  ball = new Ball();
  paddle = new Paddle();

  int k = 0;
  
  for (int i=0; i<5; i++) {
    for (int j=0; j<6; j++) {
      bricks[k] = new Brick(j*40+30, i*10+50);
      k++; 
    }
  }  
}

Breakout::~Breakout() {
    
 delete ball;
 delete paddle;
 
 for (int i=0; i<N_OF_BRICKS; i++) { 
   delete bricks[i]; 
 } 
} 

void Breakout::paintEvent(QPaintEvent *e) { 
  Q_UNUSED(e); 
  QPainter painter(this); 

  if (gameOver) { 
    finishGame(&painter, "Game lost"); 
  } else if(gameWon) { 
    finishGame(&painter, "Victory"); 
  } else { 
    drawObjects(&painter); 
  } 
} 

void Breakout::finishGame(QPainter *painter, QString message) { 
  QFont font("Courier", 15, QFont::DemiBold); 
  QFontMetrics fm(font); 
  int textWidth = fm.width(message); 
  painter->setFont(font);
  int h = height();
  int w = width();

  painter->translate(QPoint(w/2, h/2));
  painter->drawText(-textWidth/2, 0, message);    
}

void Breakout::drawObjects(QPainter *painter) {
    
  painter->drawImage(ball->getRect(), ball->getImage());
  painter->drawImage(paddle->getRect(), paddle->getImage());

  for (int i=0; i<N_OF_BRICKS; i++) { if (!bricks[i]->isDestroyed()) {
      painter->drawImage(bricks[i]->getRect(), bricks[i]->getImage());
    }
  }      
}

void Breakout::timerEvent(QTimerEvent *e) {
    
  Q_UNUSED(e);  
    
  moveObjects();
  checkCollision();
  repaint();
}

void Breakout::moveObjects() {

  ball->autoMove();
  paddle->move();
}

void Breakout::keyReleaseEvent(QKeyEvent *e) {
    
    int dx = 0;
    
    switch (e->key()) {
        case Qt::Key_Left:
            dx = 0;
            paddle->setDx(dx);        
            break;       
            
        case Qt::Key_Right:
            dx = 0;
            paddle->setDx(dx);        
            break;    
    }
}

void Breakout::keyPressEvent(QKeyEvent *e) {
    
    int dx = 0;
    
    switch (e->key()) {
    case Qt::Key_Left:
        
        dx = -1;
        paddle->setDx(dx);
        
        break;
       
    case Qt::Key_Right:
    
        dx = 1;
        paddle->setDx(dx);        
        break;
    
    case Qt::Key_P:
    
        pauseGame();
        break;
        
    case Qt::Key_Space:

        startGame();
        break;        
                
    case Qt::Key_Escape:
        
        qApp->exit();
        break;
        
    default:
        QWidget::keyPressEvent(e);
    }
}

void Breakout::startGame() {
     
  if (!gameStarted) {
    ball->resetState();
    paddle->resetState();

    for (int i=0; i<N_OF_BRICKS; i++) { bricks[i]->setDestroyed(false);
    }
    
    gameOver = false; 
    gameWon = false; 
    gameStarted = true;
    timerId = startTimer(DELAY);  
  }      
}

void Breakout::pauseGame() {
    
  if (paused) {
      
    timerId = startTimer(DELAY);
    paused = false;
  } else {
      
    paused = true;
    killTimer(timerId); 
  }        
}

void Breakout::stopGame() {
    
  killTimer(timerId);    
  gameOver = true;      
  gameStarted = false;
}

void Breakout::victory() {
    
  killTimer(timerId);    
  gameWon = true;  
  gameStarted = false;    
}

void Breakout::checkCollision() {
  
  if (ball->getRect().bottom() > BOTTOM_EDGE) {
    stopGame();
  }

  for (int i=0, j=0; i<N_OF_BRICKS; i++) { if (bricks[i]->isDestroyed()) {
      j++;
    }
    
    if (j == N_OF_BRICKS) {
      victory();
    }
  }

  if ((ball->getRect()).intersects(paddle->getRect())) {

    int paddleLPos = paddle->getRect().left();  
    int ballLPos = ball->getRect().left();   

    int first = paddleLPos + 8;
    int second = paddleLPos + 16;
    int third = paddleLPos + 24;
    int fourth = paddleLPos + 32;

    if (ballLPos < first) { ball->setXDir(-1);
      ball->setYDir(-1);
    }

    if (ballLPos >= first && ballLPos < second) { ball->setXDir(-1);
      ball->setYDir(-1*ball->getYDir());
    }

    if (ballLPos >= second && ballLPos < third) { ball->setXDir(0);
       ball->setYDir(-1);
    }

    if (ballLPos >= third && ballLPos < fourth) { ball->setXDir(1);
       ball->setYDir(-1*ball->getYDir());
    }

    if (ballLPos > fourth) {
      ball->setXDir(1);
      ball->setYDir(-1);
    }
  }      
 
  for (int i=0; i<N_OF_BRICKS; i++) { 
      if ((ball->getRect()).intersects(bricks[i]->getRect())) {

      int ballLeft = ball->getRect().left();  
      int ballHeight = ball->getRect().height(); 
      int ballWidth = ball->getRect().width();
      int ballTop = ball->getRect().top();  
  
      QPoint pointRight(ballLeft + ballWidth + 1, ballTop);
      QPoint pointLeft(ballLeft - 1, ballTop);  
      QPoint pointTop(ballLeft, ballTop -1);
      QPoint pointBottom(ballLeft, ballTop + ballHeight + 1);  

      if (!bricks[i]->isDestroyed()) {
        if(bricks[i]->getRect().contains(pointRight)) {
           ball->setXDir(-1);
        } 

        else if(bricks[i]->getRect().contains(pointLeft)) {
           ball->setXDir(1);
        } 

        if(bricks[i]->getRect().contains(pointTop)) {
           ball->setYDir(1);
        } 

        else if(bricks[i]->getRect().contains(pointBottom)) {
           ball->setYDir(-1);
        } 

        bricks[i]->setDestroyed(true);
      }
    }
  }
}
int k = 0;
for (int i=0; i<5; i++) {
  for (int j=0; j<6; j++) {
    bricks[k] = new Brick(j*40+30, i*10+50);
    k++; 
  }
}

Trong phương thức khởi tạo của lớp Breaout, chúng ta khởi tạo 30 viên gạch trên 5 hàng 6 cột.

void Breakout::paintEvent(QPaintEvent *e) {
  
  Q_UNUSED(e);  
    
  QPainter painter(this);

  if (gameOver) {
  
    finishGame(&painter, "Game lost");    

  } else if(gameWon) {

    finishGame(&painter, "Victory");
  }
  else {
      
    drawObjects(&painter);
  }
}

Trong phương thức paintEvent(), nếu trạng thái của game là đang chơi thì chúng ta vẽ paddle, ball và brick, nếu không phải thì chúng ta vẽ các đoạn chữ “Game lost”, “Victory” tùy trường hợp.

void Breakout::finishGame(QPainter *painter, QString message) {
    
  QFont font("Courier", 15, QFont::DemiBold);
  QFontMetrics fm(font);
  int textWidth = fm.width(message);

  painter->setFont(font);
  int h = height();
  int w = width();

  painter->translate(QPoint(w/2, h/2));
  painter->drawText(-textWidth/2, 0, message);    
}

Trong phương thức finishGame() cũng vậy, phương thức này vẽ đoạn chữ trên màn hình và tùy vào game đã thắng hay thua mà chúng ta vẽ cho phù hợp.

void Breakout::drawObjects(QPainter *painter) {
    
  painter->drawImage(ball->getRect(), ball->getImage());
  painter->drawImage(paddle->getRect(), paddle->getImage());

  for (int i=0; i<N_OF_BRICKS; i++) { if (!bricks[i]->isDestroyed()) {
      painter->drawImage(bricks[i]->getRect(), bricks[i]->getImage());
    }
  }      
}

Phương thức drawObjects() vẽ tất cả các đối tượng có trong game.

void Breakout::timerEvent(QTimerEvent *e) {
    
  Q_UNUSED(e);  
    
  moveObjects();
  checkCollision();
  repaint();
}

Phương thức timerEvent(), là vòng lặp chính của game, cứ mỗi lần lặp, chúng ta vẽ các đối tượng, kiểm tra sự va chạm của các đối tượng và gọi phương thức repaint().

void Breakout::moveObjects() {

  ball->autoMove();
  paddle->move();
}

Phương thức moveObjects() di chuyển quả bóng và thanh đỡ.

void Breakout::keyReleaseEvent(QKeyEvent *e) {
    
    int dx = 0;
    
    switch (e->key()) {
        case Qt::Key_Left:
            dx = 0;
            paddle->setDx(dx);        
            break;       
            
        case Qt::Key_Right:
            dx = 0;
            paddle->setDx(dx);        
            break;    
    }
}

Khi người chơi thả phím thì thanh đỡ ngừng di chuyển, chúng ta thiết lập với dx về 0.

void Breakout::keyPressEvent(QKeyEvent *e) {
    
    int dx = 0;
    
    switch (e->key()) {
    case Qt::Key_Left:
        
        dx = -1;
        paddle->setDx(dx);
        
        break;
       
    case Qt::Key_Right:
    
        dx = 1;
        paddle->setDx(dx);        
        break;
    
    case Qt::Key_P:
    
        pauseGame();
        break;
        
    case Qt::Key_Space:

        startGame();
        break;        
                
    case Qt::Key_Escape:
        
        qApp->exit();
        break;
        
    default:
        QWidget::keyPressEvent(e);
    }
}

Tại phương thức keyPressEvent(), chúng ta lắng nghe các sự kiện bấm phím. Cữ mỗi lần bấm các phím mũi tên trái hoặc phải, chúng ta set biến dx về -1 hoặc 1. Nếu bấm phím P thì game sẽ tạm dừng, nếu bấm phím Esc thì thoát toàn bộ game, bấm phím Space thì game sẽ bắt đầu chơi.

void Breakout::startGame() {
     
  if (!gameStarted) {
    ball->resetState();
    paddle->resetState();

    for (int i=0; i<N_OF_BRICKS; i++) { 
      bricks[i]->setDestroyed(false);
    }
    
    gameOver = false; 
    gameWon = false; 
    gameStarted = true;
    timerId = startTimer(DELAY);  
  }      
}

Phương thức startGame() reset lại vị trí paddle, vị trí ball, thiết lập trạng thái của các brick về lúc chưa bị hủy và chạy timer.

void Breakout::pauseGame() {
    
  if (paused) {
      
    timerId = startTimer(DELAY);
    paused = false;
  } else {
      
    paused = true;
    killTimer(timerId); 
  }        
}

Phương thức pauseGame() được dùng để dừng game hoặc cho game tiếp tục chạy. Trạng thái dừng của game được lưu trong biến paused. Để dừng game thì chúng ta chỉ đơn giản là dừng vòng lặp chứ không thay đổi gì trong các phương thức di chuyển của các lớp kia.

void Breakout::stopGame() {
    
  killTimer(timerId);    
  gameOver = true;      
  gameStarted = false;
}

Phương thức stopGame() sẽ hủy timer và thiết lập các biến trạng thái cho phù hợp với trạng thái của game.

void Breakout::checkCollision() {
  
  if (ball->getRect().bottom() > BOTTOM_EDGE) {
    stopGame();
  }
...
}

Trong phương thức checkCollision(), chúng ta kiểm tra sự va chạm giữa các đối tượng. Nếu quả bóng chạm tới đáy màn hình thì game kết thúc.

for (int i=0, j=0; i<N_OF_BRICKS; i++) { if (bricks[i]->isDestroyed()) {
    j++;
  }
    
  if (j == N_OF_BRICKS) {
    victory();
  }
}

Chúng ta kiểm tra xem có bao nhiêu viên gạch đã bị hủy, nếu số gạch bị hủy bằng 30 thì người chơi chiến thắng.

#include <QApplication>
#include "breakout.h"

int main(int argc, char *argv[]) {
    
  QApplication app(argc, argv);  
    
  Breakout window;
  
  window.resize(300, 400);
  window.setWindowTitle("Breakout");
  window.show();

  return app.exec();
}
Untitled

Qt 5 C++ – Trò chơi rắn săn mồi

Trong phần này chúng ta sẽ viết lại một game cổ điển đó là game rắn săn mồi (Snake Game).

Snake

Game này được phát hành vào những năm 70 trên các hệ máy cầm tay, một thời gian sau được đưa lên PC. Trong game người chơi sẽ điều khiển một con rắn. Mục tiêu của người chơi là cố gắng cho con rắn ăn được càng nhiều mồi càng tốt. Mỗi lần con rắn ăn mồi, cơ thể nó sẽ dài ra. Game kết thúc khi người chơi cho rắn “ăn” phải tường hoặc cơ thể chính mình.

Mô tả game

Kích thước của mỗi đốt trên con rắn là 10px. Con rắn sẽ được điều khiển bằng các phím mũi tên. Khi game kết thúc, dòng chữ “Game Over” sẽ hiện lên giữa màn hình.

#pragma once

#include <QWidget>
#include <QKeyEvent>

class Snake : public QWidget {
    
  public:
      Snake(QWidget *parent = 0);

  protected:
      void paintEvent(QPaintEvent *);
      void timerEvent(QTimerEvent *);
      void keyPressEvent(QKeyEvent *);

  private:    
      QImage apple;
    
      static const int B_WIDTH = 300;
      static const int B_HEIGHT = 300;
      static const int DOT_SIZE = 10;
      static const int ALL_DOTS = 900;
      static const int RAND_POS = 29;
      static const int DELAY = 140;    
      
      int timerId;
      int dots;
      int apple_x;
      int apple_y;      
      
      int x[ALL_DOTS]; 
      int y[ALL_DOTS]; 
      
      int startAngle = 0;
      int spanAngle = 16 * 360;
      bool leftDirection;
      bool rightDirection;
      bool upDirection;
      bool downDirection;
      bool inGame;
      
      void loadImages();
      void initGame();
      void locateApple();
      void checkApple();
      void checkCollision();
      void move();
      void doDrawing();
      void gameOver(QPainter &);      
};
static const int B_WIDTH = 300;
static const int B_HEIGHT = 300;
static const int DOT_SIZE = 10;
static const int ALL_DOTS = 900;
static const int RAND_POS = 29;
static const int DELAY = 140;  

Ý nghĩa của các hằng số trên như sau:

  • B_WIDTHB_HEIGHT là kích thước cửa sổ chính.
  • DOT_SIZE là kích thước của mồi và mỗi đốt trên con rắn.
  • ALL_DOTS là số lượng đốt tối đa của rắn. Vì cửa sổ có kích thước 300 * 300, mỗi đốt rắn có kích thước 10 * 10 nến số lượng đốt tối đa là (300 * 300) / (10 * 10) = 900.
  • RAND_POS là hằng số để tính vị trí ngẫu nhiên của mồi.
  • DELAY là tốc độ của game.
int x[ALL_DOTS]; 
int y[ALL_DOTS]; 

Hai mảng x, y lưu trữ vị trí của toàn bộ đốt của con rắn.

#include <QPainter>
#include <QTime>
#include "snake.h"

Snake::Snake(QWidget *parent) : QWidget(parent) {

    setStyleSheet("background-color:black;");
    leftDirection = false;
    rightDirection = true;
    upDirection = false;
    downDirection = false;
    inGame = true;

    resize(B_WIDTH, B_HEIGHT);
    loadImages();
    initGame();
}

void Snake::loadImages() {   
    apple.load("apple.png");
    apple = apple.scaled(DOT_SIZE, DOT_SIZE);
}

void Snake::initGame() {

    dots = 3;

    for (int z = 0; z < dots; z++) {
        x[z] = 50 - z * 10;
        y[z] = 50;
    }

    locateApple();

    timerId = startTimer(DELAY);
}
    
void Snake::paintEvent(QPaintEvent *e) {
  
    Q_UNUSED(e);  

    doDrawing();
}    
    
void Snake::doDrawing() {
    
    QPainter qp(this);
    
    if (inGame) {

        qp.drawImage(apple_x, apple_y, apple);

        for (int z = 0; z < dots; z++) { 
             if (z == 0) { 
                 qp.setBrush(QBrush("red")); 
                 qp.drawChord(x[z], y[z], DOT_SIZE, DOT_SIZE, startAngle, spanAngle); 
             } else { 
                 qp.setBrush(QBrush("green")); 
                 qp.drawChord(x[z], y[z], DOT_SIZE, DOT_SIZE, startAngle, spanAngle); 
             } 
        } 
     } else { 
        gameOver(qp); 
     } 
} 

void Snake::gameOver(QPainter &qp) { 
     QString message = "Game over"; 
     QFont font("Courier", 15, QFont::DemiBold); 
     QFontMetrics fm(font); 

     int textWidth = fm.width(message); 
     qp.setFont(font); 
     int h = height(); 
     int w = width(); 

     qp.setPen(QPen(QBrush("white"), 1)); 
     qp.translate(QPoint(w/2, h/2)); 
     qp.drawText(-textWidth/2, 0, message); 
} 

void Snake::checkApple() { 
     if ((x[0] == apple_x) && (y[0] == apple_y)) { 
          dots++; 
          locateApple(); 
     } 
} 

void Snake::move() { 
    for (int z = dots; z > 0; z--) {
        x[z] = x[(z - 1)];
        y[z] = y[(z - 1)];
    }

    if (leftDirection) {
        x[0] -= DOT_SIZE;
    }

    if (rightDirection) {
        x[0] += DOT_SIZE;
    }

    if (upDirection) {
        y[0] -= DOT_SIZE;
    }

    if (downDirection) {
        y[0] += DOT_SIZE;
    }
}

void Snake::checkCollision() {

    for (int z = dots; z > 0; z--) {

        if ((z > 4) && (x[0] == x[z]) && (y[0] == y[z])) {
            inGame = false;
        }
    }

    if (y[0] >= B_HEIGHT) {
        inGame = false;
    }

    if (y[0] < 0) { 
        inGame = false; 
    } 

    if (x[0] >= B_WIDTH) {
        inGame = false;
    }

    if (x[0] < 0) { 
        inGame = false; 
    } 

    if(!inGame) { 
        killTimer(timerId); 
    } 
} 

void Snake::locateApple() { 
    QTime time = QTime::currentTime(); 
    qsrand((uint) time.msec()); 
    int r = qrand() % RAND_POS; 
    apple_x = (r * DOT_SIZE); 
    r = qrand() % RAND_POS; 
    apple_y = (r * DOT_SIZE); 
} 

void Snake::timerEvent(QTimerEvent *e) { 
    Q_UNUSED(e); 
    if (inGame) { 
        checkApple(); 
        checkCollision(); 
        move(); 
    } 

    repaint(); 
} 

void Snake::keyPressEvent(QKeyEvent *e) { 
    int key = e->key();
    
    if ((key == Qt::Key_Left) && (!rightDirection)) {
        leftDirection = true;
        upDirection = false;
        downDirection = false;
    }

    if ((key == Qt::Key_Right) && (!leftDirection)) {
        rightDirection = true;
        upDirection = false;
        downDirection = false;
    }

    if ((key == Qt::Key_Up) && (!downDirection)) {
        upDirection = true;
        rightDirection = false;
        leftDirection = false;
    }

    if ((key == Qt::Key_Down) && (!upDirection)) {
        downDirection = true;
        rightDirection = false;
        leftDirection = false;
    }    
    
    QWidget::keyPressEvent(e);    
}
void Snake::loadImages() {
    
    apple.load("apple.png");
    apple = apple.scaled(DOT_SIZE, DOT_SIZE);
}

Phương thức loadImages() load ảnh quả táo dùng làm mồi cho con rắn. Vì ảnh chúng ta dùng có thể có kích thước khác 10*10 nên ta phải resize kích thước ảnh lại bằng phương thức scaled().

void Snake::initGame() {

    dots = 3;

    for (int z = 0; z < dots; z++) {
        x[z] = 50 - z * 10;
        y[z] = 50;
    }

    locateApple();

    timerId = startTimer(DELAY);
}

Trong phương thức initGame() chúng ta cho khởi tạo game, bao gồm khởi tạo số lượng đốt của rắn, khởi tạo vị trí ngẫu nhiên của mồi và chạy timer.

void Snake::checkApple() {

    if ((x[0] == apple_x) && (y[0] == apple_y)) {

        dots++;
        locateApple();
    }
}

Bên trong phương thức checkApple() chúng ta kiểm tra xem nếu vị trí đầu rắn có trùng khớp với vị trí của mồi thì chúng ta tăng số lượng đốt lên và khởi tạo mồi mới.

void Snake::move() {

    for (int z = dots; z > 0; z--) {
        x[z] = x[(z - 1)];
        y[z] = y[(z - 1)];
    }

    if (leftDirection) {
        x[0] -= DOT_SIZE;
    }

    if (rightDirection) {
        x[0] += DOT_SIZE;
    }

    if (upDirection) {
        y[0] -= DOT_SIZE;
    }

    if (downDirection) {
        y[0] += DOT_SIZE;
    }
}

Phương thức move() cập nhật vị trí mới của con rắn, các đốt của con rắn sẽ có vị trí mới là vị trí của đốt phía trước con rắn, đối với đốt đầu tiên thì chúng ta dựa vào hướng di chuyển hiện tại để tính vị trí mới.

Phương thức checkCollision() kiểm tra xem nếu con rắn có đụng đầu vào tường hay cắn chính mình hay không.

for (int z = dots; z > 0; z--) {

    if ((z > 4) && (x[0] == x[z]) && (y[0] == y[z])) {
        inGame = false;
    }
}

Nếu con rắn cắn chính mình thì game over.

if (y[0] >= B_HEIGHT) {
    inGame = false;
}

Nếu con rắn chạm tường thì cũng game over.

void Snake::timerEvent(QTimerEvent *e) {
    
    Q_UNUSED(e);  
    
    if (inGame) {

        checkApple();
        checkCollision();
        move();
    }

    repaint();
}  

Phương thức timerEvent() là vòng lặp của game, cứ mỗi lần lặp chúng ta kiểm tra con rắn có ăn mồi, đụng tường hay cắn chính mình hay không và cập nhật vị trí con rắn.

if ((key == Qt::Key_Left) && (!rightDirection)) {
    leftDirection = true;
    upDirection = false;
    downDirection = false;
}

Nếu bấm phím mũi tên trái và hướng đi của con rắn không phải hướng ngược lại – tức bên phải thì chúng ta cập nhật lại hướng đi mới của con rắn.

#include <QApplication>
#include "snake.h"

int main(int argc, char *argv[]) {
    
  QApplication app(argc, argv);
  
  Snake window;

  window.setWindowTitle("Snake");
  window.show();

  return app.exec();
}
Untitled