2016-01-23 72 views
0

我正在通過SICP,對於象徵性的區別,我提出了兩種方法來取得任意數量的參數。哪個比較習慣方案?

一個很多爭論收益款項:

(make-sum 1 'x 4) -> (+ 4 'x)

(make-sum '(+ 3 x) (** x 2) 5) -> (+ 8 x (** x 2))

,另一種則:

(make-sum 1 'x 4) ->(+ 1 (+ 'x 4))

(make-sum '(+ 3 x) (** x 2) 5) -> (+ (+3 x) (+ (** x 2) 5))

請注意,兩個make-sum都不完全正常工作。我的問題不在於如何解決這些問題。

首先make-sum

(define (make-sum . ad) 
    #|goes through the list ad 
    and adds up the numbers, while 
    putting non-numeric terms into 
    a separate list, returning a new 
    list with the sum and non-numeric 
    term list|# 
    (define (accumulate-terms lst) 
    (define (accumulate-terms-help lst num-sum other-terms) 
     (cond ((null? lst) `(,@other-terms ,num-sum)) 
      ((number? (car lst)) 
      (accumulate-terms-help (cdr lst) 
           (+ num-sum (car lst)) 
           other-terms)) 
      (else (accumulate-terms-help (cdr lst) 
            num-sum 
         (cons (car lst) other-terms))))) 
    (accumulate-terms-help lst 0 '())) 

    #|modified flatten that only flattens 
    sub-lists that have sym as their first element|# 
    (define (flatten lst sym) 
    (cond ((eq? lst '()) '()) 
      ((list? (car lst)) 
      (cond ((eq? (caar lst) sym) 
      (append (flatten (car lst) sym) (flatten (cdr lst) sym))) 
      (else (cons (car lst) (flatten (cdr lst) sym)))))  
      (else 
      (cons (car lst) (flatten (cdr lst) sym))))) 

    #|flattens out all addition sub terms 
    accumulates what is left and then filters 
    any zeroes|# 
    (let*() 
    (define ret 
     (filter (lambda (p) 
      (not (eq? p 0))) 
       (cond ((> (length ad) 1) 
       `(+ ,@(accumulate-terms 
        (filter (lambda (q) 
          (not (eq? q '+))) 
         (flatten ad '+))))) 
        (else ad)))) 

    (cond ((> (length ret) 2) 
      ret) 
      (else (cadr ret))))) 

第二補充和

(define (make-sum . an) 
    (cond 
    ((equal? (length an) 1) 
     (let ((it (car an))) 
     (cond 
      ((number? it) it) 
      ((variable? it) `',it) 
      ((sum? it) (eval `(make-sum ,@it))) 
      (else it)))) 
    (else 
     (let ((cur (car an)) 
      (rest (cdr an))) 
     (cond 
      ((number? cur) 
      `(+ ,cur ,(eval `(make-sum ,@rest)))) 
      ((variable? cur) 
      `(+ ',cur ,(eval `(make-sum ,@rest))))  
      ((sum? cur) 
      (let ((ad (addend cur)) 
        (ag (augend cur))) 
       (cond 
       #|if both the addend and augend of cur 
       are numbers, returns the sum|# 
       ((and (number? ad) 
        (number? ag)) 
        `(+ ,(+ ad ag) 
        ,(eval `(make-sum ,@rest)))) 

       #|if the addend of cur is a number 
       and the augend of cur is a sum|# 
       ((and (number? ad) 
        (sum? ag)) 
        (let ((adg (addend ag)) 
         (agg (augend ag))) 
        (cond 
         ((number? adg) 
        `(+ ,(+ ad adg) 
         ,(eval `(make-sum agg ,@rest)))) 

         ((number? agg) 
        `(+ ,(+ ad agg) 
         ,(eval `(make-sum adg ,@rest)))) 

         (else `(+ ,ad 
         ,(eval `(make-sum ,ag 
            ,@rest))))))) 

       (else `(+ ,cur (eval `(make-sum ,@rest)))))))))))) 

所以我的問題是,哪種方法做事情, 扁平化和過濾列表的「schemier」的方式來改造它進入正確的列表, 或通過遞歸的每個級別的規則手動遞歸? 第一個例子的優點是它的輸出比第二個(make-sum 'a 'b 'c) -> (+ a (+ b c))更易讀(make-sum 'a 'b 'c) -> (+ a b c)。第二個強項是你可以更容易地獲得衍生產品,只使用兩個函數addendaugend來選擇運算符,導數將以你在微積分類中處理它們的方式自然表達。第一個示例更難以採用衍生工具,要求將deriv映射到每個術語和幾個過濾器以確保正確的輸出。

編輯 還有,我不喜歡香港專業教育學院不得不使用eval那麼多,但它是我能想到的會有關輸入的解壓目錄的功能像

(eval `(foo ,@lst)) ;foo takes any number of arguments 
的唯一途徑

另外:

`',var ; for quoting the result from var 
     ; when this term is inside of a qq 

似乎喜歡的事,有一個更好的方式去

+1

我認爲習慣性Scheme(和一般的慣用函數式編程)是爲了在可能的情況下優先使用高階函數而不是顯式遞歸,只要它們提高可讀性(並且通常是這樣)。 –

回答

1

在鈧血紅素,慣用的方式是使用更高級的功能 - 按照map,filter,foldl,,等等(還有更多!)組成您的解決方案,這將導致更短的解決方案,而不是必須明確循環遍歷每個列表。

此外,請避免濫用準引用,拼接和eval :-)。您的所有代碼都可以在不使用它們的情況下進行重寫,本書中作者沒有涉及它們。例如,這樣的:

(eval `(foo ,@lst)) 

等效於此:

(apply foo lst) 

以上是優選的方式。同樣,這並沒有太大的意義:

`',var 

如果var已經是一個符號,然後離開它是,你不必再次引用它。底線:保持簡單,你的代碼無緣無故過於複雜。

+0

我想將var的值變成一個符號,所以如果(定義var'x)或(定義var(** x 2))被傳遞給一個函數,並且得到不加引號的'',var將返回x或( ** x 2)而不是'var – Anandamide

+0

但是正如我上面所說的,你不應該首先使用'unquote' ......這個問題並不需要,有更簡單的方法來完成這個練習所需要的東西。 –