iT邦幫忙

2018 iT 邦幫忙鐵人賽
DAY 3
0
Software Development

每天 Racket 3 分鐘系列 第 3

(define day-02 "Racket 不會咬人 — define and REPL")

  • 分享至 

  • xImage
  •  

度過了兩天沒有程式碼的內容,從這一篇開始,我們寫下人生第一個 s 表示式。

1. 來吧,寫下你的第一個 Racket 程式

我們先叫出 DrRacket,然後在上方的程式碼編輯區寫一些簡單的東西:

1 + 1

然後在右上方按下運行(Run)。有發現什麼嗎?下方的 REPL 出現了以下訊息:

1
#<procedure:+>
1

奇怪了,1 + 1 不是等於 2 嗎?

要解釋這件事之前,我們先解釋一下,程式怎麼被解讀的。

假設我們剛剛的 1 + 1 要 assign 給一個變數 a:

a =  1 + 1

然而,不管或大或小的程式,進到執行階段,都必須被轉變成為一種特殊的結構,它看起來像是一棵樹,像這樣子:

ast

有的人的畫法是把操作動作放在另一側,像這樣:

ast2

再透過尋訪語法樹的過程,將值求出。因此,我們將上述的算式用小括號括起來:

(a = (1 + 1))

然後,這步是重點:將操作子搬到前面:

(= a (+ 1 1))

最後,因為等號在早期的語言是一種比較兩個值的操作,因此把 = 換成 define 如下:

(define a (+ 1 1))

按下運行前,先再加一行:

(define a (+ 1 1))
a  ;; 加上這一行

這時你會看到下方 REPL 出現了正確的結果。

2. 沒有魔法,只有 s 表示式

而每一種語言,都有它特殊的語法,例如 Perl 裡頭,正規表示式可以當作一般語法使用,或在 Ruby,任何一個元素都是物件,因此數字可以直接用 . 來呼叫物件內部的 method。甚至,PostgreSQL 的字串相連,使用 ||,但在 Haskell,卻是使用 ++

但是,在 s 表示式裡,沒有這種東西!因為 s 表示式的語法,直接地對應到程式的語法樹,並且所有的操作全部都放在第一個位置,後面接的是參數或定義內容。因此,在閱讀這類語言時,你起初會覺得很痛苦,為什麼東西包了一層又一層,在寫的時候,會覺得很繞舌,為什麼每一個操作都要有相對應的 function。但是,一旦習慣以後,就像學會九陽神功,打通任督二脈的張無忌,你便能快速掌握每一個語言它語法與操作的背後對應到的思想。從語法、語義,到函式的各種轉換,再加上每種語言都有其特定的慣例用法,稍微了解後,就能夠快速轉換到其他語言。

Lisp 家族衍生出來的 s 表示式語言都有著這樣的特性:語法中沒有魔法,沒有約定俗成,沒有優先序。

3. 一個 define 走遍天下

稍微介紹完 s 表示式與程式語言的關係後,我們來介紹 define 的作用。Racket 要使用任何變數,一定要先宣告,而宣告的關鍵字,就是 define,如下:

(define a "a")

define 的第一個參數,就是變數名稱,這是一個固定的用法。然而,在正規的程式語言詞彙裡,不叫變數名稱,叫識別子: identifier ,我們以下簡稱 id 。第二個參數,就是要賦予前面 id 的值。這個值可以是數字、字串、boolean 值,或者是函式。函式部份,我們後續會說明。

此外,Racket 是一個支援多回傳值的語言,這是我在 Java 寫這麼久以來,最想要的語法特性。回傳值目前還沒說到,但我們可以用這個機制,一次宣告多個 id。這回,是使用 define 的擴充形式:

(define-values (a b c) (values 1 2 3))
(+ a b c)

使用 define-values,後面接 values,可以一次 assign 多個 id 的值。但在此必須注意,在 s 表示式語言裡,() 所含括起來的範圍,稱為 form ,每一個 form 所使用的 () 不能多,也不能少。例如不能這樣寫:

(define-values a b c (values (1 2 3)))

有個 C 語言的笑話說,當你的 C 程式無法執行時,在出錯的地方加上 * 或拿掉 *,或用 &,或許它就能執行了。

Lisp 家族的語言也是這樣,當你的 Racket 程式無法執行時,在出錯的地方加上 ) 或拿掉 ),或許它就能執行了。

4. REPL 是你的好朋友

DrRacket 下方所附的 REPL 很特別,跟你若用 Racket command line 工具所用的 REPL 不太一樣。最大的差別在於,DrRacket 的 REPL 可以讓你即時與上方寫的程式互動,但若這程式用 Racket 的 REPL 來開啟,就沒辦法這樣輕易地可以呼叫每個內部的元素。這是 DrRacket 的特點,這 REPL 會是你接下來使用這個語言的好朋友。例如當你上方已經這樣寫:

(define a (+ 1 1))

按下運行後,在下方 REPL 可以輸入:

(+ a 123)

可以直接得到:

125

如果需要即時驗證自己寫的程式,這是一個很方便的機制!


上一篇
(define day-01 "起手式 — Racket 安裝與編輯環境")
下一篇
(define day-0.30000000000000004 (+ 0.1 0.2)) ;; Racket 的數值系統
系列文
每天 Racket 3 分鐘17
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言