さくらんぼのlambda日記

lambdaちっくなことからゲーム開発までいろいろ書きます。

部分文字列を置き換える

replaceだと何が駄目かというと、掲題のRubyの例ですと

s = "Apple Banana Orange"

s[0..4] = "Vine" #=> s = "Vine Banana Orane"
s[5, 6] = "Lemon" #=> s = "Vine Lemon Orange"

のようになっています。
これは、指定した範囲を引数の文字列で置き換えるという処理になっています。

同様の記述をreplaceで実行してみると。

(defparameter *test-string* "Apple Banana Orange")
*TEST-STRING*
(replace *test-string* "Vine" :start1 0 :end1 4)
"Vinee Banana Orange"

このような処理になってしまいます。
処理的には、指定した範囲の文字列を第二引数の値で上書きするという処理
になっていると思うと良いのでしょうかね。

そもそも、世間の文字列置換っていうものの処理が置換ではなく「指定した範囲の文字列を削除して、その削除した位置に指定の文字列を挿入する」という処理になっているんですね。

で、問題は対応するCommon Lispの関数は存在しないみたいで
http://cl-cookbook.sourceforge.net/strings.html#manip

common lisp cookbookにも、上記の文字列置換を行なう新しい関数replace-allを定義しています。

(defun replace-all (string part replacement &key (test #'char=))
"Returns a new string in which all the occurences of the part 
is replaced with replacement."
    (with-output-to-string (out)
      (loop with part-length = (length part)
            for old-pos = 0 then (+ pos part-length)
            for pos = (search part string
                              :start2 old-pos
                              :test test)
            do (write-string string out
                             :start old-pos
                             :end (or pos (length string)))
            when pos do (write-string replacement out)
            while pos))) 

こんな関数みたいです。うーむ。loopマクロ読めないw
cookbookによるとこの関数は遅いらしいのでcl-ppcreつかったほうが良さそうですね。