1 值、函數、與定義

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 是一個不停止的計算。