On this page:
2.1 常見資料
2.1.1 數字
2.1.1.1 整數
2.1.1.2 有理數
2.1.1.3 浮點數
2.1.1.4 複數
2.1.1.5 exercise
2.1.2 布林值(Boolean)
2.1.3 文字
2.1.3.1 字元
2.1.3.2 字串
2.2 Racket 特有的資料
2.2.1 Symbol
2.2.2 Keyword
2.2.3 Pair 與 List
2.2.3.1 exercise
2.3 更複雜的資料
2.3.1 Vector
2.3.2 Hash Table
2.3.3 Box
2.3.4 Void and Undefined
8.6

2 資料(Data)

2.1 常見資料

不管是哪一種程式都會進行計算,而計算就會得到結果,結果以人類可以理解的方式呈現。為此大多數語言都設立了一些常用的資料表達方式,Racket 也不例外的支援了

下面我們分數字、布林值跟文字來討論。在 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

現在讓我們實際使用數字看看

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 的,包含

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 內):

( ) [ ] { } " , ' ` ; # | \

除此之外

而如果真的想要包含非法字元,可以用 | 包起來或是用 \ 前綴逃脫,所以以下都是合法的 symbol

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,我們需要系統性的了解它

  1. Racket 裡面所有東西都是 list 跟 atom,Racket 會去執行它

  2. quote/' 就是叫 Racket 不要執行內容

  3. quasiquote/` 還是 quote,但預期可能會出現 unquote/,,unquote 會讓 Racket 執行接續的 s-expression

  4. unquote-splicing/,@ 是說,不止要執行,還要展開到上一層

你已經對 Pair 和 List 有足夠的了解,練習看看吧

2.2.3.1 exercise

2.3 更複雜的資料

這節是講述一些相對複雜的資料,只是留作參考,對學習的影響不大,讀者可以跳到計算規則的部分。

2.3.1 Vector
2.3.2 Hash Table
2.3.3 Box
2.3.4 Void and Undefined