1.7 弱首範式
我們現在可把第1.1 節中提及的求值與範式談得更仔細些。
第一次閱讀的讀者可把本節跳過。
回顧 fst 使用樣式配對的定義 fst (x,y) = x.
假設我們把 swap 定義如下:
swap p = (snd p, fst p)
考慮 fst (swap (3,'a')) 該怎麼求值:
fst (swap (3,'a'))
= {- swap 的定義 -}
fst (snd (3,'a'), fst (3,'a'))
= {- fst 的定義 -}
snd (3,'a')
= {- snd 的定義 -}
'a'
在第一步中,由於 fst 使用樣式配對,我們得先把 swap (3,'a') 算出來。
若把 swap (3,'a') 算到底,得到的範式是 ('a',3).
但如第一步中所顯示,如果目的只是為了配對 (x,y) 這個樣式,
我們並不需要把 swap (3,'a') 算完,只需算到 (snd (3,'a'), fst (3,'a'))
即可 --- x 可對應到 snd (3,'a'), y 可對應到 fst (3,'a'),
fst 的計算便可以進行。在下一步中,子算式 fst (3,'a') 便被丟棄了,
並沒有必要算出來。
做樣式配對時,Haskell 只會把算式歸約到剛好足以與樣式配對上的程度。
當樣式的深度只有一層(如(x,y))時,與之配對的式子會被歸約成一種
稱作弱首範式(weak head normal form)%
的形式。
弱首範式有其嚴格定義,但本書讀者只需知道:算式會被歸約到露出最外面的建構元,(在此例中是被歸約成 (_,_) 的形式),然後便停下來。
例 1.14
回顧 swap 的兩種定義方式,分別命名為
swap1 (x,y) = (y,x)
swap2 p = (snd p, fst p)
若考慮不終止的參數,兩者的行為並不盡然相同。
定義 three (x,y) = 3, 並
假設 undefined 是一個沒有範式的式子 --- 一旦開始對 undefined 求值,便停不下來。
試計算 three (swap1 undefined) 和 three (swap2 undefined).
答案
由於 three 使用樣式配對,計算 three (swap1 undefined) 時得把 swap1 undefined 歸約成弱首範式。
同理,計算 swap1 undefined 時,第一步便是先試圖把 undefined 歸約成弱首範式,然後便停不下來了。
至於 three (swap2 undefined) 則可如下地求值:
three (swap2 undefined)
= {- swap2 之定義 -}
three (snd undefined, fst undefined)
= {- three 之定義 -}
3
第一步中,swap2 undefined 則依照範式順序求值的原則展開為 (snd undefined, fst undefined) --- 這是一個序對,只是該序對含有兩個沒有範式的元素。
該序對可對應到樣式 (x,y), 因此整個式子歸約為 3.
附帶一提,three undefined 是一個不停止的計算。