跳轉到內容

使用 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 語句列印一個數字和一個表示式。這個數字是為了幫助跟蹤我正在處理的是哪條語句。注意每個表示式最終都是 FalseTrue。在 Python 中,false 可以寫成 0,true 可以寫成 1。

程式碼

print(1, a == 6)
print(2, a == 7)

分別打印出 TrueFalse,正如預期的那樣,因為第一個是真,第二個是假。第三個 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

此時,andor 運算子似乎壞了。對於前兩個表示式,'a' 等效於 'a''b',而 'b' 不等效,這沒有道理。此外,'b' 等效於 'a''b' 也沒有道理。在檢查瞭解釋器如何處理布林運算子之後,這些結果實際上確實執行了您所要求的操作,只是與您認為您所要求的操作不同。

當 Python 直譯器遇到 or 表示式時,它會獲取第一個語句並檢查它是否為真。如果第一個語句為真,則 Python 返回該物件的 value,而無需檢查第二個語句。這是因為對於 or 表示式,如果其中一個 value 為真,則整個表示式為真;程式無需理會第二個語句。另一方面,如果第一個 value 被評估為假,Python 會檢查第二部分並返回該 value。該第二部分決定了整個表示式的真值,因為第一部分為假。直譯器這種“懶惰”被稱為“短路”,是許多程式語言中評估布林表示式的常用方法。

類似地,對於 and 表示式,Python 使用短路技術來加快真值評估。如果第一個語句為假,則整個表示式必須為假,因此它返回該 value。否則,如果第一個 value 為真,它會檢查第二個並返回該 value。

需要注意的一點是,布林表示式返回一個表示 TrueFalse 的 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 獲取加速度計值。

讓我們看看 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)


使用 Linkbot 學習 Python 3
 ← For 迴圈 布林表示式 字典 → 
華夏公益教科書