ftype型宣言(sbcl)
ftype型宣言。
あまり使われているのを見ない気がするが、これを使うと「型宣言なしでは遅いけど、宣言をつけるとコードが汚くなる」というような問題を解決できる時があるので、少し書いておく。
以下はsbcl(1.0.34)での挙動。
準備
;; 処理速度を最優先 (declaim (optimize (speed 3) (debug 0) (safety 0) (compilation-speed 0))) ;; unsigned int型 (deftype uint (max) `(integer 0 ,max)) ;; 計時関数 (defun test () (time (dotimes (i 100000) (dotimes (j 10) (dotimes (k 10) (fn2 j k)))))) ; fn2関数は後で定義する
型宣言なし
型宣言なしの関数定義。遅い。
> (defun fn (a b) (* a b)) --> FN > (defun fn2 (a b) (+ (fn a b) (fn b a))) --> FN2 > (test) Evaluation took: 0.165 seconds of real time 0.164010 seconds of total run time (0.164010 user, 0.000000 system) 99.39% CPU 523,636,114 processor cycles 0 bytes consed --> NIL
型宣言あり
型宣言あり。速いけど、関数定義が(宣言なしのものに比べ)汚い。
> (defun fn (a b) (declare ((uint 10) a b)) (* a b)) --> FN > (defun fn2 (a b) (+ (the (uint 100) (fn a b)) (the (uint 100) (fn b a)))) --> FN2 > (test) Evaluation took: 0.093 seconds of real time 0.096006 seconds of total run time (0.096006 user, 0.000000 system) 103.23% CPU 295,587,465 processor cycles 0 bytes consed --> NIL
型宣言あり(ftype版)
ftype版。関数定義は型宣言なしのものと変わらない。
;; fn関数の型を宣言 (declaim (ftype (function ((uint 10) (uint 10)) (uint 100)) fn)) > (defun fn (a b) (* a b)) --> FN > (defun fn2 (a b) (+ (fn a b) (fn b a))) ; 上の宣言でfn関数が(uint 100)を返すことが分かっているので、(the ...)は不要 --> FN2 > (test) Evaluation took: 0.094 seconds of real time 0.096006 seconds of total run time (0.096006 user, 0.000000 system) 102.13% CPU 297,934,421 processor cycles 0 bytes consed --> NIL
上のような簡潔な関数だと、どちらでもあまり変わらないような気もするが、少し複雑な関数定義の場合は、特に(the ...)の有る無しで、結構見やすさが変わってくるように思う。
たまに便利。
ただ、上の例でもそうだが、(declaim (ftype ...))を使って関数定義の外で宣言をした場合、定義内で宣言するのに比べて、生成されるコードが若干だが非効率になる傾向があるようなので、(最適化が必要なケースでは)結局あまり使えないかもしれない...。