UNF: Common Lisp版

しばらく(私的には)プログラミングから離れた生活が続いていたので、肩慣らしを兼ねてUNF(Unicode正規化ライブラリ)common lisp(cl-unf)を作成。

いつものようにSBCLで作成し、SBCL向けに最適化されているが、UTF-32に対応しているなら、他の処理系でも一応動作はするはず。

使用例

(require :unf)

;; NFKC正規化
(unf:normalize "ABCガギグ" :nfkc)
--> "ABCガギグ"

;; 上の簡略版
(unf:nfkc "ABCガギグ")
--> "ABCガギグ"

計時

C++版のUNFとJavajava.text.Normalizerとの比較。
C++及びJavaの計時用のプログラムや使用しているデータ(テキスト)に関してはUNF : Unicode正規化ライブラリ - sileの日記を参照のこと。
cl-unf用には、以下のプログラムを使用。

;; sbcl-1.0.40

;; テキスト読み込み関数
(defun read-lines (path)
  (with-open-file (in path)
    (loop FOR line = (read-line in nil nil)
          WHILE line
          COLLECT line)))

;; 計時用データ
(defvar *souseki* (read-lines "souseki.txt"))
(defvar *keizai* (read-lines "keizai.txt"))
  
;; 計時関数
(defun unf-time (lines form)
  (time 
   (dolist (line lines 'done)
     (unf:normalize line form))))

;; 実行例
(unf-time *keizai* :nfd)  ; NFDで正規化

Evaluation took:
  0.037 seconds of real time
  0.036002 seconds of total run time (0.036002 user, 0.000000 system)
  [ Run times consist of 0.004 seconds GC time, and 0.033 seconds non-GC time. ]
  97.30% CPU
  72,473,421 processor cycles
  11,477,392 bytes consed
--> DONE

結果:

処理時間(秒): souseki.txt*1処理時間(秒): keizai.txt*2
Normalizer(Java)NFD: 0.063s
NFC: 0.040s
NFKD: 0.067s
NFKC: 0.069s
NFD: 0.037s
NFC: 0.030s
NFKD: 0.043s
NFKC: 0.048s
UNF(C++)NFD: 0.133s
NFC: 0.073s
NFKD: 0.131s
NFKC: 0.078s
NFD: 0.048s
NFC: 0.027s
NFKD: 0.056s
NFKC: 0.043s
cl-unf(Common Lisp)NFD: 0.060s
NFC: 0.033s
NFKD: 0.061s
NFKC: 0.061s
NFD: 0.027s
NFC: 0.018s
NFKD: 0.032s
NFKC: 0.036s

この結果を見ると、cl-unfが三者の中で一番速くなっている。
C++版はUTF-8文字列、JavaのNormalizerは(おそらく)UTF-16文字列、cl-unfはUTF-32文字列、をそれぞれ対象としており、Unicode正規化のような処理の場合は、余計な変換等が不要な分だけUTF-32文字列を直接扱う方が有利なので、それほど意外な結果でもないかもしれないが、Javaより速かったのは少し驚いた。
計時方法や入力テキストデータによって、また変わってくるとは思うけど。

*1:青空文庫より取得した夏目漱石の小説テキスト。9.3MB

*2:Yahoo!ブログの「ビジネスと経済」カテゴリに属するブログ記事テキスト。3.2MB