somemo programming etc.

プログラマ、雑記、プログラミング関係はLinkから、数式はこっちでまとめていることが多い

【Emacs】前置引数とinteractiveの関係

前置引数とinteractiveの関係について調べました。

前置引数

前置引数とは、C-uによって指定される繰り返しの回数です。

interactive

前置引数を利用するには、関数内でinteractiveのpを使用します。pは、小文字だけでなく大文字も存在しそれぞれで意味が異なります。

テスト用コード

以下のコードを作成しました。

(defun interactive-p-test (n)
  (interactive "p")
;;  (interactive "P")
  (message "n:%s, (consp n):%s" n (consp n))
)

(global-set-key (kbd "\C-z") 'interactive-p-test)

関数を作成し、キーバインドしています。内容は引数の内容と引数がでコンスセルであるかを表示しています。

コンスセルとは、(1 . 2)のようなS式のペアです。2の部分をコンスセルにすると、(1 . (2 . 3))となります。このようにコンスセルの右側にコンスセルをつなげていくこの構造を、リスト(list)といいます。リストは、この書き方を(1 2 3)と省略しているだけだったのです。3の部分も実はリストであり、(3 . nil)というコンスセルの省略記法です。

結果

pを小文字大文字で試した結果です。上が小文字で、下が大文字です。まずは、単純に評価した場合です。

(interactive-p-test)

Debugger entered--Lisp error: (wrong-number-of-arguments (lambda (n) (interactive "P") (message "n:%s, (consp n):%s" n (consp n))) 0)
  interactive-p-test()
  eval*1
  eval-last-sexp-1(t)
  eval-last-sexp(t)
  eval-print-last-sexp()
  call-interactively(eval-print-last-sexp nil nil)

小文字と大文字で試しましたが、同じ結果になりました。引数がないので当たり前ですね。前置引数をとるforward-charを評価したときは、エラーも起きずに1文字進みました。これは、以下のように定義されているからだと思います。

(forward-char &optional N)

そもそもcで書かれているものであり、引数がオプション扱いのためです。評価時に引数を省略して使いたい場合の見本になりますね。

次は、前置引数のデフォルト値:4を引数に渡して評価した場合と、明示的に5を渡して評価した場合の結果です。

(interactive-p-test 4)

"n:4, (consp n):nil"
"n:4, (consp n):nil"
(interactive-p-test 5)

"n:5, (consp n):nil"
"n:5, (consp n):nil"

数字は違えど、すべて同じ結果になっています。評価時の引数はコンスセルではなくS式であることが分かりました。

次からは、キーバインドしたC-zからの実行です。まずは、前置引数なしの結果です。

C-z

n:1, (consp n):nil
n:nil, (consp n):nil

小文字pの場合、前置引数なしでも1が表示されています。これは、数値前置引数といって、引数に行う回数をS式として格納してくれます。大文字Pの場合は、nilになっています。ここでは説明を省略します。また、conspの結果は、両方ともS式なのでnilになっています。

次に、C-uを入力するが数値は渡さない場合です。

C-u C-z

n:4, (consp n):nil
n:(4), (consp n):t

小文字pの場合、C-uだけでもデフォルト値の4が表示されています。これは、数値前置引数の場合に、デフォルト値を解釈して格納していることが分かりました。大文字Pの場合は、(4)になっています。これは、(4 . nil)でありますので、小文字とはコンスセルになっています。よって、大文字はconspの結果がtとなりました。Pは、前置引数をそのまま受け取り格納するようです。

次は、明示的に数値を渡す場合です。

C-u 5 C-z

n:5, (consp n):nil
n:5, (consp n):nil

両方とも同じ結果になりました。前置引数をそのまま受け取っても、明示的である場合はS式になることが分かりました。

また、C-u C-uでは16と評価されます。予想通りでした。C-u C-u 5と入力した場合、20だと思っていたのですが5でした。これですっきりしました!

*1:interactive-p-test