2 資料(Data)
2.1 常見資料
不管是哪一種程式都會進行計算,而計算就會得到結果,結果以人類可以理解的方式呈現。為此大多數語言都設立了一些常用的資料表達方式,Racket 也不例外的支援了
整數
有理數
浮點數(沒有實數,因為不可數集合沒辦法建構出來。同理所以接下來的複數也是有限制的複數)
複數
布林值(Boolean)
字元
字串(用來表達文字,但因為程式本身也是文字嘛,所以用 quote 包起來避免混淆)
下面我們分數字、布林值跟文字來討論。在 terminal 裡面直接輸入 racket 會出現一個可以輸入運算式(expression)的文字交互環境,我們一般叫它 REPL,下面的程式是以含輸入提示符號(>)排列的。啟動 REPL 的畫面大概會長得像下面那樣(我從印出來的文字裡面拿掉版本資訊,不過執行之後應該就看得出自己有沒有弄對了)。
$ racket Welcome to Racket >
2.1.1 數字
2.1.1.1 整數
整數一如常見的理解,
> 1 1
> 1232131245 1232131245
> -99230193 -99230193
都是整數
2.1.1.2 有理數
Racket 亦支援有理數如
> 3/4 3/4
> 16066/207275633 16066/207275633
,而且它甚至會自動約分(笑)
2.1.1.3 浮點數
就像前面提過的,不可數集合沒辦法建構出來,所以用浮點數這種對於實數的近似值數值表現法來處理,寫成
> 2.4214 2.4214
> 8.34155152 8.34155152
等,也有支援常用的特殊值的近似,如
> (require racket/math) > pi 3.141592653589793
2.1.1.4 複數
除開實數是採用浮點數近似,複數跟在高中學習到的東西基本上是一樣的
> 5+2i 5+2i
> 10.0+3.412i 10.0+3.412i
2.1.1.5 exercise
現在讓我們實際使用數字看看
50 分鐘是幾秒?
8 公里是幾公尺?
2.1.2 布林值(Boolean)
布林值也是現代人熟知的資料之一了,用於真假運算上,在 Racket 中寫法比較獨特
> "#t" "#t"
> "\n" "\n"
> "#f" "#f"
大寫不改變其意
> "#T" "#T"
> "\n" "\n"
> "#F" "#F"
2.1.3 文字
2.1.3.1 字元
字元用 #\ 作為前綴,在後面接上想要的字元就是該字元
> #\c #\c
> #\a #\a
> #\0 #\0
> #\\ #\\
,而 Racket 也支援一些控制字元(鍵盤上的特殊功能鍵)如
> #\return #\return
> #\tab #\tab
2.1.3.2 字串
現在可以來說明為什麼需要 quote 了,假設我們需要一串字叫 hello,我們的語言就寫
hello
所以列印 hello 寫成
(display hello)
可是變數也是
hello
那我們怎麼知道
(display hello)
的 hello 到底是什麼?乃至 (display hello) 到底是變數還是字串?所以實際上我們用 " 把字串包起來,寫成
(display "hello")
但我們用了 " 之後,要怎麼在字串裡面表示 "?所以有所謂的轉義字符(escape character),例如 "hello" 就可以寫成
"\"hello\""
2.2 Racket 特有的資料
雖說是特有概念但其實很多概念是繼承自 Lisp/Scheme 的,包含
Symbol
Keyword
Pairs and Lists
2.2.1 Symbol
前面加了 ' 的 identifier 就會生成 Symbol 的值(所以也可以說 symbol 就是 quoted identifier),例如:
> 'a 'a
> (symbol? 'a) #t
第二句 (symbol? 'a) 可以讀作:'a 是 symbol 嗎?
我們還可以詢問兩個 symbol 是否相等:
> (eq? 'a 'a) #t
> (eq? 'a 'abc) #f
> (eq? 'a 'ABC) #f
還可以把字串轉換成 symbol:
> (string->symbol "a") 'a
除了以下字元跟空白符號之外的字元都是合法的 identifier 字元(合法在這裡指可以出現在 identifier 內):
( ) [ ] { } " , ' ` ; # | \
除此之外
# 只有在開頭不被允許,但 #% 則允許,所以 #a 不是 identifier 但 #%a 和 a# 是 identifier。
. 本身不是 identifier
而如果真的想要包含非法字元,可以用 | 包起來或是用 \ 前綴逃脫,所以以下都是合法的 symbol
'\#a
'|#a|
'\.
'|{|
2.2.2 Keyword
Keyword 跟 Symbol 很像,只是前綴是 #:,例如:
> '#:apple '#:apple
> (eq? '#:apple (string->keyword "apple")) #t
keyword 主要是用在命名參數(或是帶名參數)上,讀者暫時可以先不用管這是什麼意思,講到函數(function)時會更深入一點介紹命名參數是什麼。這裏先讓讀者看 with-output-to-file 怎麼用到 keyword 的:
(with-output-to-file (build-path (current-directory) "stuff.txt") (lambda () (printf "example\n")) #:mode 'text #:exists 'replace)
2.2.3 Pair 與 List
Pair 與 List 是 Racket 中的程式、也是資料,這種對應使得我們有能力操作製作程式的程式,但現在我們不需要關心這個進階功能。首先我們看一些 Pair 的實例:
> '(1 . 2) '(1 . 2)
> '(a . b) '(a . b)
> '(a b . c) '(a b . c)
事實上 '(a b . c) 是 '(a . (b . c)) 的簡寫。因此我們可以說 Pair 就是擁有兩個「內容」的結構,內容可以是任何資料。我們可以用 car(取得左邊)和 cdr(取得右邊)存取 Pair 的內容,因此:
> (car '(a . b)) 'a
> (cdr '(a . b)) 'b
> (cdr '(a b . c)) '(b . c)
List 是特殊的 Pair,當一個 Pair 的右側是空的 Pair () 時,該 Pair 就是一個 List,並且空 Pair 本身也是 List:
> (list? '()) #t
> (cdr '(a)) '()
> (cddr '(a b)) '()
有解構的方式,Pair 也有建構的方式,字面值語法 (1 . 2) 無法和函數呼叫(之後才會提到)語法區隔,因此我們得寫成 '(1 . 2),但我們也可以改用其建構函數 cons:
> (cons 1 2) '(1 . 2)
> (cons 'a 'b) '(a . b)
> (cons 'a (cons 'b 'c)) '(a b . c)
然而最常見的作法還是用 quote 去建立 List,我們需要系統性的了解它
Racket 裡面所有東西都是 list 跟 atom,Racket 會去執行它
quote/' 就是叫 Racket 不要執行內容
quasiquote/` 還是 quote,但預期可能會出現 unquote/,,unquote 會讓 Racket 執行接續的 s-expression
unquote-splicing/,@ 是說,不止要執行,還要展開到上一層
你已經對 Pair 和 List 有足夠的了解,練習看看吧
2.2.3.1 exercise
2.3 更複雜的資料
這節是講述一些相對複雜的資料,只是留作參考,對學習的影響不大,讀者可以跳到計算規則的部分。
Vectors
Hash Tables
Boxes
Void and Undefined