天天看點

泛函程式設計(12)-資料流-Stream

   在前面的章節中我們介紹了list,也讨論了list的資料結構和操作函數。list這個東西從外表看上去挺美,但在現實中使用起來卻可能很不實在。為什麼?有兩方面:其一,我們可以發現所有list的操作都是在記憶體中進行的,要求list中的所有元素都必須在操作時存在于記憶體裡。如果必須針對大型資料集進行list操作的話就明顯不切實際了。其二,list的抽象算法如折疊算法、map, flatmap等是無法中途跳出的,無論如何都一直進行到底;隻有通過遞歸算法在才能在中途停止運算。但遞歸算法不夠抽象,經常出現重複的代碼。最要命的是遞歸算法會随着資料量增加堆棧記憶體占用(non-tail-recursive),處理大型資料集同樣不實際。以上缺陷使list的應用被局限在小規模的資料集處理範圍。

    沖突的是,list由于記憶體占用問題不适合大資料集處理,但它的計算模式又是排列資料模式必須的選擇。stream資料類型具備了list的排列資料計算模式但有不需要将全部資料搬到記憶體裡,可以解決以上提到的大資料集處理問題。stream的特性是通過“延後計算”(lazy evaluation)來實作的。可以想象一下可能的原理:stream内元素讀取是在具體使用時才進行的。不用說,stream是典型的隻讀資料類型。既然要繼承list的計算模式,那麼在結構設計上是否相同呢?我們先看看stream的結構設計:

天啊,簡直是活脫脫的list結構嘛。不過stream的頭元素(head)和無頭尾(tail)是延後計算的(non-strict)。由于cons不是普通函數而是一個類,不容許延後計算類參數,是以傳入的是一個函數 () => ???。

以上stream結構設計與list相同;兩種狀态是用子類來表示的。以下我們探索以下另外一種設計方案:

以上的設計方案采用了結構封裝形式:資料結構uncons,兩種狀态empty, cons都被封裝在類結構裡。最起碼我們現在可以直接使用=> a 來表達延後計算參數了。

實際上stream就是對一個list的描述,一個類型的聲明。它的執行個體生成延後到了具體使用的時候,此時需要的元素已經搬入記憶體,成了貨真價實的list了:

看看,stream(1,2,3)就是一個聲明。我們通過list轉換才真正産生了執行個體。

再看看stream最基本的一些操作函數:

從操作結果可以确定:stream的操作也都是對操作的描述,是延後計算的。當元素被搬到list時系統才回真正計算這些stream元素的值。

不過這些操作函數的實作方式與list基本相像:

前面提到過list的折疊算法無法着中途跳出,而stream通過“延後計算”(lazy evaluation)是可以實作提早終結計算的。我們先看看stream的右折疊(foldright)算法:

這個與list的foldright簡直一模樣嘛,不同的隻有op函數的第二個參數是延後計算的 => b。秘密就在這個延後計算的b上。看看下面圖示:

泛函程式設計(12)-資料流-Stream

由于op的第二個參數b是延後計算的,那麼t.foldright(z)(op)這個表達式的計算就是延後的,系統可以決定先不計算這個表達式進而得到了一個中間停頓的結果。

函數exists是在碰到第一個符合條件的元素時馬上終止的。我們通常使用遞歸算法來實作exists的這個特性。現在我們也可以用右折疊算法達到同樣效果:

注意:當p(a)=true時系統不再運算b,是以整個運算停了下來。

同樣,用foldright來實作forall:

當我們遇到資料結構隻能存一個元素如option,either時我們用map2來對接兩個結構。當我們遇到能存多個元素的資料結構如list,tree時我們就會用append來對接。stream是一個多元素的資料結構,我們需要實作append:

标準裝備函數實作:

看來都備齊了。

我們再看看list與stream還有什麼别的值得關注的差別。先從一個list操作的例子開始:

根據list的特性,每個操作都會立即完成,産生一個結果list,然後接着下一個操作。我們試着約化:

實際上這個運算周遊(traverse)了list三次。一次map操作産生了中間list(11,12,13,14),二次操作filter産生了list(12,14),三次操作map産生最終結果list(36,42)。實際上我們如果把周遊這個list的方式變一下:變成每次走一個元素,連續對這個元素進行三次操作,直到走完整個list。這樣我們在一個周遊過程就可以完成全部三個操作。stream恰好是一個元素一個元素走的,因為下面的元素處于延後計算狀态。我們試着用stream來證明:

以上的#::是cons的操作符号。

上一篇: 初始化

繼續閱讀