2013-10-08 34 views
2
let rec remove x = function 
     y :: l when x = y -> l 
     |y :: l (* x <> y *) -> y :: remove x l 
     |[] -> [] 

該書說這個函數有一個問題:當找不到元素時,整個列表被不必要地複製。因此,給出以下改進版本。OCaml-Exception:控制執行流程以減少內存使用量

exception Unchanged 

    let rec remove_inner x = function 
     y :: l when x = y -> 
      l 
     |y :: l -> 
      y :: remove_inner x l 
     |[] -> 
     raise Unchanged 

    let remove x l = 
     try remove_inner x l with 
      Unchanged -> 
       l 

我不太明白這裏的意思。

回答

1

這基本上只是一個玩具的例子,以說明爲什麼你可能想要使用異常,所以沒有必要深入其中。

要理解這一點,你只需要認識到列表佔用的空間。所以,一個列表的兩個副本將佔用單個列表空間的兩倍。但在像OCaml這樣的函數式語言中,列表是不可變的。由於列表無法修改,列表與列表副本之間沒有可檢測到的差異。因此,如果您在兩個地方使用相同的列表,而不是實際複製列表,則可以節省空間而無需以任何方式更改程序的含義。

當您要求移除不存在的元素時,您顯示的代碼執行此操作。結果將與輸入相同,並且代碼確保結果與輸入(不是副本)相同。

請注意,節省的空間來自避免複製,而不是來自例外本身。但是,當這些列表不太長時,這段代碼很好而且很緊密。

我希望這會有所幫助。

(旁節點:在OCaml中,您可以使用==運算符實際檢測兩個列表之間的差異,這就是人們經常避免使用此運算符的原因;這會影響語言的功能純度。 )

+0

謝謝傑弗裏。 – user2821649

0

表達式y :: remove x l是複製發生的地方。這是因爲::是列表元素的內置[infix操作符]構造函數。

remove函數的幼稚版本中,操作者::用於構造具有原始列表的第一個除外相當於x參數中的所有元素的新列表。如果原始列表中沒有這樣的元素,那麼它仍然會創建新列表,並且它將成爲與原始列表等同的副本。

remove函數的「改進」版本調用另一個函數remove_inner,它在達到原始列表的末尾時會引發Unchanged異常,而未刪除元素。如果外部remove函數捕獲到Unchanged異常,則會返回原始列表。