2016-01-14 56 views
4

Common Lisp的最佳做法,我有合併兩個符號有序列表一個Common Lisp的功能,無需重複(兩個有序集):使用類型聲明進行優化

(defun my-merge (x y) 
    "merge two lists of symbols *already sorted and without duplicates* 
    (and return the resulting list sorted and without duplicates)" 
    (let* ((first (cons nil nil)) 
     (last first)) 
    (loop while (and x y) 
     for cx = (car x) 
     for cy = (car y) 
     if (string= cx cy) 
     do (setf x (cdr x)) 
     else if (string< cx cy) 
     do (rplacd last (cons cx nil)) 
     and do (setf last (cdr last) 
        x (cdr x)) 
     else do (rplacd last (cons cy nil)) 
     and do (setf last (cdr last) 
        y (cdr y))) 
    (rplacd last (or x y)) 
    (cdr first))) 

既然我已經發現了大約只有很少的信息爲了有效地編譯代碼的使用實際情況類型聲明的,我不能確定它是否足夠以這種方式聲明變量,例如:

(defun my-merge (x y) 
    "merge two list of symbols *already sorted and without duplicates*" 
    (declare (list x y)) 
    (let* ((first (cons nil nil)) 
     (last first)) 
    (declare (cons first last)) 
    (loop while (and x y) 
     for cx symbol = (car x) 
     for cy symbol = (car y) 
     ... 

,或者因爲我想,如果是還需要添加the說明符到我的代碼?但那麼,其中哪些情況下應該加嗎?

有一些規則可以遵循?

我是否還應該爲我的優化目的聲明我的函數的類型?

+2

優化,使用典型值的優化和類型推斷的聲明完全是特定於實現的。你會在實現中找到所有的東西:從基於類型聲明的零優化,使用類型聲明,甚至是*類型推斷*(這意味着需要更少的類型聲明)。通常:聲明類型並進行優化,然後查看生成的彙編程序和/或測量時間。如果你使用SBCL,CMUCL或者LispWorks,編譯器會(或者可以配置爲)告訴你它不能優化的地方以及爲什麼。然後會添加必要的聲明。 –

+0

推出自己的解決方案總是很有趣,但請注意,最好的選擇之一可能就是使用標準[** merge **](http://www.lispworks.com/documentation/HyperSpec/Body/) f_merge.htm)函數。它可以產生任意的序列類型,採用任意的謂詞等,並且實現可能已經優化了它(可能值得查看它們的來源)。 –

+0

@JoshuaTaylor,這是我的第一選擇,但後來我測試了SBCL和CCL的合併函數,它們是我目前使用的實現,並且* both *在大型列表上的性能明顯最差(不記得確切的數字)。我也不知道其中的原因,或者我在執行測試時做了錯誤。 – Renzo

回答

8

風格

既然你不實際使用擴展LOOP功能以任何有用的方式和LOOP語法是不是你的榜樣,偉大的,我建議把它與原始LOOP寫。瞭解如何COND使其更具可讀性的Lisp的程序員:

(defun my-merge (x y &aux (first (list nil)) (last first) cx cy) 
    (macrolet ((cdr! (v) 
       `(setf ,v (cdr ,v)))) 
    (loop (unless (and x y) 
      (return)) 
      (setf cx (car x) cy (car y)) 
      (cond ((string= cx cy) 
       (cdr! x)) 
       ((string< cx cy) 
       (rplacd last (list cx)) 
       (cdr! last) 
       (cdr! x)) 
       (t 
       (rplacd last (list cy)) 
       (cdr! last) 
       (cdr! y)))) 
    (rplacd last (or x y)) 
    (cdr first))) 

編譯

由於編譯器的複雜程度:

  • 完全傻=編譯器會忽略所有聲明 - >聲明沒有幫助

  • 大多是愚蠢的=編譯器ne編輯聲明無處不在,但優化 - >你需要編寫大量聲明

例如:

(let ((a 1) (b 2)) 
    (declare (integer a b)) 
    (let ((c (the integer (* (the integer (+ a b)) 
          (the integer (- a b)))))) 
    (declare (integer c)) 
    (the integer (* c c)))) 

注意,這可能還不夠了解的參數類型是什麼,它可能是必要的宣佈結果的類型。因此使用theDISASSEMBLE和分析器是你的朋友。

  • basic =編譯器需要類型聲明,優化,但也可以推斷某些類型。標準語言的類型是已知的。

更好的編譯器抱怨類型錯誤,可以跨函數傳播類型,並且可以在某些優化不可能的時候發生抱怨。

順序功能

注意序列功能是一種特殊的情況下艱難。 序列具有作爲亞型列表載體(包括字符串)。

比方說,一個序列的功能是:

(foo result-type sequence-1 sequence-2 fn) 
  • 如果序列是同類型的,人們可能會想有一個優化的代碼版本列表,另一個用於載體。

  • 如果序列是不同類型的,則將一個序列轉換爲不同類型可能是有用的。也許不會。

  • 結果類型也具有影響,這取決於結果類型,不同的算法是可能的/必要

所以自由度是相當高的。編譯器可能有助於快速編寫代碼。但是特定序列函數的實現也許能夠在運行時進行一些優化。

然後fn是一個接受元素併產生新元素的函數。知道它的類型簽名可能會有幫助 - 不然。

我真的不知道哪個當前的Common Lisp具有複雜的序列函數實現。雖然我記得Symbolics Common Lisp的實現給了它一些努力。

文檔和文件

通常什麼編譯器可以優化以及如何沒有很好的記載,如果在所有。有一些關於這個話題的論文,但是他們經常是老的和/或過時的。

+0

真的很棒的答案,非常感謝您的代碼,以及解釋和所有鏈接!頭腦的食物。 – Renzo