« August 2014 | Main | October 2014 »

September 19, 2014

プログラミングを始めるには(35)

かなり說明の順番が狂ったかもしれないが、Lispのデータについて書いてみようと思う。

だいぶ前に書いたと思うが、初心者向けのプログラマ硏修では、データのことを器だと思えと敎えられる。器には形があるので、それにあったものを入れることが出來る。合わないものを入れることは出來ない...これはプログラムで扱うデータのことを言い表す上で一見もっともらしい說明だが、明らかに靜的型付けのプログラミング言語を前提としているのではないか。そういうプログラミング言語では、整數型のデータに文字列を設定すると暴走することがあるが、たいていコンパイルの段階でエラーやワーニングを出してくれるので氣付く。

プログラム設計の段階では特定のプログラミング言語に依存しない記述を理想としているらしいので、そこに記載されているデータが何型をしているのか不明である。したがって、プログラム設計が終わってもそこで扱うデータの形が分からないという不思議な狀態になっている。プログラムが入力データを處理して出力データを作り出すものだとすると、データの形も決めないで設計書を書いていることになり、プログラマはそんな設計書を渡されても、「こんな處理で大丈夫なの?」という疑問を解消できないまま、疑心暗鬼でプログラムを作る羽目になる。そんなプログラムを結合してシステムテストをしても動くはずがない。

データがどういう形をしていれば良いかは設計が決めるべきところだが、データ型はプログラミング言語に依存するものであり、プログラム設計書ではデータの内容や論理構造を定義するにとどまることが多い。また、靜的型付け言語は、あるデータがどういう形をしているかをデータに尋ねる機能がないので、設計者やプログラマが管理する必要がある。そういう意味で、たとえばCのようなプログラミング言語は眞に面倒臭い。これはC++でもJavaでも大差はない。

Lispの場合は、あるデータが器としてどのような形をしているかをあまり氣にする必要が無い。しいて言えばすべてLispオブジェクト型である、ということになる。プログラム上のデータとしては、Lispオブジェクトをシンボルにバインドして扱う。したがってLispではシンボルがデータであるかのように記述することが多い。

(defvar test-value 123)
test-value

test-value
123

(set 'test-value "123")
"123"

test-value
"123"

(setq test-value '(1 2 3))
(1 2 3)

test-value
(1 2 3)

これはC言語における、ポインタと實體の關係に似ていると思われるかもしれないが、C言語のポインタとLispのシンボルは全く違うものと考えた方が良い。

| | Comments (0) | TrackBack (0)

September 03, 2014

プログラミングを始めるには(34)

Emacsのプロセス閒通信を使ったプログラムはあまり書いたことがないということで、何か適當なネタはないか、いろいろと考えていたが大した考えは浮かんでこない。それとgnupackをあきらめてCygwinを入れてみたものの、Cygwin端末からマニュアルを參照(man)すると何やらコアダンプしているようで參照出來ない。まあインストールに問題があるのだろうが、それを動くようにあれこれ調べるより、せっかく假想マシンでちゃんと動いているFedoraCentOSUbuntuがあるので、そっちでやった方が良いのではないかと思い始めた。なんか以前もこんなことを書いた氣がする。まあ、便利なUNIXコマンドはelispで書いてみるという樂しみもあるのだが...。

假想マシンとMeadowの閒を行ったり來たりするのはちょっと面倒なので、TeraTermから假想マシン上のFedorasshログインしてみる。これは何も問題なく出來るし、X無しのCygwin代わりになる。TeraTermから起動するEmacsではAltキーが使えないので多少不便だが、むかしはAltよりはESCを多用していたので問題ない。「stty erase ^h」はhelpの參照が不便なので「stty erase ^?」に變更した。

さて、手始めにshellと對話してみよう。

Ws000099


對話の手順としては、「C-x bswitch-to-buffer)」で任意の名前(ここではfoo)のバッファを作り、start-process/bin/bashを起動するだけである。これがEmacsの外にあるプロセスと對話する最も單純な例ではないだろうか。最初にsetenvでプロンプトに空文字列を設定しているのは、fooという名前を付けたプロセスバッファに不要なプロンプトが表示されるからである。バッファから入力を受け付けるわけではないので、プロンプトが表示されると邪魔になるだけだ。起動したbasheofを受け取るまでコマンド入力待ちとして動き續けるので、慌ててprocess-send-stringする必要はない。ただし、コマンド文字列の最後に「\n」を付け忘れると送信したコマンドを實行しない。忘れたら「\n」だけ送ってやるとコマンドを實行する。

(defconst my-start-shell-name "my-start-shell")

(defun my-start-shell ()
  (interactive)
  (or (string= "" (getenv "PS1"))
      (setenv "PS1" ""))
  (let* ((buffer (get-buffer-create my-start-shell-name))
         (proc (start-process my-start-shell-name buffer "/bin/bash")))
    (set-process-filter proc 'my-start-shell-filter)
    (set-process-sentinel proc 'my-start-shell-sentinel)
    (pop-to-buffer buffer)))

(defun my-start-shell-input (command)
  (interactive "s$ ")
  (let* ((proc (get-process my-start-shell-name))
         (buffer (process-buffer proc)))
    (process-send-string proc (concat command "\n"))
    (pop-to-buffer buffer)))

(defun my-start-shell-filter (proc string)
  (with-current-buffer (process-buffer proc)
    (goto-char (marker-position (process-mark proc)))
    (insert-before-markers string "\n")))

(defun my-start-shell-end ()
  (interactive)
  (let* ((proc (get-process my-start-shell-name))
         (buffer (process-buffer proc)))
    (process-send-eof proc)))

(defun my-start-shell-sentinel (proc status)
  (and (string= "exit" status)
       (kill-buffer (process-buffer proc))))

shellとの對話を思いっきり安直に作るとこんな感じだろうか。別にこんなものを作らなくても、shell-commandさえあれば十分と思う人も居るだろう。しかし、非同期プロセスを使用したelispのプログラムを書いたことがない人は一度こういう他愛のないプログラムを書いて試してみると良い。おそらく想像していたのと違った動きをするのが分かるはずだ。私の場合、process-send-eofを實行後にprocess-bufferを消そうとしたら、filterファンクションが呼び出されてバッファに文字列を書き込むことが出來ずエラーとなった。なのでsentinelファンクションを登錄して、プロセスがexitしたらバッファを消そうとしたのだが、結局sentinelファンクションは呼び出されず、バッファは殘ったままとなった。ただ、この動作は私の處理がおかしいからそうなったのか、處理は妥當でもそういう動きになるのかは分からない。いずれにしてもprocess-send-eofした後にfilterファンクションが呼ばれるのは意外だった。

| | Comments (0) | TrackBack (0)

« August 2014 | Main | October 2014 »