使用 Linkbot 學習 Python 3/布林表示式
這裡有一個布林表示式的小例子(你不需要輸入它)
a = 6
b = 7
c = 42
print(1, a == 6)
print(2, a == 7)
print(3, a == 6 and b == 7)
print(4, a == 7 and b == 7)
print(5, not a == 7 and b == 7)
print(6, a == 7 or b == 7)
print(7, a == 7 or b == 6)
print(8, not (a == 7 and b == 6))
print(9, not a == 7 and b == 6)
輸出為
1 True 2 False 3 True 4 False 5 True 6 True 7 False 8 True 9 False
發生了什麼?程式由一組看起來很奇怪的 print 語句組成。每個 print 語句列印一個數字和一個表示式。這個數字是為了幫助跟蹤我正在處理的是哪條語句。注意每個表示式最終都是 False 或 True。在 Python 中,false 可以寫成 0,true 可以寫成 1。
程式碼
print(1, a == 6)
print(2, a == 7)
分別打印出 True 和 False,正如預期的那樣,因為第一個是真,第二個是假。第三個 print 語句 print(3, a == 6 and b == 7) 有點不同。運算子 and 的意思是,如果前後的語句都為真,則整個表示式為真,否則整個表示式為假。下一行 print(4, a == 7 and b == 7) 顯示瞭如果 and 表示式的一部分為假,則整個表示式都為假。and 的行為可以總結如下
| 表示式 | 結果 |
|---|---|
true and true |
true |
true and false |
false |
false and true |
false |
false and false |
false |
請注意,如果第一個表示式為假,Python 不會檢查第二個表示式,因為它知道整個表示式為假。嘗試執行 False and print("Hi") 並將其與執行 True and print("Hi") 進行比較。這個技術的專業術語是 短路求值
下一行 print(5, not a == 7 and b == 7) 使用了 not 運算子。not 只給出表示式的反義。(這個表示式可以改寫為 print(5, a != 7 and b == 7))。這裡有表格
| 表示式 | 結果 |
|---|---|
not true |
false |
not false |
true |
接下來的兩行 print(6, a == 7 or b == 7) 和 print(7, a == 7 or b == 6) 使用了 or 運算子。or 運算子如果第一個表示式為真,或者第二個表示式為真,或者兩者都為真,則返回真。如果兩者都不為真,則返回假。以下是表格
| 表示式 | 結果 |
|---|---|
true or true |
true |
true or false |
true |
false or true |
true |
false or false |
false |
請注意,如果第一個表示式為真,Python 不會檢查第二個表示式,因為它知道整個表示式為真。這是因為 or 在至少有一半表示式為真的情況下為真。第一部分為真,所以第二部分可以是假或真,但整個表示式仍然為真。
接下來的兩行 print(8, not (a == 7 and b == 6)) 和 print(9, not a == 7 and b == 6) 顯示了可以使用括號將表示式分組並強制先計算其中一部分。請注意,括號將表示式從假改為真。這是因為括號強制 not 應用於整個表示式,而不是隻應用於 a == 7 部分。
這是一個使用布林表示式的示例
list = ["Life", "The Universe", "Everything", "Jack", "Jill", "Life", "Jill"]
# make a copy of the list. See the More on Lists chapter to explain what [:] means.
copy = list[:]
# sort the copy
copy.sort()
prev = copy[0]
del copy[0]
count = 0
# go through the list searching for a match
while count < len(copy) and copy[count] != prev:
prev = copy[count]
count = count + 1
# If a match was not found then count can't be < len
# since the while loop continues while count is < len
# and no match is found
if count < len(copy):
print("First Match:", prev)
以下是輸出
First Match: Jill
這個程式透過不斷地檢查匹配 while count < len(copy) and copy[count] is not equal to prev 來工作。當 count 大於 copy 的最後一個索引或找到匹配項時,and 不再為真,所以迴圈退出。if 只檢查確保 while 退出是因為找到了匹配項。
本例中還使用了 and 的另一個“技巧”。如果你檢視 and 的表格,你會發現第三個條目是“false and false”。如果 count >= len(copy)(換句話說,count < len(copy) 為假),那麼 copy[count] 永遠不會被檢視。這是因為 Python 知道如果第一個為假,那麼它們不可能都為真。這被稱為短路,如果 and 的後半部分在出現錯誤時會導致錯誤,它將非常有用。我使用第一個表示式 (count < len(copy)) 來檢查 count 是否是 copy 的有效索引。(如果你不相信我,請刪除匹配的“Jill”和“Life”,檢查它是否仍然有效,然後反轉 count < len(copy) and copy[count] != prev 的順序為 copy[count] != prev and count < len(copy)。)
當您需要同時檢查兩個或更多不同事項時,可以使用布林表示式。
程式設計新手常犯的一個錯誤是誤解布林運算子的工作方式,這源於 Python 直譯器讀取這些表示式的方式。例如,在最初學習了“and”和“or”語句之後,人們可能會認為表示式 x == ('a' or 'b') 會檢查變數 x 是否等效於字串 'a' 或 'b' 之一。事實並非如此。要了解我的意思,請使用直譯器啟動互動式會話並輸入以下表達式
>>> 'a' == ('a' or 'b')
>>> 'b' == ('a' or 'b')
>>> 'a' == ('a' and 'b')
>>> 'b' == ('a' and 'b')
這將是違反直覺的結果
>>> 'a' == ('a' or 'b')
True
>>> 'b' == ('a' or 'b')
False
>>> 'a' == ('a' and 'b')
False
>>> 'b' == ('a' and 'b')
True
此時,and 和 or 運算子似乎壞了。對於前兩個表示式,'a' 等效於 'a' 或 'b',而 'b' 不等效,這沒有道理。此外,'b' 等效於 'a' 和 'b' 也沒有道理。在檢查瞭解釋器如何處理布林運算子之後,這些結果實際上確實執行了您所要求的操作,只是與您認為您所要求的操作不同。
當 Python 直譯器遇到 or 表示式時,它會獲取第一個語句並檢查它是否為真。如果第一個語句為真,則 Python 返回該物件的 value,而無需檢查第二個語句。這是因為對於 or 表示式,如果其中一個 value 為真,則整個表示式為真;程式無需理會第二個語句。另一方面,如果第一個 value 被評估為假,Python 會檢查第二部分並返回該 value。該第二部分決定了整個表示式的真值,因為第一部分為假。直譯器這種“懶惰”被稱為“短路”,是許多程式語言中評估布林表示式的常用方法。
類似地,對於 and 表示式,Python 使用短路技術來加快真值評估。如果第一個語句為假,則整個表示式必須為假,因此它返回該 value。否則,如果第一個 value 為真,它會檢查第二個並返回該 value。
需要注意的一點是,布林表示式返回一個表示 True 或 False 的 value,但 Python 認為許多不同的事物都有分配給它們的真值。要檢查任何給定物件 x 的真值,可以使用函式 bool(x) 來檢視其真值。以下是各種物件的真值示例表格
| True | False |
|---|---|
| True | False |
| 1 | 0 |
| 非零數字 | 字串 'None' |
| 非空字串 | 空字串 |
| 非空列表 | 空列表 |
| 非空字典 | 空字典 |
現在,我們可以理解之前測試那些布林表示式時出現的令人困惑的結果。讓我們來看看直譯器在執行該程式碼時的“視角”
第一種情況
>>> 'a' == ('a' or 'b') # Look at parentheses first, so evaluate expression "('a' or 'b')"
# 'a' is a nonempty string, so the first value is True
# Return that first value: 'a'
>>> 'a' == 'a' # the string 'a' is equivalent to the string 'a', so expression is True
True
第二種情況
>>> 'b' == ('a' or 'b') # Look at parentheses first, so evaluate expression "('a' or 'b')"
# 'a' is a nonempty string, so the first value is True
# Return that first value: 'a'
>>> 'b' == 'a' # the string 'b' is not equivalent to the string 'a', so expression is False
False
第三種情況
>>> 'a' == ('a' and 'b') # Look at parentheses first, so evaluate expression "('a' and 'b')"
# 'a' is a nonempty string, so the first value is True, examine second value
# 'b' is a nonempty string, so second value is True
# Return that second value as result of whole expression: 'b'
>>> 'a' == 'b' # the string 'a' is not equivalent to the string 'b', so expression is False
False
第四種情況
>>> 'b' == ('a' and 'b') # Look at parentheses first, so evaluate expression "('a' and 'b')"
# 'a' is a nonempty string, so the first value is True, examine second value
# 'b' is a nonempty string, so second value is True
# Return that second value as result of whole expression: 'b'
>>> 'b' == 'b' # the string 'b' is equivalent to the string 'b', so expression is True
True
因此,Python 在給出那些看似錯誤的結果時,實際上是在正常工作。如前所述,重要的是要認識到您的布林表示式在評估時會返回什麼 value,因為它並不總是顯而易見的。
回到那些初始表示式,以下是如何編寫它們以使其按您想要的方式執行
>>> 'a' == 'a' or 'a' == 'b' True >>> 'b' == 'a' or 'b' == 'b' True >>> 'a' == 'a' and 'a' == 'b' False >>> 'b' == 'a' and 'b' == 'b' False
當這些比較被評估時,它們會以 True 或 False 的形式返回真值,而不是字串,所以我們得到了正確的結果。
加速度計是一種可以用來檢測物體加速度或重力作用的裝置。例如,當您坐在汽車裡而司機踩油門時,您會感覺到一股力量將您向後推回座椅。或者,當您現在坐在椅子上時,您會感覺到地球的重力將您拉向地球中心。Linkbot 也可以使用其加速度計來感受作用於它的這些力,您可以從 Linkbot 獲取加速度計值。
讓我們看看 Linkbot 的 getAccelerometerData() 函式。
import barobo
dongle = barobo.Dongle()
dongle.connect()
myLinkbot = dongle.getLinkbot()
accel = myLinkbot.getAccelerometerData()
print(accel)
輸出
[0.005859375, -0.1005859375, 0.9755859375]
這個小程式連線到 Linkbot,獲取其加速度計資料並打印出來。你得到的數字可能與顯示的數字不同,具體取決於你執行程式時 Linkbot 的方向和加速度。
請注意,該值不是一個單一的值,而是一個包含 3 個值的列表。每個值表示 Linkbot 當前在特定軸方向上所受到的力(以 G 為單位),分別對應於 Linkbot 的 x 軸、y 軸和 z 軸。
我們可以透過以下公式計算 Linkbot 所受到的力的總量:
讓我們編寫一個 Python 函式,根據三個加速度值列表計算加速度的大小。
import math # for math.sqrt(), the square-root function
def accelMag(accel):
return math.sqrt( accel[0]**2 + accel[1]**2 + accel[2]**2 )
import barobo
dongle = barobo.Dongle()
dongle.connect()
myLinkbot = dongle.getLinkbot('ABCD') # Replace 'ABCD' with your Linkbot's Serial ID
accel = myLinkbot.getAccelerometerData() # Get the accel values
print(accel) # print accel values
print(accelMag(accel)) # print total magnitude of accel values
輸出
[0.0078125, -0.1015625, 0.974609375] 0.9799180631054775
請注意,這些數字的單位是地球重力單位,通常稱為“G”。當生成以上輸出時,程式碼是在靜置在桌上的 Linkbot 上執行的。由於 Linkbot 只受到地球重力的作用,我們預計其大小非常接近 1。如果 Linkbot 失重,漂浮在太空中或處於自由落體狀態,我們預計其大小會接近零。
讓我們嘗試以對角線方式傾斜 Linkbot 並再次執行它。我們應該預期列表中的三個數字會發生變化,但總量級仍然應該非常接近 1。
輸出
[0.72265625, -0.6875, -0.0244140625] 0.997739621400201
正如我們所看到的,我們編寫的 accelMag() 函式可以顯示作用在 Linkbot 上的加速度總量,無論其方向如何。這意味著我們可以用它來檢測自由落體或高加速度事件,例如 Linkbot 被掉落或撞到東西時。
現在,讓我們嘗試編寫一個程式,如果 Linkbot 檢測到它處於自由落體狀態,就讓它發出蜂鳴聲。請注意,加速度計報告的值存在一些“噪聲”。噪聲是指機器人感測器隨機拾取的微量誤差。這意味著 Linkbot 報告的大小几乎永遠不會完全為零或完全為 1,即使 Linkbot 處於自由落體狀態或靜置在桌面上。這意味著當我們編寫程式時,我們不想檢查加速度大小是否為零。相反,我們想檢查它是否低於某個閾值;例如,0.2 G。
import math # for math.sqrt()
def accelMag(accel):
return math.sqrt( accel[0]**2 + accel[1]**2 + accel[2]**2 )
import barobo
import time # for time.sleep()
dongle = barobo.Dongle()
dongle.connect()
myLinkbot = dongle.getLinkbot('ABCD') # Replace 'ABCD' with your Linkbot's Serial ID
print('Gently toss your Linkbot into the air. Type Ctrl-C to quit the program.')
while True:
accel = myLinkbot.getAccelerometerData()
if accelMag(accel) < 0.2: # 1
myLinkbot.setBuzzerFrequency(440) # 2
print('Wheeee!')
time.sleep(1)
myLinkbot.setBuzzerFrequency(0)
請注意,我們現在在程式中添加了一個無限迴圈。迴圈在“# 1”處重複檢查加速度計的大小。如果大小小於 0.2,它會在“# 2”處使蜂鳴器響 1 秒鐘。
示例
[edit | edit source]password1.py
## This program asks a user for a name and a password.
# It then checks them to make sure that the user is allowed in.
name = input("What is your name? ")
password = input("What is the password? ")
if name == "Josh" and password == "Friday":
print("Welcome Josh")
elif name == "Fred" and password == "Rock":
print("Welcome Fred")
else:
print("I don't know you.")
示例執行
What is your name? Josh What is the password? Friday Welcome Josh
What is your name? Bill What is the password? Money I don't know you.
練習
[edit | edit source]編寫一個程式,讓使用者猜你的名字,但他們只有 3 次機會才能猜到,否則程式會退出。
print("Try to guess my name!")
count = 1
name = "guilherme"
guess = input("What is my name? ")
while count < 3 and guess.lower() != name: # .lower allows things like Guilherme to still match
print("You are wrong!")
guess = input("What is my name? ")
count = count + 1
if guess.lower() != name:
print("You are wrong!") # this message isn't printed in the third chance, so we print it now
print("You ran out of chances.")
else:
print("Yes! My name is", name + "!")
編寫一個 Linkbot 程式,如果加速度超過 2 G,則發出蜂鳴聲。這通常發生在機器人被撞擊或劇烈搖晃時。
import math
def accelMag(accel):
return math.sqrt( accel[0]**2 + accel[1]**2 + accel[2]**2 )
import barobo
import time # For time.sleep()
dongle = barobo.Dongle()
dongle.connect()
myLinkbot = dongle.getLinkbot('ABCD') # Change 'ABCD' to your Linkbot's Serial ID
print('Shake or bump your Linkbot. Type Ctrl-C to quit the program.')
while True:
accel = myLinkbot.getAccelerometerData()
if accelMag(accel) > 2:
myLinkbot.setBuzzerFrequency(440)
print('Ow!')
time.sleep(1)
myLinkbot.setBuzzerFrequency(0)