Try   HackMD

原版:
請連接7個LED燈至GPIO腳位,並讓LED燈從中間開始亮起,當亮滿之後則從外圍開始熄滅。程式需使用for迴圈取代窮舉控制,並以while迴圈包覆持續執行。時間間隔為兩秒,輸出如下所示:

LED 1 2 3 4 5 6 7
第二秒 *
第四秒 * * *
第六秒 * * * * *
第八秒 * * * * * * *
第十秒 * * * * *
第十二秒 * * *
第十四秒 *
第十六秒

一個比較正常的作法是分成兩個部分:

import time, RPi.GPIO as GPIO
GPIO.setmode(GPIO.BOARD)

pin = [3,5,7,8,10,12,13]
for i in pin:
    GPIO.setup(i, GPIO.OUT)

for i in range(4)
    GPIO.output(pin[4-i], 1)
    GPIO.output(pin[4+i], 1)
    time.sleep(2)

for i in range(4)
    GPIO.output(pin[4-i], 0)
    GPIO.output(pin[4+i], 0)
    time.sleep(2)

但我相信大家也知道,這邊的軟工不是正常人,所以想辦法把它塞成一個:

import time, RPi.GPIO as GPIO
GPIO.setmode(GPIO.BOARD)

pin = [3,5,7,8,10,12,13]

for i in pin:
    GPIO.setup(i, GPIO.OUT)

while(True):
    for i in range(8)
        GPIO.output(pin[3-(-1)**(i//4)*(i+i//4)%4], 1 - i//4)
        GPIO.output(pin[3+(-1)**(i//4)*(i+i//4)%4], 1 - i//4)
    time.sleep(2)

過程依序是:這問題表列有8種狀態,所以基本上會考慮for迴圈重複8次。而03是先前擴展燈(改)的程式,這沒有問題,問題是出在47。我們首先想到的是03是開燈,47是關燈,所以後面的output必須從4開始變成0,這可以以1 – i//4辦到。

GPIO.output(pin[3-i], 1 - i//4)
接著是指定腳位,由於從4開始變成關燈,第一個想法是取4的餘數,這樣對於i來說就會變成:

|For迴圈|0|1|2|3|4|5|6|7|
|%4|0|1|2|3|0|1|2|3|

所以程式碼會變成這樣:

GPIO.output(pin[3-i%4], 1 - i//4)

又由於03是往外指定腳位,47是往內,所以方向是相反的──這時候想到的是依靠-1的次方來轉換正負號:

GPIO.output(pin[3-(-1)**(i//4)*i%4], 1 - i//4)

當然這時候我們會習慣性的執行看看,結果發現在i等於4的時候,pin[3]被調整為關,導致後續正中間的燈都是熄滅狀態。

要解決這個狀況一個方法是加入continue跳過4,再將迴圈拉到9圈。而另外一個方法就是把47變成58,利用的是i取4的餘數會從4開始變成1的性質:

GPIO.output(pin[3-(-1)**(i//4)*(i+i//4)%4], 1 - i//4)

注意數學運算子的先後順序適當的以括號包覆。

一般來說到這邊就滿足了啦……後來想一想,如果我只是想印出一個菱形呢?

一般而言,印出一個菱形都會用二維陣列(串列),以座標點的方式搭配線性函數切割。那用這方法是不是就不用碰到函數了?

於是來稍稍改動一下,確實可以:

pin = [0,0,0,0,0,0,0]
for i in range(8):
    pin[3-(-1)**(i//4)*(i+i//4)%4] = pin[3+(-1)**(i//4)*(i+i//4)%4] = 1 - i//4
    print(pin)
-----------------------output------------------------------
[0, 0, 0, 1, 0, 0, 0]
[0, 0, 1, 1, 1, 0, 0]
[0, 1, 1, 1, 1, 1, 0]
[1, 1, 1, 1, 1, 1, 1]
[0, 1, 1, 1, 1, 1, 0]
[0, 0, 1, 1, 1, 0, 0]
[0, 0, 0, 1, 0, 0, 0]
[0, 0, 0, 0, 0, 0, 0]

但怎麼想怎麼怪,於是就和我的學弟討論了一下,學弟提了另外一個寫法,拆成上下兩半print,中間計算[0]和[1]的個數。

確實,我們只要輸出的話根本不用管串列這件事情。

於是有了下面這個完全沒有可讀性的一行文:

print(str([[0]*(i-3) + [1]*(2-(i-5)*2+1) + [0]*(i-3) if i>3 else [0]*(2-i+1) + [1]*(i*2+1) + [0]*(2-i+1) for i in range(7)]).replace('], [','\n').replace('[','').replace(']',''))

------------------------output----------------------------------
0, 0, 0, 1, 0, 0, 0
0, 0, 1, 1, 1, 0, 0
0, 1, 1, 1, 1, 1, 0
1, 1, 1, 1, 1, 1, 1
0, 1, 1, 1, 1, 1, 0
0, 0, 1, 1, 1, 0, 0
0, 0, 0, 1, 0, 0, 0

因為太長了,用換行字元切斷,不然放不下。

……拜託別這樣寫。