まずはふつうの例。こんな文字列があったとき、
(define text-with-blockスペースかどうか判断するプロシージャを is-space? 渡して、この文字列をぶったぎりたい。
"words, |entering a block|, got out of the block.")
(define is-space? (pa$ char=? #\space))これは問題ない。
(string-split text-with-block is-space?)
=> ("words," "|entering" "a" "block|," "got" "out" "of" "the" "block.")
今度は、文字列のうちで縦棒 "|" でくくられている部分を塊とみなし、その内部にあるスペースは無視したいとする。つまりこんな結果がほしい。
(string-split text-with-block is-isolated-space?)クロージャーの出番です。is-isolated-space? はこんな定義でいいだろう。
=> ("words," "|entering a block|," "got" "out" "of" "the" "block.")
(define is-isolated-space?実際、 text-with-block に対して is-isolated-space? を繰り返し呼べば、縦棒 "|" でくくられた内部がセパレータとみなされないことが確かめられる。
(let ((inblock? #f))
(lambda (c)
(cond ((char=? c #\|)
(set! inblock? (if inblock? #f #t))
#f)
(inblock?
#f)
((char=? #\space c)
#t)
(else
#f)))))
(let R ((ls (string->list text-with-block)))見づらいけど、 "|entering a block|," の位置に相当する部分がぜんぶゼロになっているのがポイント。
(cond ((null? ls)
'())
((is-isolated-space? (car ls))
(cons 1 (R (cdr ls))))
(else
(cons 0 (R (cdr ls))))))
=> (0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 1 0 0 0 1 0 0 1 0 0 0 1 0 0 0 0 0 0)
だから、string-split に渡したプロシージャが文字列にある各文字に対して順番に呼ばれるなら、目的の結果が得られるはず。でも得られない。
(string-split text-with-block is-isolated-space?)しかも、もういっかい呼び出すと結果が変わる。
=> ("words," "|entering" "a" "block|, got out of the block.")
=> ("words, |entering" "a" "block|, got out of the block.")ということは、各文字ごとに is-isolated-space? の抱えているクロージャがクリアされているわけではないんだよな。2回目の呼び出しでは、inblock? の初期値が #t になっているようだ。
ちなみに、マニュアルにはこうある。
また、ソースの stringutil.scm にある string-split の定義を見ると、ふつうに文字列の先頭から各文字をプロシージャーで処理してるっぽい。なにがおかしいのかなあー。
splitter に手続きが与えられた場合、string にある各文字に対してその手続きが呼ばれ、
splitter が真の値を返すような連続した文字群がデリミタとして使われます。
仕方がないので代わりの関数をでっちあげる。
(define (string-split-by str proc)
(let ((n (string-index str proc)))
(if n
(receive (h r)
(values
(string-take str n)
(string-drop str n))
(if (= (string-length r) 0)
'()
(if (> n 0)
(cons h (string-split-by (string-drop r 1) proc))
(string-split-by (string-drop r 1) proc))))
`(,str))))
(string-split-by text-with-block is-isolated-space?)
=> ("words," "|entering a block|," "got" "out" "of" "the" "block.")
2 件のコメント:
スペース直後の文字に対しては is-isolated-space? が二度ずつ呼ばれるみたいですね.
is-isolated-space? の「(cond」の前に「#?=c」とか入れて実行してみると…
# 「svnInfoMaxRevision」で検索してここに来ました :-)
ほんとだ。is-space? に #?=c をかませても、やはり splitter のあとの文字が2回呼ばれるみたい。
それにしてもデバッグマクロ便利ですね。ありがとうございます。
> # 「svnInfoMaxRevision」で検索してここに来ました :-)
検索エンジン仕事速すぎ :)
コメントを投稿