Shammer's Philosophy

My private adversaria

dolist 排除計画その1

概要

続・Lisp Condition Level 2・concat 完成? - Shammerismで書いた concat だが、これでいいかと思ったものの、文字を連結させるところで dolist を使用しているのが気になった。

	      ((string-listp strings)
	       (let ((result string1))
		 (dolist (s strings)
		   (setf result (concatenate 'string result s)))
		 result))

これを再帰にできないだろうか、というところで、ちょっと再帰について考察。

リスト内の文字列を全て連結する例。

とりあえず、list になった文字列を再帰で連結させてみる。とりあえず dolist を使う。

? (let ((result nil))
    (dolist (x (list "a" "b" "c"))
      (setf result (concatenate 'string result x)))
    result)
"abc"
?

dolist を使用しない再帰バージョンは以下。

? (defun rec (l)
    (if (null l)
        ""
        (concatenate 'string (car l) (rec (cdr l)))))
REC
? (rec (list "a" "b" "c"))
"abc"
?

おお、アッサリできてしまったか?

dolist 排除版 concat

以下のような実装に変更。

(defun concat (string1 &rest strings)
  (labels ((string-listp (l)
	     (if (null l)
		 t
		 (if (listp l)
		     (and (stringp (car l)) (string-listp (cdr l))))))
	   (conc (l)
	     (if (null l)
		 ""
		 (concatenate 'string (car l) (conc (cdr l))))))
    (if (stringp string1)
	(cond ((null strings)
	       string1)
	      ((string-listp strings)
	       (conc (list string1 strings)))
	      (t
	       (error
		(make-condition
		 'arguments-not-string-error
		 :text "Passed arguments includes objects which is not a string."))))
	(error
	 (make-condition
	  'arguments-not-string-error
	  :text (format nil "concat first argument should be string, but ~A is ~A."
			string1 (type-of string1)))))))

実行結果

? (concat "a" "b" "c")
> Error: The value "b" is not of the expected type CHARACTER.
> While executing: CCL::CONCAT-TO-STRING, in process listener(1).
> Type :POP to abort, :R for a list of available restarts.
> Type :? for other options.
1 >

うーむ。。。おそらく、(list string1 strings) が入れ子のリストになっているためと思われる。予想だと (string1 (strings)) みたいな感じ。これを(string1 strings)というようにする処理が必要になる。入れ子のリストをフラットなリストにする処理を追加してやらないといけなそうだ。