さまざまな文字セットを効率よく表現したり操作することはたいして魅力的ではないが、 テキスト処理コードにとっては非常に便利な機能であり、 他のライブラリにおいても実装されることがしばしばある。 そのため、早い時期にこの機能の基盤を定めておくことは有益である。 この SRFI では、そのための汎用的なライブラリを定義する。 この SRFI には参照実装が付属している。 参照実装はかなり効率的で、移植性が高く、 「フリーソフトウェア」としての著作権が与えられている。 この実装は、ASCII や Latin-1 のような 7 ビットまたは 8 ビットの小さな文字タイプに対して最適化されている。 Unicode のような 16 ビットまたは 32 ビットの文字タイプに対しては、 データ構造やアルゴリズムを変更する必要があるだろう。 しかし、ここで定める仕様は、このような大きな文字タイプのことも考慮して 注意深く設計されている。 今後の SRFI では、次の事項も定義されるだろう。
read-line
)
このライブラリでエクスポートされる束縛の完全な一覧を以下に示す。 モジュール システムやパッケージ システムをもつ Scheme 処理系では、 これらの手続きはモジュール名 "char-set-lib" に含めるべきである。
char-set? char-set= char-set<= char-set-hash
char-set-cursor char-set-ref char-set-cursor-next end-of-char-set? char-set-fold char-set-unfold char-set-unfold! char-set-for-each char-set-map
char-set-copy char-set list->char-set string->char-set list->char-set! string->char-set! char-set-filter ucs-range->char-set char-set-filter! ucs-range->char-set! ->char-set
char-set->list char-set->string char-set-size char-set-count char-set-contains? char-set-every char-set-any
char-set-adjoin char-set-delete char-set-adjoin! char-set-delete! char-set-complement char-set-union char-set-intersection char-set-complement! char-set-union! char-set-intersection! char-set-difference char-set-xor char-set-diff+intersection char-set-difference! char-set-xor! char-set-diff+intersection!
char-set:lower-case char-set:upper-case char-set:title-case char-set:letter char-set:digit char-set:letter+digit char-set:graphic char-set:printing char-set:whitespace char-set:iso-control char-set:punctuation char-set:symbol char-set:hex-digit char-set:blank char-set:ascii char-set:empty char-set:full
文字セットを効率的に操作する機能は、 テキスト処理コードにとって非常に有用である。 この機能を、汎用的で効率的に実装されたライブラリにカプセル化しておけば、 テキスト処理を行うときの助けになる。 このライブラリでは、文字セットを表現するために "char-set" という新しいデータ構造を定義する。 char-set 型は、他のすべての型とは異なる型である。
このライブラリは、異なる文字タイプを使用するさまざまな実装において
移植性があるように設計されている。
とくに、ASCII、Latin-1、Unicode の間で移植性がある。
Unicode の場合は、Java との互換性を保つようにいくらか努力をした
(ただ一つの非互換な仕様については
char-set:whitespace
を参照せよ)。
この SRFI の手続きは、基本的に「純粋関数的」である。 つまり、手続きはその引数を変更しない。 しかし、この SRFI では、いくつかの「線形更新」(linear-update) 手続きを定義する。 これらは、純粋関数的意味論および副作用的意味論をハイブリッドした意味論をもつ。 この種の手続きでは、結果を作成するために引数に対して副作用を及ぼすことが許されるが、 それは必須ではない。 線形更新手続きの実装では、副作用のない純粋関数的な実装を行うこともできるし、 副作用を及ぼす実装を行ってもよい。 どのような実装を行うかは、 背景にあるデータ構造を考慮した上で、 実装の効率性や容易性により選択すればよい。
線形更新手続きの名前は、末尾に "!" が付加される。
線形更新手続きを使用する場合、 副作用的に実装されていることを当てにしてはいけない。 たとえば、次のコードは正しくない。
(let* ((cs1 (char-set #\a #\b #\c)) ; cs1 = {a,b,c}. (cs2 (char-set-adjoin! cs1 #\d))) ; Add d to {a,b,c}. cs1) ; Could be either {a,b,c} or {a,b,c,d}.
しかし、次のコードは正しい。
(let ((cs (char-set #\a #\b #\c))) (char-set-adjoin! cs #\d)) ; Add d to {a,b,c}.
したがって、線形更新手続きを使用する場合、関数型スタイルで書くことはできるが、 その引数として渡したオブジェクトを別の場所で使用していないことを確認しなければならない。 引数として渡されたオブジェクトは、潜在的に変更されている可能性があるため、 別の場所で使用してはいけないのである (そのため「線形更新」と呼ばれる)。
線形更新手続きには、次の利点がある。
ASCII や Latin-1 をベースにした Scheme 実装では、 純粋関数的な表現は正しい選択であることに注意せよ。 その場合の文字セットは、4 つの 32 ビット ワードで表現できるからである。 この表現では、純粋な集合代数操作は非常に高速で効率的である。 線形更新操作を使用してコードを書くプログラマは、 システムが複数のプラットフォームに渡る最良の実装を提供してくれることを期待できる。
実用上は、限られたローカル コンテキストにおいて副作用的に文字セットを作成し、 作成された文字セットをローカル スコープ外に純粋関数的に返すような用途において、 線形更新手続きは最も有用である。
線形更新手続きに渡された引数は潜在的に副作用を受けるが、 その引数が線形性をもつかどうかを判断するための方法は Scheme にはない。 線形であるかどうかを判断する機能 (linear type checker) はないし、 線形性の違反を検出する実行時機構もない。 (しかし DrScheme のような洗練されたプログラミング環境では可能かもしれない)
以下の R5RS の述語は、
char-alphabetic?
char-numeric?
char-whitespace?
char-upper-case?
char-lower-case?
以下の SRFI 14 の文字セットに一致するかもしれないし、 一致しないかもしれないことに注意せよ。
char-set:letter
char-set:digit
char-set:whitespace
char-set:upper-case
char-set:lower-case
実装者は、上記の述語をこの SRFI の文字セットと一致するように定義することを 強く推奨する。 そうしないと大きな混乱が生じるだろう。
以下で手続きの仕様を記述するにあたり、次のことを前提とする。
手続きに対して、ここで指定された型以外の値を渡すと、エラーである。
特に断りのない限り、手続きは、引数で指定された文字セットとは
(線形更新の意味で) 異なる文字セットを返す。
たとえば、char-set-adjoin
は、
引数に文字を 1 つも指定しない場合であっても、
新しい文字セットを返すことが保証される。
角括弧に囲まれた引数はオプションである。 手続きの解説で特に断りのない限り、 これらのオプション引数は前から順番に任意の数を指定することができる。 オプション引数を 1 つも指定しなくてもよいし、すべて指定してもよい。 手続きが多重値を返す場合も、戻り値を角括弧内に記述する。 たとえば、次のようなシグニチャをもつ手続きの場合、
halts? f [x init-store] -> [boolean integer]1 つ (f)、 2 つ (f, x)、または、 3 つ (f, x, init-store) の引数をとり、ブール値と整数値の 2 つの値を返す。
後ろに "...
" がつく引数は、
0 個以上の引数を意味する。
たとえば、次のシグニチャをもつ手続きの場合、
sum-squares x ... -> number0 個以上の引数をとる (x ...)。 また、次のシグニチャをもつ手続きの場合、
spell-check doc dict1 dict2 ... -> string-list2 つの必須引数 (doc と dict1) をとり、 0 個以上のオプション引数 (dict2 ...) をとる。
char-set?
obj -> boolean
char-set=
cs1 ... -> boolean
境界条件:
(char-set=) => true (char-set= cs) => true
論拠: 推移的な二項関係は、Scheme では n-項関係に拡張することができ、 より明快で簡潔なコードを書くことができる。 n-項関係を 1 階 (first-order) で使用する限りにおいては、 引数の数が 0 個や 1 個であることはまずないが、 高階で使用する場合や、マクロ展開されたコードで使用する場合、 引数の数が 0 個や 1 個ということがあり得る。 たとえば、次の式を考える。
(apply char-set= cset-list)
これはリストが空であるか 1 個の要素しかもたない場合は、はっきりとした定義をもつ。 そこで、この関係を任意個の引数に対して拡張した。 実装者たちは、n-項関係を 2 個より少ない引数に対しても適用するような 高階の使用例を報告している。 Scheme の慣習では汎用的に使えるものを定義するから、 ここでは完全に汎用的な拡張を行った。
この拡張に対する反論は、
R5RS
の推移的な二項算術関係
(=
, <
など)
は、少なくとも 2 つの引数を必要としており、
0 個や 1 個の引数を許すと、この慣例を破ってしまうことになるというものである。
しかし、少なくとも後方互換ではある。
char-set<=
cs1 ... -> boolean
境界条件:
(char-set<=) => true (char-set<= cs) => true
論拠:
0 個および 1 個の引数に関する議論については
char-set=
を参照せよ。
char-set のリストの単調増加性を調べるための次の式を考えてみるとよい。
(apply char-set<= cset-list)
char-set-hash
cs [bound] -> integer
bound が 0 であるか指定されない場合、 実装固有のデフォルト値が使用される。
不変式:
(char-set= cs1 cs2) => (= (char-set-hash cs1 b) (char-set-hash cs2 b))
次の実装は正しい実装だが、推奨されない。
(define (char-set-hash cs . maybe-bound) 1)
論拠: この手続きの使用者がハッシュ値の制限を指定できることにより、 使用者のコードで通常よく行われるハッシュ値の剰余計算が必要なくなり、 コードが簡単になる。 また、ハッシュ関数の実装でこの制限値を利用することにより、 ハッシュ値の計算が効率的になる可能性がある。 たとえば、制限値が小さい場合、 計算の途中の値が多倍長整数 (bignum) にならないように計算することで、 固定長整数 (fixnum) に固有の高速な計算処理を行うことができるかもしれない。
char-set-cursor
cset -> cursor
char-set-ref
cset cursor -> char
char-set-cursor-next
cset cursor -> cursor
end-of-char-set?
cursor -> boolean
char-set-cursor
は、
指定された文字セットに対する新しいカーソルを作成する。
文字セットの文字は char-set-ref
で取得する。
カーソルのインデックスは char-set-cursor-next
でインクリメントする。
これにより、文字セット内のすべての文字を列挙することができる。
カーソルが最後の文字を越えると、
end-of-char-set?
は真を返す。
最後の文字を越えたカーソルを char-set-ref
や
char-set-cursor-next
に渡すとエラーである。
カーソルの値は異なる文字セットとともに使用してはならない。
カーソルを作成したときの文字セットとは異なる文字セットを
char-set-ref
や char-set-cursor-next
に指定した場合、
その結果や作用は未定義である。
カーソルの値は他の型と異なる必要はない。 整数値、リンクリスト、レコード、手続き、などの型であってよい。 これを許すことにより、カーソルはとても「軽量」にすることができるので、 カーソルの実装が単純であっても、厳しいループの中でも使用可能なものとなり得る。
ループ マクロを使用して文字セット内の文字を列挙するためには、 これらの手続きが必要であることに注意せよ。
例:
(define cs (char-set #\G #\a #\T #\e #\c #\h)) ;; 文字セット cs の要素をリストに集める。 (let lp ((cur (char-set-cursor cs)) (ans '())) (if (end-of-char-set? cur) ans (lp (char-set-cursor-next cs cur) (cons (char-set-ref cs cur) ans)))) => (#\G #\T #\a #\c #\e #\h) ;; 同じことをリストの逆畳み込み (SRFI 1) を利用して行う。 (unfold-right end-of-char-set? (curry char-set-ref cs) (curry char-set-cursor-next cs) (char-set-cursor cs)) => (#\G #\T #\a #\c #\e #\h)
論拠:
カーソル API の上記の 4 つの手続きは、
リスト、文字列、および文字セットに関する SRFI で提供される逆畳み込み関数のプロトコルに
適合していることに注意せよ (上の例を参照せよ)。
これと比較するために、
より簡単な 2 つの関数を考えてみる
(これらの関数は逆畳み込みに使えないので否決された)。
その 2 つの関数とは、
char-set-cursor
と、
カーソルと文字セットを文字と次のカーソルにマップする関数である。
もしカーソルが文字セットの終端に達した場合、
この関数は文字ではなく偽値を返し、
終端に達した別の文字セットも返す。
この方法を使うと、他の 3 つの関数を 1 つの関数に結合することができる。
char-set-fold
kons knil cs -> object
(char-set-fold kons (kons c knil) cs')
例:
;; 文字セットに含まれる文字のリスト (CHAR-SET-MEMBERS) (lambda (cs) (char-set-fold cons '() cs)) ;; 文字セットのサイズ (CHAR-SET-SIZE) (lambda (cs) (char-set-fold (lambda (c i) (+ i 1)) 0 cs)) ;; 文字セットにいくつの母音が含まれているか? (lambda (cs) (char-set-fold (lambda (c i) (if (vowel? c) (+ i 1) i)) 0 cs))
char-set-unfold
f p g seed [base-cs] -> char-set
char-set-unfold!
f p g seed base-cs -> char-set
char-set-unfold!
はマップされた文字を線形更新的に
base-cs に追加する。
つまり、base-cs に副作用を及ぼして結果を構成することが許される。
より正確に言うと、次のように定義できる。 ただし、オプション引数の処理は省略している。
(define (char-set-unfold p f g seed base-cs) (char-set-unfold! p f g seed (char-set-copy base-cs))) (define (char-set-unfold! p f g seed base-cs) (let lp ((seed seed) (cs base-cs)) (if (p seed) cs ; P says we are done. (lp (g seed) ; Loop on (G SEED). (char-set-adjoin! cs (f seed)))))) ; Add (F SEED) to set.(実際の実装はもっと効率的にできるだろう)
例:
(port->char-set p) = (char-set-unfold eof-object? values (lambda (x) (read-char p)) (read-char p)) (list->char-set lis) = (char-set-unfold null? car cdr lis)
char-set-for-each
proc cs -> unspecified
この手続きの戻り値は規定されない。
この手続きを呼び出すたびに戻り値が異なることもあり得る。
戻り値は、コマンド継続 (command continuation) に渡すことができる値
(または多重値) であれば何でもよい。
たとえば、begin
式の非終端サブフォームの値でもよい。
R5RS
においては、このことは、1 つの値を返さなければならないという制限になる。
非-R5RS システムではこの制限はない。
char-set-map
proc cs -> char-set
つまり、手続き proc を char -> char の手続きから char-set -> char-set の手続きに格上げする。
例:
(char-set-map char-downcase cset)
char-set-copy
cs -> char-set
以下で述べる線形更新手続きを純粋関数的に実装するシステムでは、
この手続きを恒等関数として実装してもよい。
そのため、元の文字セットとそのコピーを
eq?
により区別できることは保証されない。
char-set
char1 ... -> char-set
list->char-set
char-list [base-cs] -> char-set
list->char-set!
char-list base-cs -> char-set
文字セット base-cs が指定された場合、
char-list 内の文字がそれに追加される。
list->char-set!
は base-cs
を再利用して副作用を及ぼしてもよい (副作用がなくてもよい)。
list->char-set
はまったく新しい文字セットを作成する。
string->char-set
s [base-cs] -> char-set
string->char-set!
s base-cs -> char-set
文字セット base-cs が指定された場合、
s の文字はそこに追加される。
string->char-set!
は base-cs
を再利用して副作用を及ぼしてもよい (副作用がなくてもよい)。
string->char-set
はまったく新しい文字セットを作成する。
char-set-filter
pred cs [base-cs] -> char-set
char-set-filter!
pred cs base-cs -> char-set
(pred c)
を満たす文字からなる文字セットを返す。
文字セット base-cs が指定されている場合、
pred で選別された文字がそこに追加される。
char-set-filter!
は base-cs
を再利用して副作用を及ぼしてもよい (副作用がなくてもよい)。
char-set-filter
はまったく新しい文字セットを作成する。
この手続きの実装では、pred の参照を保持しておいて
char-set-filter
や char-set-filter!
から戻った後でそれを呼び出す、ということをしてはいけない。
pred が変更可能な外部データに依存しているかもしれないし、
あるいは、副作用をもつかもしれないので、
そういったオンデマンドの実装は許されない。
論拠:
この手続きは、文字述語をそれに等価な文字セットに変換する方法を提供する。
引数 cs は、その述語の定義域を制限する働きをする。
32 ビット Unicode のような巨大な文字タイプの実装において
char-set:full
のような文字セットにフィルタをかけると
非常に高価な操作になるということを、
プログラマは意識しなければならない。
このライブラリの初期の草案では
predicate->char-set
という単純な手続きを定義したが、
この理由により否決され、char-set-filter
のほうが採択された。
ucs-range->char-set
lower upper [error? base-cs] -> char-set
ucs-range->char-set!
lower upper error? base-cs -> char-set
ISO/IEC 10646 UCS-4 の半開コード範囲 [lower,upper) を含む文字セットを返す。
文字セット base-cs が指定された場合、
指定された範囲の文字がそこに追加される。
ucs-range->char-set!
は base-cs
を再利用して副作用を及ぼしてもよい (副作用がなくてもよい)。
ucs-range->char-set
はまったく新しい文字セットを作成する。
ASCII は Latin-1 のサブセットであり、 Latin-1 は 16 ビット Unicode のサブセットであり、 16 ビット Unicode は 32 ビット UCS-4 のサブセットであることに注意せよ。 Scheme 実装において文字がどのように表現されているにせよ、 文字コードに固有の処理はこの手続きに任せることになるため、 このライブラリを使用するコードには移植性がある。 言い換えると、 この SRFI に準拠する Scheme 実装が、 文字コードとして EBCDIC や SHIFT-JIS を使用してもよいが、 その場合、指定範囲の UCS 文字をネイティブ表現にマップ可能であればマップし、 マップ不可能であればエラーを発生させなければならない。
->char-set
x -> char-set
char-set-size
cs -> integer
char-set-count
pred cs -> integer
char-set->list
cs -> character-list
char-set->string
cs -> string
char-set-contains?
cs char -> boolean
MIT Scheme の character-set パッケージは、 この手続きに char-set-member? という名前を付けているが、 引数の順序がその名前と整合していない。
char-set-every
pred cs -> boolean
char-set-any
pred cs -> boolean
char-set-every
手続きは、
文字セット cs のすべての文字に対して述語
pred が真となるときに、真を返す。
同様に、char-set-any
は文字セット cs
のすべての文字に対して pred を適用し、
最初に真となった値を返す。
すべての文字で真とならない場合は、偽を返す。
文字セット cs の文字に述語が適用される順序は規定されない。
述語が真となる文字を知りたければ、
char-set-any
を使用し、
その述語の戻り値として文字そのものを返せばよい。
たとえば、
(char-set-any (lambda (c) (and (char-upper-case? c) c)) cs)
char-set-adjoin
cs char1 ... -> char-set
char-set-delete
cs char1 ... -> char-set
char-set-adjoin!
cs char1 ... -> char-set
char-set-delete!
cs char1 ... -> char-set
char-set-complement
cs -> char-set
char-set-union
cs1 ... -> char-set
char-set-intersection
cs1 ... -> char-set
char-set-difference
cs1 cs2 ... -> char-set
char-set-xor
cs1 ... -> char-set
char-set-diff+intersection
cs1 cs2 ... -> [char-set char-set]
境界条件:
(char-set-union) => char-set:empty (char-set-intersection) => char-set:full (char-set-xor) => char-set:empty (char-set-difference cs) => cs
char-set-diff+intersection
は、引数の差集合と積集合を同時に返す。
つまり、最初の引数を 2 つに分割する。
次の式に等しい。
(values (char-set-difference cs1 cs2 ...) (char-set-intersection cs1 (char-set-union cs2 ...)))しかし、もっと効率的に実装することができる。
Scheme 実装が 32 ビット Unicode のような巨大な文字タイプを使用している場合、
char-set-complement
は潜在的に非常に高価な操作であることを、
プログラマは意識するべきである。
その可能性がある場合、char-set-difference
を使用してより小さな領域に対して補集合をとるとよい。
char-set-complement!
cs -> char-set
char-set-union!
cs1 cs2 ... -> char-set
char-set-intersection!
cs1 cs2 ... -> char-set
char-set-difference!
cs1 cs2 ... -> char-set
char-set-xor!
cs1 cs2 ... -> char-set
char-set-diff+intersection!
cs1 cs2 cs3 ... -> [char-set char-set]
char-set-diff+intersection!
は、
2 つの必須引数 cs1
と cs2 に対して副作用を及ぼしてもよい。
便宜のために、いくつかの文字セットがあらかじめ定義されている。
char-set:lower-case | 小文字 |
char-set:upper-case | 大文字 |
char-set:title-case | 表題文字 (title-case) |
char-set:letter | レター |
char-set:digit | 数字 |
char-set:letter+digit | レターと数字 |
char-set:graphic | スペースを除く印字文字 |
char-set:printing | スペースを含む印字文字 |
char-set:whitespace | 空白文字 |
char-set:iso-control | ISO 制御文字 |
char-set:punctuation | 区切り文字 |
char-set:symbol | 記号文字 |
char-set:hex-digit | 16 進数文字: 0-9, A-F, a-f |
char-set:blank | ブランク文字 -- 水平空白文字 |
char-set:ascii | ASCII 文字セットのすべての文字 |
char-set:empty | 空の文字セット |
char-set:full | すべての文字 |
char-set:letter
の中に大文字でも小文字でもない文字が
存在する可能性があることに注意せよ。
このことは、Unicode のような ASCII よりも豊富な文字タイプを使用する実装で起きる。
図形文字 (graphic character) とは、紙の上にインクを残す文字のことである。
これらの文字セットの正確な構成は、
Scheme 処理系が提供する文字タイプにより異なる可能性があるが、
ASCII を使用した Scheme 実装における定義を以下に示す。
char-set:lower-case | a-z |
char-set:upper-case | A-Z |
char-set:letter | A-Z および a-z |
char-set:digit | 0123456789 |
char-set:punctuation | !"#%&'()*,-./:;?@[\]_{} |
char-set:symbol | $+<=>^`|~ |
char-set:whitespace | スペース、改行、タブ、フォームフィード |
垂直タブ、復帰 | |
char-set:blank | スペース、タブ |
char-set:graphic | letter + digit + punctuation + symbol |
char-set:printing | graphic + whitespace |
char-set:iso-control | ASCII の 0-31 および 127 |
文字セット char-set:ascii
が存在するということは、
その Scheme システムで実装されている文字セットが少なくとも (制御文字も含めて)
ASCII ぐらいに豊富でなければならないことを意味している。
論拠: R5RS や Posix では "alphabetic/numeric" という用語が使われていたが、 Unicode では "letter/digit" という用語が使われており、 ここではこちらの用語を採択している。
Unicode Scheme 実装においては、ベースとなる文字セットは Java の Unicode 仕様と互換性をもたせる。 ASCII または Latin-1 においては、 単純に Unicode をその最初の 128 または 256 コードに制限することにする。 ASCII, Latin-1, Unicode のいずれもベースとしない Scheme 実装においては、 これらの定義の意味や精神を保持するように努めるべきである。
以下の解説では、しばしば「Unicode 文字データベース」を参照している。 これは次の URL から入手できるファイルである。
各行には Unicode 文字の属性が書かれている。 セミコロンで区切られた最初のフィールドは、 文字コードの 16 進数値である。 2 つめのフィールドは文字の名前、 3 つめのフィールドは 2 文字のカテゴリ名である。 他のフィールドは、単純な一対一のケース マッピングとその他の情報を表す。 ファイル形式の詳細については、次の URL を参照せよ。
特に、3 つめのフィールドの 2 文字のカテゴリ名は、 以下の解説で頻繁に参照する。
Unicode では、Java の仕様にしたがうものとする。 以下の場合に小文字とする。
ASCII の小文字は、以下の文字とする。
Latin-1 では、ASCII の小文字に加えて、次の 33 文字を小文字とする。
00B5 | MICRO SIGN |
00DF | LATIN SMALL LETTER SHARP S |
00E0 | LATIN SMALL LETTER A WITH GRAVE |
00E1 | LATIN SMALL LETTER A WITH ACUTE |
00E2 | LATIN SMALL LETTER A WITH CIRCUMFLEX |
00E3 | LATIN SMALL LETTER A WITH TILDE |
00E4 | LATIN SMALL LETTER A WITH DIAERESIS |
00E5 | LATIN SMALL LETTER A WITH RING ABOVE |
00E6 | LATIN SMALL LETTER AE |
00E7 | LATIN SMALL LETTER C WITH CEDILLA |
00E8 | LATIN SMALL LETTER E WITH GRAVE |
00E9 | LATIN SMALL LETTER E WITH ACUTE |
00EA | LATIN SMALL LETTER E WITH CIRCUMFLEX |
00EB | LATIN SMALL LETTER E WITH DIAERESIS |
00EC | LATIN SMALL LETTER I WITH GRAVE |
00ED | LATIN SMALL LETTER I WITH ACUTE |
00EE | LATIN SMALL LETTER I WITH CIRCUMFLEX |
00EF | LATIN SMALL LETTER I WITH DIAERESIS |
00F0 | LATIN SMALL LETTER ETH |
00F1 | LATIN SMALL LETTER N WITH TILDE |
00F2 | LATIN SMALL LETTER O WITH GRAVE |
00F3 | LATIN SMALL LETTER O WITH ACUTE |
00F4 | LATIN SMALL LETTER O WITH CIRCUMFLEX |
00F5 | LATIN SMALL LETTER O WITH TILDE |
00F6 | LATIN SMALL LETTER O WITH DIAERESIS |
00F8 | LATIN SMALL LETTER O WITH STROKE |
00F9 | LATIN SMALL LETTER U WITH GRAVE |
00FA | LATIN SMALL LETTER U WITH ACUTE |
00FB | LATIN SMALL LETTER U WITH CIRCUMFLEX |
00FC | LATIN SMALL LETTER U WITH DIAERESIS |
00FD | LATIN SMALL LETTER Y WITH ACUTE |
00FE | LATIN SMALL LETTER THORN |
00FF | LATIN SMALL LETTER Y WITH DIAERESIS |
これらの文字のうち次の 3 文字は、対応する大文字が Latin-1 に存在しないことに注意せよ。
00B5 | MICRO SIGN |
00DF | LATIN SMALL LETTER SHARP S |
00FF | LATIN SMALL LETTER Y WITH DIAERESIS |
(互換マイクロ文字 (compatibility micro character) の大文字は Latin-1 には存在しないギリシャ文字のミューになる。 ドイツ語のシャープ s 文字 (German sharp s character) の大文字は 2 つの文字 "SS" になる。 y-with-diaeresis の大文字は Latin-1 には存在しない文字になる。)
(次のドキュメントで定義されている Java の小文字の仕様は、
矛盾したものであることに注意せよ。 U+00B5 MICRO SIGN は (Unicode 3.0 の) 小文字としての条件を満たすが、 小文字の番号リストには記載されていない。)
(次のドキュメントで定義されている Java の isLowerCase()
の仕様は、
相互に矛盾した「小文字」の定義を与えていることに注意せよ。 1 つめの定義は、この SRFI の定義と同じである。 それに続いて次のように書かれている。 「Unicode 2.0 標準規格において小文字として定義されている文字 (Unicode 仕様データファイルのカテゴリ Ll に属する文字) を小文字と定義し、かつ、その文字に限り小文字と定義する。」 これが 2 つめの定義である。 1 つめの定義では、 U+00AA FEMININE ORDINAL INDICATOR と U+00BA MASCULINE ORDINAL INDICATOR は小文字ではないが、 2 つめの定義では、これらは小文字になる。 Java のドキュメントには Latin-1 サブセットにおける小文字の一覧を記載しているが、 これが 3 つめの定義である。 この一覧では U+00B5 MICRO SIGN が除外されているが、 この文字は、1 つめと 2 つめの定義に含まれている。)
Unicode では、Java の仕様にしたがうものとする。 以下の場合に大文字とする。
ASCII の大文字は、以下の文字とする。
Latin-1 では、ASCII の大文字に加えて、次の 30 文字を大文字とする。
00C0 | LATIN CAPITAL LETTER A WITH GRAVE |
00C1 | LATIN CAPITAL LETTER A WITH ACUTE |
00C2 | LATIN CAPITAL LETTER A WITH CIRCUMFLEX |
00C3 | LATIN CAPITAL LETTER A WITH TILDE |
00C4 | LATIN CAPITAL LETTER A WITH DIAERESIS |
00C5 | LATIN CAPITAL LETTER A WITH RING ABOVE |
00C6 | LATIN CAPITAL LETTER AE |
00C7 | LATIN CAPITAL LETTER C WITH CEDILLA |
00C8 | LATIN CAPITAL LETTER E WITH GRAVE |
00C9 | LATIN CAPITAL LETTER E WITH ACUTE |
00CA | LATIN CAPITAL LETTER E WITH CIRCUMFLEX |
00CB | LATIN CAPITAL LETTER E WITH DIAERESIS |
00CC | LATIN CAPITAL LETTER I WITH GRAVE |
00CD | LATIN CAPITAL LETTER I WITH ACUTE |
00CE | LATIN CAPITAL LETTER I WITH CIRCUMFLEX |
00CF | LATIN CAPITAL LETTER I WITH DIAERESIS |
00D0 | LATIN CAPITAL LETTER ETH |
00D1 | LATIN CAPITAL LETTER N WITH TILDE |
00D2 | LATIN CAPITAL LETTER O WITH GRAVE |
00D3 | LATIN CAPITAL LETTER O WITH ACUTE |
00D4 | LATIN CAPITAL LETTER O WITH CIRCUMFLEX |
00D5 | LATIN CAPITAL LETTER O WITH TILDE |
00D6 | LATIN CAPITAL LETTER O WITH DIAERESIS |
00D8 | LATIN CAPITAL LETTER O WITH STROKE |
00D9 | LATIN CAPITAL LETTER U WITH GRAVE |
00DA | LATIN CAPITAL LETTER U WITH ACUTE |
00DB | LATIN CAPITAL LETTER U WITH CIRCUMFLEX |
00DC | LATIN CAPITAL LETTER U WITH DIAERESIS |
00DD | LATIN CAPITAL LETTER Y WITH ACUTE |
00DE | LATIN CAPITAL LETTER THORN |
Unicode では、文字属性データベースでカテゴリ Lt をもつ文字を表題文字 (titlecase) とする。 このカテゴリの文字は非常に少ない。 Unicode 3.0 における 31 文字すべてを以下に示す。
01C5 | LATIN CAPITAL LETTER D WITH SMALL LETTER Z WITH CARON |
01C8 | LATIN CAPITAL LETTER L WITH SMALL LETTER J |
01CB | LATIN CAPITAL LETTER N WITH SMALL LETTER J |
01F2 | LATIN CAPITAL LETTER D WITH SMALL LETTER Z |
1F88 | GREEK CAPITAL LETTER ALPHA WITH PSILI AND PROSGEGRAMMENI |
1F89 | GREEK CAPITAL LETTER ALPHA WITH DASIA AND PROSGEGRAMMENI |
1F8A | GREEK CAPITAL LETTER ALPHA WITH PSILI AND VARIA AND PROSGEGRAMMENI |
1F8B | GREEK CAPITAL LETTER ALPHA WITH DASIA AND VARIA AND PROSGEGRAMMENI |
1F8C | GREEK CAPITAL LETTER ALPHA WITH PSILI AND OXIA AND PROSGEGRAMMENI |
1F8D | GREEK CAPITAL LETTER ALPHA WITH DASIA AND OXIA AND PROSGEGRAMMENI |
1F8E | GREEK CAPITAL LETTER ALPHA WITH PSILI AND PERISPOMENI AND PROSGEGRAMMENI |
1F8F | GREEK CAPITAL LETTER ALPHA WITH DASIA AND PERISPOMENI AND PROSGEGRAMMENI |
1F98 | GREEK CAPITAL LETTER ETA WITH PSILI AND PROSGEGRAMMENI |
1F99 | GREEK CAPITAL LETTER ETA WITH DASIA AND PROSGEGRAMMENI |
1F9A | GREEK CAPITAL LETTER ETA WITH PSILI AND VARIA AND PROSGEGRAMMENI |
1F9B | GREEK CAPITAL LETTER ETA WITH DASIA AND VARIA AND PROSGEGRAMMENI |
1F9C | GREEK CAPITAL LETTER ETA WITH PSILI AND OXIA AND PROSGEGRAMMENI |
1F9D | GREEK CAPITAL LETTER ETA WITH DASIA AND OXIA AND PROSGEGRAMMENI |
1F9E | GREEK CAPITAL LETTER ETA WITH PSILI AND PERISPOMENI AND PROSGEGRAMMENI |
1F9F | GREEK CAPITAL LETTER ETA WITH DASIA AND PERISPOMENI AND PROSGEGRAMMENI |
1FA8 | GREEK CAPITAL LETTER OMEGA WITH PSILI AND PROSGEGRAMMENI |
1FA9 | GREEK CAPITAL LETTER OMEGA WITH DASIA AND PROSGEGRAMMENI |
1FAA | GREEK CAPITAL LETTER OMEGA WITH PSILI AND VARIA AND PROSGEGRAMMENI |
1FAB | GREEK CAPITAL LETTER OMEGA WITH DASIA AND VARIA AND PROSGEGRAMMENI |
1FAC | GREEK CAPITAL LETTER OMEGA WITH PSILI AND OXIA AND PROSGEGRAMMENI |
1FAD | GREEK CAPITAL LETTER OMEGA WITH DASIA AND OXIA AND PROSGEGRAMMENI |
1FAE | GREEK CAPITAL LETTER OMEGA WITH PSILI AND PERISPOMENI AND PROSGEGRAMMENI |
1FAF | GREEK CAPITAL LETTER OMEGA WITH DASIA AND PERISPOMENI AND PROSGEGRAMMENI |
1FBC | GREEK CAPITAL LETTER ALPHA WITH PROSGEGRAMMENI |
1FCC | GREEK CAPITAL LETTER ETA WITH PROSGEGRAMMENI |
1FFC | GREEK CAPITAL LETTER OMEGA WITH PROSGEGRAMMENI |
ASCII と Latin-1 では表題文字 (titlecase) は存在しない。
Unicode では、 Unicode 文字データベースでレター カテゴリ (Lu, Ll, Lt, Lm, Lo) に属する文字をレター (letter) とする。
ASCII では以下の 52 文字をレターとする。
Latin-1 には 117 文字のレターがある。
Latin-1 の char-set:lower-case
と char-set:upper-case
に属する 115 文字と、以下の 2 文字をレターとする。
00AA | FEMININE ORDINAL INDICATOR |
00BA | MASCULINE ORDINAL INDICATOR |
(Unicode では、これらの 2 文字は小文字とされるが、 Java や SRFI 14 では小文字とはならない。)
Unicode では、文字属性データベースでカテゴリ Nd をもつ文字を数字 (digit) とする。 Latin-1 と ASCII では、0123456789 を数字とする。 Unicode では、他のコードブロックに Gujarati digits や Tibetan digits などの別の数字文字が存在する。
16 進数文字 (hex digit) は、0123456789abcdefABCDEF のみとする。
char-set:letter
と char-set:digit
の和集合。
図形文字 (graphic character) とは、紙にインクを残す文字のことである。 ASCII と Latin-1 の図形文字は、以下の文字である。
char-set:letter |
char-set:digit |
char-set:punctuation |
char-set:symbol |
印字文字 (printing character) とは、印字したときに空間を占有する文字のことである。
つまり、図形文字か空白である。
char-set:printing
は
char-set:whitespace
と char-set:graphic
の和集合である。
Unicode では、空白文字 (whitespace character) とは、以下のいずれかである。
Unicode 3.0 では、24 文字の空白文字がある。
0009 | HORIZONTAL TABULATION | \t control-I |
000A | LINE FEED | \n control-J |
000B | VERTICAL TABULATION | \v control-K |
000C | FORM FEED | \f control-L |
000D | CARRIAGE RETURN | \r control-M |
0020 | SPACE | Zs |
00A0 | NO-BREAK SPACE | Zs |
1680 | OGHAM SPACE MARK | Zs |
2000 | EN QUAD | Zs |
2001 | EM QUAD | Zs |
2002 | EN SPACE | Zs |
2003 | EM SPACE | Zs |
2004 | THREE-PER-EM SPACE | Zs |
2005 | FOUR-PER-EM SPACE | Zs |
2006 | SIX-PER-EM SPACE | Zs |
2007 | FIGURE SPACE | Zs |
2008 | PUNCTUATION SPACE | Zs |
2009 | THIN SPACE | Zs |
200A | HAIR SPACE | Zs |
200B | ZERO WIDTH SPACE | Zs |
2028 | LINE SEPARATOR | Zl |
2029 | PARAGRAPH SEPARATOR | Zp |
202F | NARROW NO-BREAK SPACE | Zs |
3000 | IDEOGRAPHIC SPACE | Zs |
ASCII の空白文字は、上記一覧のうちの最初の 6 文字、
ラインフィード、水平タブ、垂直タブ、フォームフィード、復帰、スペース、である。
これらの文字は、Posix の isspace()
手続きにより認識される文字でもある。
Latin-1 では、これらに加えて、ノーブレークスペース (no-break space) を追加する。
ノート:Java の isWhitespace()
メソッドとは互換性がない。
このメソッドは、次の文字を含み、
0009 | HORIZONTAL TABULATION | (\t control-I) |
001C | FILE SEPARATOR | (control-\) |
001D | GROUP SEPARATOR | (control-]) |
001E | RECORD SEPARATOR | (control-^) |
001F | UNIT SEPARATOR | (control-_) |
次の文字は含まない。
00A0 | NO-BREAK SPACE |
Java がノーブレークスペースを含んでいないことで、
トークナイザは単純に文字ストリームの「空白」の位置で分割できるようになっている。
しかし、この文字を除外することで、他の場面で例外を設けなければならなくなる。
たとえば、char-set:printing
は単純に
char-set:graphic
と char-set:whitespace
の和集合としては定義できなくなる。
ISO 制御文字 (ISO control character) は、 [U+0000,U+001F] および [U+007F,U+009F] の範囲の Unicode/Latin-1 文字である。
ASCII では、この文字セットは [U+0000,U+001F] の範囲と 文字 U+007F に制限される。
Unicode では、この文字セットには属さない別の制御文字を定義していることに注意せよ
(そのため名前に "iso-" というプレフィックスをつけているのである)。
この制限により、Java の IsISOControl()
メソッドとの互換性が保てる。
Unicode では、 Unicode 文字データベースにおいて何らかの区切りカテゴリ (Pc, Pd, Ps, Pe, Pi, Pf, Po) に属する文字を区切り文字 (punctuation character) とする。
ASCII では、次の 23 文字を区切り文字とする。
!"#%&'()*,-./:;?@[\]_{}
Latin-1 では、これに 6 文字を追加する。
00A1 | INVERTED EXCLAMATION MARK |
00AB | LEFT-POINTING DOUBLE ANGLE QUOTATION MARK |
00AD | SOFT HYPHEN |
00B7 | MIDDLE DOT |
00BB | RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK |
00BF | INVERTED QUESTION MARK |
9 つの ASCII 文字 $+<=>^`|~
は区切り文字ではないことに注意せよ。
これらは「記号文字」である。
Unicode では、Unicode 文字データベースにおいて何らかの記号カテゴリ (Sm, Sc, Sk, So) に属する文字を記号文字 (symbol character) とする。 ASCII では、次の 9 文字を記号文字とする。
$+<=>^`|~
Latin-1 では、これに 18 文字を追加する。
00A2 | CENT SIGN |
00A3 | POUND SIGN |
00A4 | CURRENCY SIGN |
00A5 | YEN SIGN |
00A6 | BROKEN BAR |
00A7 | SECTION SIGN |
00A8 | DIAERESIS |
00A9 | COPYRIGHT SIGN |
00AC | NOT SIGN |
00AE | REGISTERED SIGN |
00AF | MACRON |
00B0 | DEGREE SIGN |
00B1 | PLUS-MINUS SIGN |
00B4 | ACUTE ACCENT |
00B6 | PILCROW SIGN |
00B8 | CEDILLA |
00D7 | MULTIPLICATION SIGN |
00F7 | DIVISION SIGN |
ブランク文字 (blank character) とは、 水平空白 (horizontal whitespace) のことである。 Unicode では、ブランク文字は次のいずれかである。
Unicode 3.0 では、次の 18 文字がブランク文字である。
0009 | HORIZONTAL TABULATION | \t control-I |
0020 | SPACE | Zs |
00A0 | NO-BREAK SPACE | Zs |
1680 | OGHAM SPACE MARK | Zs |
2000 | EN QUAD | Zs |
2001 | EM QUAD | Zs |
2002 | EN SPACE | Zs |
2003 | EM SPACE | Zs |
2004 | THREE-PER-EM SPACE | Zs |
2005 | FOUR-PER-EM SPACE | Zs |
2006 | SIX-PER-EM SPACE | Zs |
2007 | FIGURE SPACE | Zs |
2008 | PUNCTUATION SPACE | Zs |
2009 | THIN SPACE | Zs |
200A | HAIR SPACE | Zs |
200B | ZERO WIDTH SPACE | Zs |
202F | NARROW NO-BREAK SPACE | Zs |
3000 | IDEOGRAPHIC SPACE | Zs |
ASCII のブランク文字は、上記の最初の 2 文字、水平タブとスペースである。 Latin-1 では、これにノーブレークスペースが追加される。
Java には「ブランク文字」という概念がないので、 互換性の問題はない。
この SRFI には参照実装が付属している。 以下の URL からダウンロードできる。
私はこのソースコードを「オープンな」著作権とともにネットワーク上においた。 この参照実装のいくつかのコードは古い MIT Scheme の実装を元にしているので、 MIT Scheme の著作権 (これは一般的な BSD スタイルのオープンソース著作権である。 詳細はソースファイルを参照せよ。) も含めている。 その他のコードは scsh またはこの SRFI のために私自身が書いた。 私はこのコードを scsh の著作権の下に指定したが、 これもまた一般的な BSD スタイルのオープンソース著作権である。
このコードは移植性を考慮して書かれているので、 どんな Scheme 実装にも簡単に移植できるだろう。 R4RS には以下の点で準拠していないが、 これらについてはコメントの中で明快に検討している。
error
手続き
values
手続き
check-arg
手続き
let-optionals*
および :optional
マクロ。
define-record-type
フォーム
bitwise-and
%latin1->char
および %char->latin1
.
このライブラリは明快に書かれているし、きちんとコメントされている。 現在のソースは約 375 行のコードと 375 行のコメントおよび空白で書かれている。 また、効率のことも考えて書かれている。 よく使用されるケースでは、高速処理を行っている。
特定の Scheme 実装においてこれ以上最適化できないというわけではない。 参照実装の性能を向上させるための方法については、 コメントの中で言及している。
要するに、このライブラリを採用してよい結果を得られるように、 この参照実装が実装者 (あるいは通常のプログラマ) に苦痛を与えないように書いたのである。
この参照実装では、ASCII/Latin-1 文字セットに対して 256 文字の文字列という、 単純で効率の悪い表現方法を使用している。 文字コードが i の文字が集合に含まれるのは s[i] = ASCII 1 (soh, or ^a) の場合であり、 集合に含まれないのは s[i] = ASCII 0 (nul) の場合である。 16 または 32 バイトのビット列表現を使えば、 ずっと高速で小型になるだろう。 ビット集合を使用した移植性のあるコードを書くには、 ビット演算やバイト ベクタの標準が定まるのを待たなければならない。
Unicode のような大きな文字タイプに対しては、 希薄な表現 (sparse representation) を使用し、 かつ、その Latin-1 サブセットに対しては濃密な 32 バイト ビット セットで表現するのがよいだろう。
The design of this library benefited greatly from the feedback provided during the SRFI discussion phase. Among those contributing thoughtful commentary and suggestions, both on the mailing list and by private discussion, were Paolo Amoroso, Lars Arvestad, Alan Bawden, Jim Bender, Dan Bornstein, Per Bothner, Will Clinger, Brian Denheyer, Kent Dybvig, Sergei Egorov, Marc Feeley, Matthias Felleisen, Will Fitzgerald, Matthew Flatt, Arthur A. Gleckler, Ben Goetter, Sven Hartrumpf, Erik Hilsdale, Shiro Kawai, Richard Kelsey, Oleg Kiselyov, Bengt Kleberg, Donovan Kolbly, Bruce Korb, Shriram Krishnamurthi, Bruce Lewis, Tom Lord, Brad Lucier, Dave Mason, David Rush, Klaus Schilling, Jonathan Sobel, Mike Sperber, Mikael Staldal, Vladimir Tsyshevsky, Donald Welsh, and Mike Wilson. I am grateful to them for their assistance.
I am also grateful the authors, implementors and documentors of all the systems mentioned in the introduction. Aubrey Jaffer should be noted for his work in producing Web-accessible versions of the R5RS spec, which was a tremendous aid.
This is not to imply that these individuals necessarily endorse the final results, of course.
During this document's long development period, great patience was exhibited by Mike Sperber, who is the editor for the SRFI, and by Hillary Sullivan, who is not.
Certain portions of this document -- the specific, marked segments of text describing the R5RS procedures -- were adapted with permission from the R5RS report.
All other text is copyright (C) Olin Shivers (1998, 1999, 2000). All Rights Reserved.
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.