我正在通過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)
。第二個強項是你可以更容易地獲得衍生產品,只使用兩個函數addend
和augend
來選擇運算符,導數將以你在微積分類中處理它們的方式自然表達。第一個示例更難以採用衍生工具,要求將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
似乎喜歡的事,有一個更好的方式去
我認爲習慣性Scheme(和一般的慣用函數式編程)是爲了在可能的情況下優先使用高階函數而不是顯式遞歸,只要它們提高可讀性(並且通常是這樣)。 –