XSLT 是圖靈完成的(Turing complete)。也就是說,如果有足夠的內存,那麼 XSLT 可以完成其他任何圖靈完成語言(如 C++)所能完成的計算。對於屬性更傳統的語言的程序員來說,這可能有點奇怪。畢竟 XSLT 缺少對很多算法來說極其重要的特性,其中包括循環和可變的變量。
注意:XSLT 所謂的變量在其他多數語言中稱為常量。它們更像是代數變量而不是傳統的編程變量。
函數式編程
上述的遺漏並非疏忽所致。XSLT 是一種函數式語言而不是過程性語言。在 C 或 Pascal 這樣的過程性語言中,程序被定義成一系列的步驟,這些步驟按照規定的順序執行,並在最後一步產生最終結果。在函數式語言中,程序被定義成由其他函數組成的函數,函數求值形成最終的結果。函數式語言的最大優點是執行的順序無關緊要。作為一個簡單的例子,考慮下面兩個(代數)函數:
f(x) = 2*x
g(x) = x - 3
設函數 h(x) 是 f 與 g 的復合函數:
h(x) = f(g(x))
對該函數求值可以先計算 g:
h(x) = f(x - 3) = 2 * (x - 3) = 2x - 6
也可以先計算 f:
h(x) = 2 * g(x) = 2 * (x - 3) = 2x - 6
兩者的結果是一樣的。語言的函數式使其更適合並行處理,因為問題的多個部分可以同時計算,無需擔心其中的一部分要先於其他部分計算。線程安全是自動實現的。
函數式語言包括 XSLT,但不能包含傳統的循環,因為循環在時間上是有序的。就是說,典型循環的編寫和編譯必須保證 i==1 出現在 i==2 之前。當然,也可以反向而不是正向運行循環,或者使用 1 之外的循環計數器增量,甚至像 while 語句那樣完全取消循環計數器。但是無論什麼類型的循環,執行的順序都至關重要,這一點與函數式編程正好相反。
遞歸
函數式編程中,傳統語言中用循環完成的多數任務都可以使用遞歸來完成。參數代替了變量。比如,最近有人問我如何輸出在編譯時不知道數量的點(句點)。比方說,格式化菜單時可能要用到,因為在菜名和價格之間常常需要不同數量的點:
Crawfish Etoufee.......$9.95
FrIEd Chicken..........$6.95
在 C 語言中可以編寫一個簡單的函數:
void printDots(int n) {
int i;
for (i = 0; i < n; i++) {
printf(".");
}
}
但是這並不是解決問題的惟一辦法。可以用遞歸代替循環,比如:
void printDotsRecursively(int n) {
if (n > 0) {
printf(".");
printDots(n-1);
}
}
在 C 中很少這樣做,但是在 XSLT 中,這是惟一的辦法。
下面的模板准確地生成 count 參數所傳遞的數量的點。邏輯很簡單:如果 $count 的值大於零,就輸出一個點,將 count 參數減去 1 後再調用這個函數;否則什麼也不做。這與 printDotsRecursively 函數采用的算法基本相同,只不過是用 XSLT 而非 C 實現的:
<xsl:template name="dots">
<xsl:param name="count" select="1"/>
<xsl:if test="$count > 0">
<xsl:text>.</xsl:text>
<xsl:call-template name="dots">
<xsl:with-param name="count" select="$count - 1"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
比如要輸出 100 個點,用 100 count 參數值調用該函數:
<xsl:call-template name="dots">
<xsl:with-param name="count" select="100"/>
</xsl:call-template>
如果不希望傳遞一個常數值,那麼還可以根據其他數據計算要打印的點數。比如,下面的指令分別計算價格和菜名的長度(更具體地說,是上下文節點中 price 和 entree 子元素的 string-values 函數值)後,輸出足夠做的點數將每個菜單行填充到 80 個字符:
<xsl:call-template name="dots">
<xsl:with-param name="count"
select="80 - string-length(entree) - string-length(price)"/>
</xsl:call-template>
結束語
無論是在 C、XSLT 中,還是在 Scheme 中,都應用了使用遞歸代替循環。但是這種技術非常優雅。在 XSLT 中不需要經常使用這種技術,但使用這種技術可以完成用其他任何標准 XSLT 技術都做不到的技巧性任務。