跳轉至內容

程式語言導論/模板化程式設計

來自華夏公益教科書,自由的教科書,為一個開放的世界

模板化程式設計

[編輯 | 編輯原始碼]

高階函式促成了一種我們稱之為模板化的程式設計風格。一個模板是一個帶有"空洞"的演算法。這些空洞必須用正確型別的操作填充。演算法的骨架是固定的;但是,透過使用不同的操作,我們可以獲得截然不同的行為。例如,讓我們考慮 filter 的 SML 實現

fun filter _ nil = nil
  | filter p (h::t) = if p h then h :: (filter p t) else filter p t

實現 filter 的演算法必須對列表中的每個元素應用給定的單目運算子 p。然而,無論操作是什麼,必須遵循的步驟總是相同的:遍歷列表,對每個元素應用p。這個過程是一個模板,一個必須用實際操作填充才能工作的骨架。這個骨架可以與各種不同的運算子一起使用;因此,它具有很高的可重用性。

這種程式設計風格遵循開閉原則,通常在面向物件程式設計的背景下提及。filter 的實現是封閉的。換句話說,它可以與其他模組連結並使用,無需任何修改。但是,這種實現也對擴充套件開放。只要這些操作服從 filter 強制執行的型別規則,就可以將新的操作傳遞給該演算法。即使我們假設將來可能會實現新的操作,只要這些操作符合 filter 強制執行的型別契約,filter 就可以在無需修改的情況下使用。

模板和偏應用的結合使程式設計師能夠建立一些非常優雅的程式碼。例如,下面我們看到 SML 中快速排序演算法的實現。在這個例子中,函式 grt 被用作函式工廠。每次必須處理新的樞軸時,即列表的第一個元素,我們透過呼叫 grt h 建立一個新的比較函式。如果我們讓比較操作(在本例中為大於)開放,我們可以使該函式更加通用。透過傳遞不同的運算子,比如小於,我們將獲得一個演算法,該演算法以降序而不是升序對整數進行排序。

fun grt a b = a > b
 
fun qsort nil = nil
  | qsort (h::t) = (qsort (filter (grt h) t)) @ [h] @ (qsort (filter (leq h) t))

沒有高階函式的模板

[編輯 | 編輯原始碼]

一些程式語言不提供高階函式。這個家族中最著名的成員是Java。然而,模板也可以在這門語言中實現。Java 透過繼承子型別多型性的強大組合來彌補缺乏高階函式的不足。例如,我們將展示如何在 Java 中實現 map 骨架。下面的程式碼是一個抽象類Mapper 定義了一個必須由擴充套件它的類實現的方法 applyMapper 還定義了一個具體的方法 map。該方法完全實現了對映演算法,並在其主體內部呼叫 apply。但是,apply 的實現是開放的。

import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;

public abstract class Mapper<A, B> {

  public abstract B apply(A e);

  public final List<B> map(final List<A> l) {
    List<B> retList = new LinkedList<B>();
    Iterator<A> it = l.iterator();
    while (it.hasNext()) {
      retList.add(apply(it.next()));
    }
    return retList;
  }
}

為了使用這個骨架,開發人員必須透過稱為繼承的機制來擴充套件它。如果類A擴充套件了另一個類B,那麼我們稱AB子類。例如,下面的類是Mapper 的一個子類,它實現了一個將列表元素增量化的函式

public class Incrementer extends Mapper<Integer, Integer> {
  @Override
  public Integer apply(Integer e) { return e + 1; }
}

Incrementer 將一個整數列表對映到一個新的整數列表。下面的程式碼片段演示瞭如何使用這個類的例項來遞增整數列表中的每個元素。如我們所見,在沒有高階函式的語言中模擬模板的整體過程相當冗長。

    List<Integer> l0 = new LinkedList<Integer>();
    for (int i = 0; i < 16384; i++) {
      l0.add(i);
    }
    Mapper<Integer, Integer> m = new Incrementer();
    List<Integer> l1 = m.map(l0);

顯著的高階函式 · 定義和作用域型別

華夏公益教科書