あどけない話

Internet technologies

cdr の decrement

Lisp の car や cdr が、以下の略であることぐらい、Lisp をかじったことのある人なら知っているでしょう。

  • Contents of the Address part of Register number
  • Contents of the Decrement part of Register number

Lisp が最初に実装された IBM 704 の機械語には、address の部分と decrement の部分があったこともよく語られています。address はすぐに分りますが、decrement とは何でしょうか?

長年の疑問を和田先生に聞いてみました。

和田先生の答え

ループを作るときにインデックス・レジスタから decrement の部分を引く

for 文に例えるなら、インデックス i から引く数であると言うのです。そうだったんですかぁ。詳しく調べてみました。

IBM 704

IBM 704 Manual of Operationの 7 ページと 8 ページに詳しく載っていました。

  • 1ワードは36ビット
  • アドレスは、4096ワード
    • すなわち 12 ビットで表現可能

1ワードを機械語の命令として使うとき、命令にはタイプAとタイプBがあります。
タイプAの命令は、以下のような構造を持っています。

                            1 1 1 2 2                   3
  0 1 2 3 ................. 7 8 9 0 1 ................. 5
 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 | | | | | | | | | | | | | | | | | | | | | | | | | | | | |
 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  <---> <-------------------> <---> <------------------->
  prefix      decrement        tag         address
prefix
0〜2の3ビット、命令の種類
decrement
3〜17の15ビット、インデックス・レジスタから引く値
tag
18〜20の3ビット、インデックス・レジスタを指す
address
21〜35の15ビット、アドレス

タイプ A の命令は、1 ビット目と 2 ビット目のどちらか、あるいは両方が 1 です。両方が 0 だと、タイプ B の命令になります。だから、タイプ A の命令は 6 種類ですね。

驚愕の事実

car よりも cdr の方が、前にあるんですね。。。History of Lisp を読むと、cpr と ctr もあったようです。

ruby-mode

Ruby には Emacsruby-mode.el が付随しています。README などに設定方法が書いてないので、ほとんどの人は、検索して設定例を探すでしょう。すると、こんな不効率な設定方法が見つかります。

(autoload 'ruby-mode "ruby-mode" "Mode for editing ruby source files" t)
(setq auto-mode-alist (append '(("\\.rb$" . ruby-mode)) auto-mode-alist))

どうして、こうもみんなそろって間違うんだろうと思っていましたが、inf-ruby.el のコメント部分が原因だと分りました。セル・イメージのある Lisper なら append を使わずに、cons を使います。

(autoload 'ruby-mode "ruby-mode" nil t)
(setq auto-mode-alist (cons '("\\.rb$" . ruby-mode) auto-mode-alist))

まぁ、今回の例だと、セルの無駄遣いは微々たるものですが、必要に迫られない限り append は使わないという習慣を身につける方がいいでしょうね。

Electric な Ruby モードの end

Ruby に付いてくる ruby-electric.el では、たとえば class と打った後に空白を入れると end が挿入されます。僕が欲しいエレクトリックな機能は、これじゃありません。

end と押すと対応する単語に飛んでほしいのです。というわけで作ってみました。別ファイルにすると設定方法を説明するのが面倒なので、とりあえず以下を .emacs に入れて下さい。

(defvar ruby-elct-regex "def\\|if\\|class\\|module\\|unless\\|case\\|while\\|do\\|until\\|for\\|begin\\|end")

(defun ruby-elect-end ()
  (interactive)
  (insert "d")
  (when (and (char-equal (char-before (1- (point))) ?n)
	     (char-equal (char-before (- (point) 2)) ?e))
    (ruby-indent-command)
    (let ((orig (point)) open)
      (forward-char -3)
      (when (looking-at "\\bend\\b")
	(setq open (ruby-elect-begin))
	(when open
	  (goto-char open)
	  (sit-for 0.3)))
      (goto-char orig))))

(defun ruby-elect-begin ()
  (let ((level 0))
    (catch 'loop
      (while (re-search-backward ruby-elct-regex nil t)
	(cond
	 ((string= (match-string 0) "end")
	  (setq level (1+ level)))
	 (t
	  (if (= level 0) (throw 'loop (match-beginning 0)))
	  (setq level (1- level))))))))

(add-hook 'ruby-mode-hook
	  (lambda ()
	    (define-key ruby-mode-map "d" 'ruby-elect-end)))

あんまりテストしていません。飛んで一瞬留まる方法も正しいのか調べていません。

こういう構想は、Pascal を使っていた学生時代に持っていたんですが、そのときの実力では実現できませんでした。

今日、書いてみたら 10 分ぐらいでできたので、成長したんだなぁと思います。。。(Pascal を使わなくなったとはいえ、構想の実現に 15 年以上もかかるとは。。。)