第25章「周辺分野」

概要

ANSI Common Lispの第25章「周辺分野」を説明します。

時間に関するオペレータ

ANSI Common Lispの第25章は Environment で、直訳すると「環境」ですが、要はLispの言語そのものとは関係がない周辺の分野についてまとめてある章です。そこで、このページでは主に時間に関するオペレータと処理系に関するオペレータを紹介します。

まず、これまでにも何回か使ってきた、現在時刻を取得するオペレータです。時刻に関する情報は言語の中にはなく、ハードウェアとOSによって提供されるため、Lispの外の問題です。get-universal-time関数とget-decoded-time関数が現在の時刻を取得してくれます。
(get-universal-time)
; => 3722764826

(get-decoded-time)
; => 34 ;
;    20 ;
;    22 ;
;    20 ;
;    12 ;
;    2017 ;
;    2 ;
;    NIL ;
;    -9

なお、ユニバーサルタイムと日時の情報を相互に変換するにはdecode-universal-time関数とencode-universal-time関数を使います。

もう一つ、時間に関してよく使うオペレータがtimeマクロです。これは、引数を評価する時間を計測するマクロです。
(time (loop for i from 1 to 1000000 summing i))
; Real time: 8.329995 sec.
; Run time: 8.326985 sec.
; Space: 9656 Bytes
; => 500000500000

どのような情報が表示されるかは処理系依存ですが、とても便利なマクロです。

処理系に関するオペレータ

処理系に関するオペレータは大きく分けて3種類紹介します。

まず、traceマクロです。このマクロは特定の関数の実行状況をトレースして表示してくれます。末尾再帰を多用するSchemeではよく学習に使われますが、Common Lispにはloopマクロがあるので、あまり学習目的でも使われていないかもしれません。

例えば、再帰を用いてリストの要素数を求める関数を書いてみます。
(defun my-length (list n) 
  (if (endp list) 
      n 
      (my-length (cdr list) (1+ n))))
; => MY-LENGTH
この関数をトレースしてみます。
(trace my-length)
;; Tracing function MY-LENGTH.
; => (MY-LENGTH)

(my-length '(a b c d e) 0)
; 1. Trace: (MY-LENGTH '(A B C D E) '0)
; 2. Trace: (MY-LENGTH '(B C D E) '1)
; 3. Trace: (MY-LENGTH '(C D E) '2)
; 4. Trace: (MY-LENGTH '(D E) '3)
; 5. Trace: (MY-LENGTH '(E) '4)
; 6. Trace: (MY-LENGTH 'NIL '5)
; 6. Trace: MY-LENGTH ==> 5
; 5. Trace: MY-LENGTH ==> 5
; 4. Trace: MY-LENGTH ==> 5
; 3. Trace: MY-LENGTH ==> 5
; 2. Trace: MY-LENGTH ==> 5
; 1. Trace: MY-LENGTH ==> 5
; => 5

この関数をコンパイルして、もう一度評価してみます。
(compile 'my-length)
; => MY-LENGTH ;
;    NIL ;
;    NIL

(my-length '(a b c d e) 0)
; 1. Trace: (MY-LENGTH '(A B C D E) '0)
; 1. Trace: MY-LENGTH ==> 5
; => 5

関数呼び出しの処理が大幅に短縮化されているのが分かります。traceマクロは特定の関数が呼び出される時に、その呼び出しと評価のプロセスを表示するものです。

traceは関数の実行状況をトレースするオペレータですが、ANSI Common Lispではドキュメントを表示するオペレータも定められています。それがdocumentation総称関数で、関数やマクロなどの定義の中に含まれる「ドキュメント文字列」を取り出すことができます。

例えば、Alexandriaのwith-gensymsマクロは非常によく利用されますが、そのドキュメントをみたければ、ASDFなどでAlexandriaをロードしてから、以下のようにdocumentation総称関数を呼び出します。
(documentation 'alexandria:with-gensyms 'function)
; => "Binds each variable named by a symbol in NAMES to a unique symbol around
; FORMS. Each of NAMES must either be either a symbol, or of the form:
; 
;  (symbol string-designator)
; 
; Bare symbols appearing in NAMES are equivalent to:
; 
;  (symbol symbol)
; 
; The string-designator is used as the argument to GENSYM when constructing the
; unique symbol the named variable will be bound to."
with-gensymsはマクロですが、documentationの第2引数は'functionで構いません。
最後に紹介するのは、インタプリタのみで利用できるコマンドです。

このサイトでも何度か使ってきましたが、*スペシャル変数は一つ前の式の評価結果を使うことができます。
(+ 1 2)
; => 3

*
; => 3

返り値ではなく、評価した式自体を取り出したい場合は+スペシャル変数を使います。
(+ 1 2)
; => 3

+
; => (+ 1 2)

手前の評価式ではなく、今の評価式自体を取り出したい場合は-スペシャル変数を使います。
(format t "~a" -)
; (FORMAT T ~a -)
; => NIL

最後に、/というスペシャル変数もあります。これは、直前の評価式の返り値が多値の時に、リストにして使えるようにするものです。
(get-decoded-time)
; => 32 ;
;    17 ;
;    23 ;
;    20 ;
;    12 ;
;    2017 ;
;    2 ;
;    NIL ;
;    -9

/
; => (32 17 23 20 12 2017 2 NIL -9)

これらのスペシャル変数は処理系をインタプリタとして使う場合に利用できるため、ソースコードの中に含めるべきではありません。

0 件のコメント :

コメントを投稿