Archive for the ‘プログラミング’ Category

コンパイルしたらあなたと合体した

月曜日, 8月 15th, 2011

Common Lispでは、
よく似てるけどeqlではない2つのリテラルを含むソースをコンパイルしたら、
コンパイル後には2つのリテラルがeqlになることがあるそうです。
そのことを「2つのリテラルはコンパイラによって合体された」と言うとか何とか。

ちゃんとした定義はこちら
(“similar”は「似ている」ではなく厳密な定義が与えられています)

coalesce v.t. (literal objects that are similar) to consolidate the identity of those objects, such that they become the same object. See Section 3.2.1 (Compiler Terminology).

The term coalesce is defined as follows. Suppose A and B are two literal constants in the source code, and that A’ and B’ are the corresponding objects in the compiled code. If A’ and B’ are eql but A and B are not eql, then it is said that A and B have been coalesced by the compiler.

ということで実際に試してみました。
使用したのはSBCL 1.0.50です。

(defvar a '(a b c))
(defvar b '(a b c))

こちらのソースコードをloadして (eql a b) を試したところ、NILになりました。
しかし、compile-fileしたものをloadすると、 (eql a b) の値はTになりました。
まさに2つのリテラルが合体したという感じです。

ちなみに、CLISP、Clozure CL、Allegro CLではcompile-fileしてもNILのままでした。
smilarなリテラルがたくさん出てくるようなソースコードでは、
リテラルを合体した方が、コンパイル後のバイナリファイルのサイズや、
ロードした際のヒープの使用量を節約できそうですが、
コピペを繰り返したソースでもない限り、効果は薄いような気がします。
誰かcoalesceの効果の程を測った人とかいないんでしょうかね。

今日は観鈴ちんの誕生日なのでBiwaSchemeでゲーム作った

土曜日, 7月 23rd, 2011

先日、後輩が
風子の誕生日を祝ったのに、観鈴ちんの誕生日を忘れてた人がいるらしいですよ」
と私の古傷をえぐってきたので、
傷を癒すためにゲームを作った。

遊ぶ

どっかで見たことがあるようなゲームな気もするけど、
たぶん気のせいだろう。

CLとSchemeのfloat紛らわしい話

金曜日, 7月 22nd, 2011

突然ですが、(/ 22 7.)この式の値は何でしょうか。
7の後にドットがあるのがポイントです。

実際に評価してみると、
Common Lispでは22/7といったように分数になり、
Schemeでは3.142857142857143というように小数になります。

CLHSのここを見れば分かる通り、CLでは数の後にドットのみが続くものはintegerとして扱い、
R5RSのここにあるように、Schemeではintegerとして扱わないようです。
あまり困ることもないでしょうが紛らわしい話です。

#ちなみに本日7/22は「円周率近似値の日」らしいですよ

C++の参照を使ってもcall by referenceにはならない

日曜日, 6月 5th, 2011

* はじめに *
ここ最近(昔から?)、C言語の本や記事などに
「ポインタを使うことで参照呼び(call by reference)が…」
なんて書いてあると、
「C言語には値呼び(call by value)しかないくぁwせdrftgyふじこlp…」
などというツッコミで荒れるみたいでが、
C++の本や記事に「参照呼び」と書いてあることで荒れるところを
(少なくても私は)見たことがないのが不思議なので、このエントリを書きます。

* 参照でcall by reference? *
C++でこんなコードを書くと、

int f(int& x) { ... }
int g() {
  ...
  f(a + 1);
  ...
}

こんな感じのエラーが出ますと。

error: invalid initialization of non-const reference of type ‘int&’ from a temporary of type ‘int’
error: in passing argument 1 of ‘int f(int&)’

C++がこういう仕様であること自体には何も問題はないと思います。
でも、これがエラーになるということは、C++の参照を使っても、
call by referenceにはなっていないということです。

* call by referenceの定義 *
私が説明をつらつらと書いても納得してもらえなさそうなので、
有名どころから引用します。

参照呼び

引数を参照呼び(番地呼びまたは記憶場所呼びともいう)にすると,一般には,
呼出側の手続きは,呼び出される側に対して,次のように実引数の記憶場所
の番地を渡す.

  1. 実引数が名前または左辺値をもつ式であれば,その左辺値自身を渡す.
  2. しかし,実引数がa+bや2のような式であれば,左辺値はないので,式を
    評価して,その値を新しい記憶場所に格納し,その番地を渡す.

(『コンパイラII 原理・技法・ツール』 初版 517ページ)

ついでにもう一冊

This parameter-passing mechanism is called call-by-reference. If an operand is simply a variable reference, a reference to the variable’s location is passed. The formal parameter of the procedure is then bound to this location. If the operand is some other kind of expression, then the formal parameter is bound to a new location containing the value of the operand, just as in call-by-value.

(“Essentials of Programming Languages” second edition, p.109)

お分かりいただけたでしょうか。
call by referenceでは、上記プログラムのようなa+1みたいな式も渡せないといけないのです。
つまり、C++の参照をつかってもcall by referenceにはならないということですね。
ちなみに、その昔FORTRANではcall by referenceしかなかったので、a+0のような式を書くことで、
call by valueをシミュレートするという技があったらしいです。
今時のFORTRANがどうなっているかは知りません。

* おわりに *
Javaも「参照」という言葉を使っているためか、
時折「Javaの参照呼びが云々」と書かれることがありますが、
これには「それは参照の値呼びだ!」という、
分かりやすく分かりにくいツッコミが入るところを何度か見たことがあります。
しかし、冒頭にも書いたとおり、C++の参照に関してはツッコミが入ったところを見たことがありません。
単に私が見ているところが悪いだけなのか、それともC++特有の文化があるのか(たとえばC++の仕様書に”call by reference”と書いてあるとか)。

謎です。

みなさんお待ちかね「新・ニコ動でLisp」アップデートですよ

火曜日, 5月 31st, 2011

久しぶりに新・ニコ動でLispを見てみたら、

数値比較したいです先生

というコメントがあったので、数値比較のための関数
=, <, >, <=, >=
を追加しました。

それから、階乗のプログラムを書く人が後を絶たないので、乗算も追加しました。
皆がどんなプログラムを試したかが残るというのはなかなか便利なものです。

loopの:doと:while (:until) の話

日曜日, 5月 22nd, 2011

comp.lang.lispのとある記事を見てて気づいたんですが、
(loop :until ... :do ...)
なんて書き方ができたんですね。
私は今まで
(loop :do ... :until)
のようなコードしか見たことがありませんでした。
当然、:untilだけでなく:whileでも同じようなことが出来るみたいです。

で、これらは同じ意味かと思ったら違うみたいです。

(loop :do (print 'oh!) :until t)
; OH!
; => NIL

(loop :until t :do (print 'oh!))
; => NIL

(macroexpand '(loop :do (print 'oh!) :until t))
; =>
; (MACROLET ((LOOP-FINISH NIL (SYSTEM::LOOP-FINISH-ERROR)))
;  (BLOCK NIL
;   (LET NIL
;    (MACROLET ((LOOP-FINISH NIL '(GO SYSTEM::END-LOOP)))
;     (TAGBODY SYSTEM::BEGIN-LOOP
;      (PROGN (PROGN (PRINT 'OH!)) (WHEN T (LOOP-FINISH)))
;      (GO SYSTEM::BEGIN-LOOP) SYSTEM::END-LOOP
;      (MACROLET
;       ((LOOP-FINISH NIL (SYSTEM::LOOP-FINISH-WARN)
;         '(GO SYSTEM::END-LOOP))))))))) ;
; T

(macroexpand '(loop :until t :do (print 'oh!)))
; =>
; (MACROLET ((LOOP-FINISH NIL (SYSTEM::LOOP-FINISH-ERROR)))
;  (BLOCK NIL
;   (LET NIL
;    (MACROLET ((LOOP-FINISH NIL '(GO SYSTEM::END-LOOP)))
;     (TAGBODY SYSTEM::BEGIN-LOOP
;      (PROGN (WHEN T (LOOP-FINISH)) (PROGN (PRINT 'OH!)))
;      (GO SYSTEM::BEGIN-LOOP) SYSTEM::END-LOOP
;      (MACROLET
;       ((LOOP-FINISH NIL (SYSTEM::LOOP-FINISH-WARN)
;         '(GO SYSTEM::END-LOOP))))))))) ;
; T

:doを先に書いた場合は、その内容が必ず1度は評価されるのに対し、
:whileや:untilを先に書いた場合はテストが先に行われるため、
:doの内容が評価されない場合もあるみたいです。

CLHSを眺めたところ、loopは基本的に書かれたとおりの順番に動くみたいです。
次のように気持ち悪いことも出来ます。

(loop :for i :from 0 :do (print i) :while (< i 3)  :collect i)
; 0
; 1
; 2
; 3
; => (0 1 2)

:whileを:collectより先に書いてあるので、リストに3は含まれません。
もちろん、:whileと:collectの順番を逆にすると、リストに3が含まれます。
loop恐るべし。

Proof GeneralでCoq

金曜日, 5月 6th, 2011

プログラミング Coqを参考に、CoqとCoqIDEを入れて試してみたが、
どうもCoqIDEの動きがおかしい。時々固まったり変な動作をする。
周りの人に相談したところ
CoqIDEは使いものにならないからProof Generalを使え」
と言われたのでProof Generalとやらを入れてみた。
Emacs上で動く便利なものらしい。

以下はただのメモ書き。

* ダウンロード *
公式サイトからダウンロードする。
現時点のバージョンは4.0のようだ。

* インストール *
公式サイトの説明に従い、ファイルを展開して、
.emacsにload-fileを追加。

試しにEmacsで拡張子がvのファイルをひらいてみるが、
「Emacsのバージョンが23.1じゃないからダメ」
と怒られる。これまた公式サイトの説明に従いmake。

make clean
make compile EMACS=`which emacs`

しかし、make時にエラーが起こる。
「`font-lock-beginning-of-syntax-function’はobsoleteだから’syntax-begin-function’を使え」
と言ってるようなので、何も考えずにソースコード中の
font-lock-beginning-of-syntax-functionをsyntax-begin-functionに置換。
makeはこれで通った。

* 起動 *
Emacsで拡張子がvのファイルを開くと自動的にProof Generalが立ち上がる。
公式サイトのマニュアルを適当に眺めたところ、

  • C-c C-nで証明をひとつ進める
  • C-c C-uで証明をひとつ戻す

らしい。今の私の知識ではどうせこれしか使わない。後のものは後で覚えよう。

C-c C-RETでカーソルの位置まで証明を進めるみたいだが、
これをやろうとすると「C-c RET is undefined」と言われてしまう。
controlを押しながらreturnを押してるはずなんだけど、なんでだろう。
まあ、誰か詳しい人が解決法を教えてくれるだろう。

* 疑問点 *
CoqIDEでは「Eval simpl in xxx」みたいなものを、
専用の場所に書いていたけど、Proof Generalではどこに書けばいいんだろう。
ソース中に書いたら動いたけど、ソースとは分離したい。

WiLiKiにファイルアップロード機能を付けた

木曜日, 5月 5th, 2011

前回に続いて、こんどはファイルのアップロード機能を付けました。

添付ファイルについて

セキュリティとか何も考えてないので結構怖いけど、
まあ、誰も使わないので今のところ大丈夫(?)でしょう。

前回とのdiffを貼っつけておきます。
(続きを読む…)

琵琶湖をPostScriptで描く

木曜日, 4月 21st, 2011

先日の日曜日、自転車で琵琶湖をまわってきました。
今年はAndroid端末 HTC DesireでMy Tracksというソフトを動かし、
GPSのログを記録しながら走ってみました。

で、せっかくなの、でそのログを使ってPostScriptで琵琶湖を描いてみました。

PDF
PostScript

なかなかいい感じに出来ました。
滋賀県民以外なら「これが琵琶湖の正確な形です」といっても騙せそうです。
(滋賀県民には「奥琵琶湖の形がおかしい」などとツッコまれそうだ。)

MyTracksは緯度経度のログをCSV形式で、出力することができるので、
それをCommon Lispで読み込み、PostScriptに変換という流れで作りました。
変換につかったプログラムは以下のとおりです。

(defun skip-elms (stream n)
  (when (char= (peek-char nil stream) #\,)
    (read-char stream))
  (dotimes (i n)
    (unless (char= (peek-char nil stream) #\,)
      (read stream))
    (assert (char= (read-char stream) #\,))))

(defun read-latlon (line)
  (let (lat lon)
    (with-input-from-string (s line)
      (skip-elms s 2)
      (setf lat (read-from-string (read s)))
      (skip-elms s 0)
      (setf lon (read-from-string (read s))))
    (values lat lon)))

(defun convert-latlon (lat lon)
  (values (+ 50 (* (- (* lat 111) 3881) 11))
          (+ 50 (* (- (* lon 91) 12363) 11))))

(defun doit ()
  (with-open-file (s "biwa.csv")
    (with-open-file (out "biwa01.ps" :direction :output :if-exists :supersede)
      (write-line "%!" out) (write-line "gsave" out)
      (dotimes (_ 3611)
        (read-line s))
      (do ((i 3612 (1+ i)))
          ((>= i 30334) t)
        (multiple-value-bind (lat lon)
            (multiple-value-call #'convert-latlon
              (read-latlon (read-line s)))
          (if (= i 3612)
              (format out "~A ~A moveto~%" lon lat)
              (format out "~A ~A lineto~%" lon lat))))
      (format out "stroke~%showpage~%grestore"))))

マジックナンバーがたくさん出てますが、
書き捨てのプログラムなので気にしない方針で。
convert-latlonは緯度経度をPostScriptの座標(ポイント)に変換します。
数111は緯度1度あたりのおおよその距離(km)、
数91は経度1度あたりのおおよその距離(km)、
あとの数はA4用紙全体を使うように調整するためのものです。
かなり大雑把ですが、それなりに見栄えがいいのでいいことにしてください。

おまけ:

WiLiKiにアクセス制御機能を付けた

土曜日, 4月 16th, 2011

説明するより実物を見たほうが早いでしょう。

KyotoWiLiKi

以下はdiffです。
(物好きな人は)ご自由にお使いください
(続きを読む…)