Emacs Lisp の習得を目指す誰かの参考になるかもしれないので、僕なりの解答を書いておきます。
アルゴリズムの実装
繰り返しと仕事は分離したいので、まず仕事の部分を定義します。
(defun fizzbuzz (num) (cond ((= (% num 15) 0) "FizzBuzz") ((= (% num 3) 0) "Fizz") ((= (% num 5) 0) "Buzz") (t num))) (fizzbuzz 1) ;; => 1 (fizzbuzz 3) ;; => "Fizz" (fizzbuzz 5) ;; => "Buzz" (fizzbuzz 15) ;; => "FizzBuzz"
whileループ
Emacs Lisp で一番現実的なのは、while 分による繰り返しです。
(defun fizzbuzz1 (limit) (let ((i 1) ret) (while (<= i limit) (setq ret (cons (funcall 'fizzbuzz i) ret)) (setq i (1+ i))) (nreverse ret))) (fizzbuzz1 15) ;; => (1 2 "Fizz" 4 "Buzz" "Fizz" 7 8 "Fizz" "Buzz" 11 "Fizz" 13 14 "FizzBuzz")
マクロ
マクロで抽象化してみましょう。
(defmacro times-list (counter limit &rest body) `(let ((,counter 1) -temp-ret-) (while (<= ,counter ,limit) (setq -temp-ret- (cons ,@body -temp-ret-)) (setq ,counter (1+ ,counter))) (nreverse -temp-ret-))) (defun fizzbuzz2 (limit) (times-list i limit (funcall 'fizzbuzz i))) (fizzbuzz2 15) ;; => (1 2 "Fizz" 4 "Buzz" "Fizz" 7 8 "Fizz" "Buzz" 11 "Fizz" 13 14 "FizzBuzz")
末尾再帰
Emacs Lisp には末尾再帰の最適化がないにも関わらず、末尾再帰してみます。
(defun fizzbuzz3 (limit) (fizzbuzz3-recurse 1 limit)) (defun fizzbuzz3-recurse (i limit) (if (> i limit) nil (cons (funcall 'fizzbuzz i) (fizzbuzz3-recurse (1+ i) limit)))) (fizzbuzz3 15) ;; => (1 2 "Fizz" 4 "Buzz" "Fizz" 7 8 "Fizz" "Buzz" 11 "Fizz" 13 14 "FizzBuzz")
map
Emacs Lisp には遅延評価がないにも関わらず、1 から limit までの数のリストを生成して、map してみます。
(defun generate-list (limit) (let (ret) ;; dotimes は 0 から数え始める (dotimes (i limit (cdr (nreverse (cons limit ret)))) (setq ret (cons i ret))))) (defun fizzbuzz4 (limit) (mapcar 'fizzbuzz (generate-list limit))) (fizzbuzz4 15) ;; => (1 2 "Fizz" 4 "Buzz" "Fizz" 7 8 "Fizz" "Buzz" 11 "Fizz" 13 14 "FizzBuzz")