【數據蔣堂】第21期:常規遍曆語法

我們從簡單到複雜來考查遍曆運算中的可能情況,並討論SQL語法在這方麵的表現。
1. 直接針對集合成員運算
比如計算集合成員的合計。
這是最簡單的情況,采用普通的函數語法風格就可以,將待遍曆的集合作為參數獲得返回值,比如sum(A)用於計算集成A成員的合計,當然也可以使用對象式的語法風格寫成A.sum()。
2. 引用集合成員
比如我們不是要計算集合成員的合計,而是要計算平方和,那麼這個平方該如何描述?
這就會用到我們在談集合化語法時提到的lambda語法。平方這個運算本質上是一個函數,在遍曆過程中它以被遍曆集合的當前成員作為參數,返回該參數的平方。而lambda語法允許將這個函數以表達式的形式並一起寫入整個計算遍曆運算式,一個語句就可以完成。但這裏就有一個問題,我們在這個lambda表達式中用什麼標識符或符號表示這個當前成員呢?
顯然,象普通函數那個先定義參數名不是個好辦法,那會讓lamdba表達式寫得很臃腫,失去lambda語法的簡潔性。盡管有些程序設計語言確實是這麼做的,不過我們並不提倡。使用一個固定的標識符也不好,太長了用起來不方便,太短又很可能與其它局部變量重名導致歧義。我們提倡在這裏使用一個特殊符號來完成這個目的。
比如使用~表示當前成員時,平方和就可以寫成A.sum(~*~),簡單易懂。也可以分兩步做,先計算出集合成員的平方構成一個新集合,再計算新集合的合計,寫成類似A.(~*~).sum()的形式,後一步不再需要~寫法,前一步仍需要~寫法來描述平方這個表達式函數。
3. 使用結構化數據時引用字段
但是,我們發現,被認為是集合化語言的SQL中並沒有使用某個符號或標識符來表示當前遍曆成員,那麼SQL又是怎麼解決問題2的呢?
事實上,SQL並沒有普通意義上可由任何成員構成的集合。SQL的集合就是表,而表的成員都是相同結構的記錄。SQL體係中有記錄這個概念,但並不能把記錄作為一種數據類型來引用。如果我們要在SQL中針對一個單值成員的集合進行遍曆,也隻能把單值做成隻有一個字段的記錄,而針對這些記錄構成的表進行遍曆。所有計算都是針對某些字段進行的,而不能針對整條記錄。
但這和SQL沒有表示當前成員的符號有什麼關係呢?
我們在前麵說集合化語法時還提到,麵向結構化數據計算的集合化語法需要有簡潔的方式引用字段,SQL提供了可以直接引用字段的便捷機製,而SQL又隻能計算字段,那就可以不必再提供引用當前成員(記錄)的手段了。比如SQL中計算平方和一定是某個字段的平方和,而整條記錄(集合成員)的平方則沒有意義。
SQL犧牲了集合的表達能力而簡化了語法。對於能夠支持泛型成員構成集合的語言來講,~寫法就是必要的了。而且,如果用於結構化數據計算時,SQL這種可以直接字段的寫法也要得到支持才會方便,計算某銷售帳目的金額時寫成"~.單價*~.數量"顯然不如寫成"單價*數量“更為簡單直觀,好的程序語言應當借鑒SQL這種風格。
4. 嵌套引用時的規則
遍曆在本質上就是一個循環,而循環語句可能有多層,這樣遍曆也可能會有嵌套引用。比如計算A,B兩個集合的交集,簡單的算法就是遍曆A的成員,看是不是在B集合中出現過(也是遍曆),這就會涉及到兩層的遍曆。
這時候~寫法就會產生歧義了,~到底是指A集合還是B集合的當前成員,這需要在語法規則上做一個明確的約定。
一般采用的是就近原則,即如果沒有指明~是哪個集合的,那缺省認為是內層遍曆集合的,而外層遍曆集合的當前成員則需要顯式地指出其從屬於哪個集合。計算交集的表達式就可以寫成A.select(B.count(~==A.~)>0),其中的~缺省表示B的當前成員,而另一個要顯式地寫成A.~以示區分。
麵向結構化數據計算時可以直接引用字段名,這時也可能產生內外層的歧義,也可以適用於就近原則,SQL就是這樣。當內外層表有相同字段名時,則缺省被認為是內存表的字段,引用外層表的同名字段時必須顯式地寫上表名;如果內外存表中沒有相同字段名,則可以正確識別出來而不必書寫表名。
遍曆運算雖然很基本,但設計其語法時仍有一些注意事項。SQL在這方麵總體表現不錯,除了缺乏泛型成員的集合外,用於描述常規遍曆運算還是比較方便簡捷的。
最後更新:2017-09-06 13:04:02