mapcarでsum-of-squaresを書き直す

mapcarを覚えたので、sum-of-squaresを書き直してみました。こんな便利なものがあるんだったら早く使えば良かった・・・。

(defun sum-of-squares (x)
  (if (null x)
      0
      (+ (* (first x) (first x)) (sum-of-squares (rest x)))))

上記のコードがもともとのsum-of-squares。再帰を使っていて、しかも末尾再帰じゃないので長いベクトルに対しては効率が悪そうです。

(defun sum-of-squares2 (x)
  (if (null x)
      0
      (eval (cons '+ (mapcar #'(lambda (y) (* y y)) x)))))

こちらがmapcarを使って書き換えたバージョン。2乗を計算する関数が分からなかったので無名関数を使っています。


さて、中身がどうなっているかというと、

(mapcar #'(lambda (y) (* y y)) x)

で計算結果のリスト(たとえば(setf x (1 2 3 4))に対しては(1 4 9 16))が出てきます。このリストの中身を全て加算すればいいのですが、そのまま

(+ (mapcar #'(lambda (y) (* y y)) x))

とすることはできません。

(+ '(1 3 9 16))

となってしまうからなのですね。どうにかして

(+ 1 3 9 16)

という形に直してやらないといけません。そこで、加算命令をリストの先頭にくっつけて(cons)やり、それを評価(eval)するという、ちょっと回りくどい方法を使いました。


【追記】cons&evalを一発でやるapplyという関数を見つけました。それで書き換えたものが以下。

(defun sum-of-squares3 (x)
  (if (null x)
      0
      (apply '+ (mapcar #'(lambda (y) (* y y)) x))))