7 區段問題

一個串列中連續的任意一截被稱作一個「區段」(segment)。 例如,[1,2,3] 的區段包括[],[1],[2],[3],[1,2],[2,3], 以及 [1,2,3]本身(注意:空串列也是一個區段)。 也許因為歷史因素,許多有趣的演算法問題有這樣的形式: 給定一個串列 xs :: List A,一個述語 p :: List A -> Bool, 和一個函數 f :: List A -> B,我們想在 xs 的所有區段中,找出滿足述語 p 並使得 f 值最大的那個。 我們把這類問題統稱為「區段問題」。

本節討論一些簡單的區段問題。我們先回顧一下區段的形式定義。 第2.6.3 節曾提及,下述函數 segments 算出一個串列的所有區段:

segments :: List a -> List (List a)
segments = concat . map inits . tails 

其中 tails :: List a -> List (List a) 計算一個串列所有的後段(suffixes),例如 tails [1,2,3] = [[1,2,3],[2,3],[3],[]]; 函數 inits :: List a -> List (List a) 則計算一個串列的所有前段(prefixes),例如 inits [1,2,3] = [[],[1],[1,2],[1,2,3]]. 對每一個後段,計算所有的前段,便得到所有的區段了。 我們也可以反過來,定義 segments = concat . map tails . inits. 本節中的所有推導與證明可調整成相對應的版本。 事實上,如此的定義之下 segments [1,2,3] 會將空串列傳回很多次。 不過以本章的目的而言,我們對這些重複的 [] 並不在意。

為方便讀者,我們將 inits 與 tails 的歸納定義重複如下。

inits :: List a -> List (List a)
inits []      = [[]]
inits (x:xs)  = [] : map (x:) (inits xs) 
tails :: List a -> List (List a)
tails []      = [[]]
tails (x:xs)  = (x:xs) : tails xs