直接入力によって実行する
式を実行する-
REPLの中で式を直接入力して実行できます.
$ guile ↵ GNU Guile 3.0.5 ...... 起動メッセージ ...... guile> (+ 10 20) ↵ $1 = 30 guile> (* 10 20) ↵ $2 = 200 guile> (+ 10 20) (* 10 20) ↵ $3 = 30 $4 = 200 guile> (display "Guile Scheme!!!") (newline) ↵ Guile Scheme!!! guile>
1番目と2番目の式は10+20と10*20を計算しています.3番目は2つの式を一緒に計算しています.この例が示すように,複数の式を一緒に指定して実行することができます.4番目は"Guile Scheme!!!"という文字列を表示(display)して改行(newline)しています.
-
上の実行例に出てくる $1,$2,$3,... は,計算結果に付けた一時的な変数名です.同じセッションの中でこの変数名を使って計算結果を利用できます.例えば,次の式は$1=30と$2=200の和を求めています.
guile> (+ $1 $2) ↵ $5 = 230 guile>
これらの一時変数をvalue historyと呼びます.
プログラムファイルをロードして実行する
サンプルプログラム-
複数の式からなるプログラムを実行するときには,プログラムファイルを作成して,そのファイルをロードして実行します.以下では,次のようなプログラムを適当なエディタを使って作成して,hello.scm というファイルに保存したとします.
;; hello.scm (display "*** Hello, world! ***") (newline)
このプログラムは "*** Hello, world! ***" という文字列を表示(display)して改行(newline)します. - Guileは .scm をソースファイルの標準の拡張子にしています.さらに,Emacsを使っているのならば,Emacsも拡張子が.scmのファイルをSchemeのソースファイルと判定して,キーワードハイライトや適切なインデントを行ってくれます.
-
プログラムファイルを実行するためには,まず,プログラムファイルを保存したディレクトリ上でGuileのREPLを起動します.
$ guile ↵ GNU Guile 3.0.5 ...... 起動メッセージ ...... guile>
次に,load手続きを使ってプログラムファイルをロードします.load手続きの一般な形式は(load "ファイル名")です.guile> (load "hello.scm") ↵ ;;; ............ ;;; ...... コンパイルに関するメッセージ ...... ;;; ............ *** Hello, world! *** guile>
load手続きを実行すると,プログラムファイルがコンパイルされて,そのメッセージが色々と表示されます.そのあとにプログラムの実行結果が表示されます.2つ目のプロンプトの直前にある「***Hello, world! ***」 という表示が実行結果です.コンパイルメッセージが混みいっていて実行結果が分かりづらいのですが,慣れるしかありません. -
load手続きに指定するファイル名は,ターミナル上で指定するのと同様に,相対パスや絶対パスで指定します.相対パスの起点はカレントディレクトリ(REPLを実行したディレクトリ)です.例えば,カレントディレクトリが /home/user/tmp だった場合,サンプルプログラムのファイル(hello.scm)を次のように指定できます.
guile> (load "./hello.scm") ↵ *** Hello, world! *** guile> (load "../tmp/hello.scm") ↵ *** Hello, world! *** guile> (load "/home/user/tmp/hello.scm") ↵ *** Hello, world! *** guile>
-
(注意)ホームディレクトリを表すティルダ ~ は使えません(ティルダの機能がPOSIXでは定義されていないため).ティルダを使ってファイル名を指定するとエラーが発生します.
guile> (load "~/tmp/hello.scm") ↵ ;;; Stat of /home/user/tmp/~/tmp/hello.scm failed: ;;; In procedure stat: そのようなファイルやディレクトリはありません: "/home/user/tmp/~/tmp/hello.scm" ............
-
プログラムを変更したり修正したときにはload手続きを使って再びロードします.例えばhello.scmを次のように変更してみます.
;; hello.scm (display "*** Hello, world! ***") (newline) (display "*** Hello, Guile! ***") (newline)
これを再びロードすると,改めてコンパイルされて実行されます.guile> (load "hello.scm") ↵ ...... コンパイルに関するメッセージ ...... *** Hello, world! *** *** Hello, Guile! *** guile>
REPLを使ってデバッグしながらプログラムを作成するときには,修正と再ロードを繰り返します.
- Guileは,ホームディレクトリ上にキャッシュディレクトリ(~/.cache/guile)を作っていて,そこにコンパイル後のバイトコードを保存しています.ちなみに,コンパイル時のメッセージの中にバイトコードのファイル名が絶対パスで表示されます.
- バイトコードが古くならない限り(つまり,ソースコードを変更しない限り),再コンパイルは行わずにキャッシュしたバイトコードが実行されます.そのため,コンパイル済みのプログラムを再ロードしてみるとコンパイルメッセージは表示されません.例えば,相対パスや絶対パスを指定した上の実行例では,初めてロードしたときにコンパイル済みなので,再コンパイルは行われずコンパイルメッセージは表示されません.
自前の手続きを実行する
サンプルプログラム-
自前の手続きの定義を含む次のようなプログラムを作成して,message.scmというファイルに保存したとします.
;; message.scm (define (hello-message mes) (display (string-append "*** Hello, " mes "!!! ***")) (newline))
これはhello-messageという名前の手続きを定義しています.この手続きは,1つの文字列(mes)を引数として受け取って,その文字列の前後に"*** Hello, "と"!!! ***"という文字列を連結(string-append)して,連結後の文字列を表示(display)して改行(newline)します.
-
message.scmをロードしてみます.
guile> (load "message.scm") ↵ ...... コンパイルに関するメッセージ ...... guile>
今度はコンパイルメッセージだけが表示されて,実行結果らしきものは何も表示されません.define形式をロードすると,現在のセッションにその定義が取り込まれるだけで計算は行われません.自前の手続きを実行するためには,適当な実引数を指定して手続きを呼び出す必要があります.guile> (hello-message "Scheme") ↵ *** Hello, Scheme!!! *** guile> (hello-message "Guile") ↵ *** Hello, Guile!!! *** guile>
エラーが発生したとき
エラーとデバッグモード-
エラーが発生したとき,REPLはそれまでのセッションをいったん凍結して,エラーの状態を保持した新たなセッションを開始します.REPLを再帰的に実行してデバッグモードを開始するとも言えます.試しに,hello.scmを次のように変更します.2番目のdisplayをわざとdipslayにしています.
;; hello.scm (display "*** Hello, world! ***") (newline) (dipslay "*** Hello, Guile! ***") (newline)
このhello.scmをロードしてみます.guile> (load "hello.scm") ↵ ;;; ............ ;;; ...... コンパイルに関するメッセージ ...... ;;; ............ *** Hello, world! *** ice-9/boot-9.scm:1669:16: In procedure raise-exception: Unbound variable: dipslay Entering a new prompt. Type `,bt' for a backtrace or `,q' to continue. guile [1]>
1行目と2行目には誤りはないので"*** Hello, world! ***"は表示されます.そのあとのところでエラーが発生しています.赤字がエラーメッセージです.「dipslayは未束縛(変数の値が未定義)だ」と怒っています.
- エラーが発生したあとのプロンプトに'[1]'といった数字が付いています.これはREPLがそれまでのセッションをいったん凍結して再帰的に新たなセッション(デバッグモード)を開始していることを示しています.数字は再帰の深さ(デバッグモードの深さ)を示しています.例えば,上の状態でさらにエラーが発生すると,この数字が‘[2]’になります.試しに,誤りを含んだhello.scmをもう一度ロードしてみて下さい.数字が増えるはずです.
- デバッグモードのセッションはREPLを再帰的に実行しているだけなので,(quit)やCtrl-Dによって元々の(あるいは一つ前の)セッションに戻ることができます.デバッグモードだからと言って何も恐れることはありません.それに,デバッグモードを放置していると,どんどん再帰していきます.エラーの原因が判明したり,プログラムを再実行するときには,デバッグモードを解消したほうがよいでしょう.
セッションが終わるまで,その束縛,生きてます
- 一度ロードして確立した束縛は,セッションが終わるまで有効です.
-
REPLのload手続きを使ってプログラムの動作を確認するとき,上の点に注意が必要です.注意すべき点を説明するために,次の2つのプログラム(sample-1.scmとsample-2.scm)を考えましょう.
;; sample-1.scm (define x 10) (define y 20) (display "x+y=") (display (+ x y)) (newline)
;; sample-2.scm (define y 99) (display "x+y=") (display (+ x y)) (newline)
sample-2.scmは,xの定義をわざと外しています.xの値は未定義ですから,当然,(+ x y)を計算するところでエラーが発生するはずです. -
これら2つのプログラムを同じセッションの中で実行してみます.まずsample-1.scmをロードし,続けてsample-2.scmをロードします.
$ guile ↵ GNU Guile 3.0.5 ...... 起動メッセージ ...... guile> (load "sample-1.scm") ↵ ...... コンパイルに関するメッセージ ...... x+y=30 guile> (load "sample-2.scm") ↵ ...... コンパイルに関するメッセージ ...... x+y=109 guile>
両方とも問題なく実行できています.sample-2.scmが実行できたのは,sample-1.scmをロードしたときにxの値が定義され,それを利用したからです.ちなみに,,bindingというコマンドを使うと,その時点の変数束縛の一覧が表示されます.guile> ,binding ↵ y #<variable 7f0f706b3cb0 value: 99> x #<variable 7f0f706b3ce0 value: 10> guile>
これを見ると,xが10に束縛され,yが99に束縛されていることが分かります. -
REPLの1つのセッションの中で色々なプログラムをロードしたとき,あるプログラムがうまく動いたからといって,そのプログラムが単独で正しく動作するとは限りません.確実に確認したいときにはREPLを起動し直してプログラムを実行すべきでしょう.例えば,REPLを再起動してsample-2.scmをロードしてみるとエラーがきちんと発生します.
$ guile ↵ GNU Guile 3.0.5 ...... 起動メッセージ ...... guile> (load "sample-2.scm") ↵ x+y=ice-9/boot-9.scm:1669:16: In procedure raise-exception: Unbound variable: x ............... guile [1]>
上の赤字がエラーメッセージです.このエラーメッセージは,xが未束縛(値が未定義)であることを示しています.エラーメッセージが少しヘンですが,(display "x+y=") までは問題なく実行できて,そのあとの加算が実行できずにエラーメッセージを表示しているためです. - (参考)プログラムを再ロードしたり,複数のプログラムをロードしたときに,ある変数の定義(束縛)が重複した場合,最後に確立された束縛が有効です.上の例で言えば,sample-1.scmをロードしたときに変数yはいったん20に束縛されますが,sample-2.scmをロードすると変数yは99に束縛され直されます.,bindingコマンドは各時点での最新の束縛を表示します.
コマンドラインオプション
- REPLを使ってプログラムを実行する際に知っておくと便利なコマンドラインオプションを説明します.
-
-l は,REPLを起動する時点でプログラムファイルをロードするコマンドラインオプションで,一般的な形式は次の通りです.
guile -l file -l file ...ここで,fileはロードしたいプログラムファイルの名前です. -lオプション1つにつきファイルは1つしか指定できませんが, 上に示したように -l オプションは幾つでも指定できます.
-
具体例:以下の2つのファイルを -l オプションでロードして,
各ファイルで定義された手続きを実行してみます.
;; hello.scm (define (hello-everybody args) (for-each hello-somebody args)) (define (hello-somebody name) (display (string-append "*** Hello," name "!!! ***")) (newline))
;; fib.scm (define (fib n) (if (= n 0) 0 (let loop ((k 1) (curr 1) (prev 0)) (if (= k n) curr (loop (1+ k) (+ curr prev) curr)))))
$ guile -l hello.scm -l fib.scm ↵ GNU Guile 3.0.5 ...... コンパイルメッセージや起動メッセージ ...... guile> (hello-everybody '("Alice" "Bob" "Carol" "David")) ↵ *** Hello,Alice!!! *** *** Hello,Bob!!! *** *** Hello,Carol!!! *** *** Hello,David!!! *** guile> (fib 10) ↵ $1 = 55 guile>
REPLを起動するときに hello.scm と fib.scm がまだコンパイルされていなければ,それらをコンパイルしてからロードします.そのときには,コンパイルメッセージが表示されたあとに起動メッセージが表示されます. - -l オプションでファイルをロードするときにはシェルの補完機能(注:ファイル名の補完機能)が使えるので,load 手続きによってロードするよりも楽かも知れません.
-
-- (注;ハイフン2つ)は,REPLに対してコマンドライン引数を渡すためのコマンドラインオプションです.一般的な形式は次の通りです.
guile -- arg arg ...ここで,arg はコマンドライン引数です.
-
コマンドライン引数は,以下の command-line 手続きによって取得します.
(command-line)command-line 手続きは無引数で,以下のようなリストを返します.("/usr/bin/guile" "arg" "arg" ...)ここで,"/usr/bin/guile" はguileコマンドのファイル名です(環境によって異なるかも知れません). また,"arg" "arg" ... はコマンドライン引数の値です. この形式が示すように,コマンドライン引数の値は文字列として与えられます.
-
具体例:まず,command-line 手続きによってコマンドライン引数が取得できることを確認します.
$ guile -- Alice Bob Carol David ↵ GNU Guile 3.0.5 ...... 起動メッセージ ...... guile> (command-line) ↵ $1 = ("/usr/bin/guile" "Alice" "Bob" "Carol" "David") guile>
コマンドライン引数として,例えば,数値を指定したとしても, それらの数値はREPLに対して文字列として与えられます. 以下では,これを確認してみます.$ guile -- 11 22 33 44 ↵ GNU Guile 3.0.5 guile> (command-line) ↵ $1 = ("/usr/bin/guile" "11" "22" "33" "44") guile>
-
具体例:
-- オプションは他のコマンドラインオプションと一緒に指定できます.
ただし,-- からうしろはすべてREPLへのコマンドライン引数になってしまうので,
-- オプションはコマンドの最後に指定します.試しに,-- オプションのあとに -l オプションを指定した実行例を示します.
$ guile -- Alice Bob Carol David -l hello.scm ↵ GNU Guile 3.0.5 ...... 起動メッセージ ...... guile> (command-line) ↵ $1 = ("/usr/bin/guile" "Alice" "Bob" "Carol" "David" "-l" "hello.scm") guile>
この実行例から,-l や hello.scm もコマンドライン引数になってしまっていることが分かります. 改めて,-- と -l を逆にしてREPLを起動して,-- で指定した引数を利用して hello.scm の中の hello-everybody 手続きを実行してみます.$ guile -l hello.scm -- Alice Bob Carol David ↵ GNU Guile 3.0.5 ...... 起動メッセージ ...... guile> (command-line) ↵ $1 = ("/usr/bin/guile" "Alice" "Bob" "Carol" "David") guile> (hello-everybody (cdr (command-line))) ↵ *** Hello,Alice!!! *** *** Hello,Bob!!! *** *** Hello,Carol!!! *** *** Hello,David!!! ***
この実行例では,REPLを起動したあと, まず command-line 手続きを単独で実行して, コマンドライン引数の値を確認しています. 次に command-line 手続きが返す値(リスト)を利用して hello-everybody 手続きを実行しています.なお,command-line 手続きが返すリストの先頭はguileコマンドのファイル名であることに注意して下さい.上では cdr を使ってそのファイル名を除外しています.
- -q オプションは,REPLの起動時に初期設定ファイル(~/.guile)の実行を抑止します.筆者の場合,(ice-9 readline) モジュールの activate-readline 手続きを実行することと,プロンプトを guile> にすることしか設定していないので,このオプションを利用する機会がどのくらいあるのかは不明です.詳細は省略します.
- 独自のモジュールをREPLの中でロードしたいとき, -l オプションでモジュールファイルをロードして use-modules によってモジュール内の束縛名を取得するという方法と, ロードパスを指定して use-modules によってロードする方法があります. ロードパスを指定するときには,-L オプションを使って指定できます. ただし,add-to-load-path を利用する方法や環境変数を利用する方法もあります. カレントディレクトリ以外のディレクトリからプログラムファイルをロードしたいときにも,-L オプション(や add-to-load-path)が利用できます. ただし,REPLの中でファイルをロードするには,load ではなく load-from-path 手続きを使用します.詳細は省略します.
(おしまい)