○ guile メモ

guileとはGNUによるScheme(Lisp方言)の実装であり、
GNU の標準スクリプト言語となっています。


ソースの先頭にguileのパスを書き込み実行権限を与えるとbashから直接、 プログラムを動作させることができます
whichコマンドなどでguileのパスを調べて書き込んでください

#!/usr/bin/guile -s
!#

(define func (lambda (a)
	(if(< a 10) 
		(begin (display a)(newline)(set! a (+ a 1))(func a))
	)
))

(func 0)

▼上記ソースをtest.txtとして保存して実行

$ ./test.txt
0
1
2
3
4
5
6
7
8
9


■コメント文

コメント文は;文字から改行までがコメント文として扱われます

;コメント


■画面への出力

display文を使うことにより画面に値を出力できます
newline文は改行を行います
’は式を評価せずに返せという意味でquoteの略です
(+ 1 2 3 4) は 1+2+3+4 を表しており
(+ 1/2 2/1) は 1/2+2/1 を表しています

▼プログラム

(display '(1 2 3 4 "test"))
(newline)
(display (+ 1 2 3 4))
(newline)
(display (+ 1/2 2/1))

▼実行結果

$ ./test.txt
(1 2 3 4 test)
10
5/2


■変数

define により変数を宣言し、set! により変数に値を設定します

▼プログラム

(define abc 4)  ;変数 abc を宣言し 4 で初期化
(set! abc 5)    ;変数 abc の値を変更
(display abc)

▼実行結果

$ ./test.txt
5


■関数

Lispでは関数名と変数名に区別が無いため変数宣言と同じdefineを使います

引数としてa b二つの変数を指定しており 戻り値は関数内で処理された結果が戻ります
lambda とは引数を受け取り結果を返す関数という意味です

▼プログラム

(define func (lambda (a b)(+ a b)))
(display (func 19 20))
または
(define (func a b)(+ a b))
(display (func 19 20))

▼実行結果

$ ./test.txt
39


■ローカル変数

(let ((変数名 0))
	変数の有効範囲
)
変数を宣言した括弧の中のみ有効なローカル変数を作成できます

▼プログラム

(let ((a 1)(b 2)) ;ローカル変数 a bを宣言
(begin (display (+ a b))(newline)))

(let ((a 3)(b 4)) ;ローカル変数 a bを宣言
(set! a 5)        ;ローカル変数 a の値を変更
(begin (display (+ a b))(newline)))

▼実行結果

$ ./test.txt
3
9


■IF文

(if 真理値 真だった場合の処理 偽だった場合の処理)

▼プログラム

(display (if(< 1 2) "true" "false"))

▼実行結果

$ ./test.txt
true


■真理値

真理値は、真は#t、偽は#fであり、
if文では#f以外、総て真となります

▼プログラム

(display (if #f "true" "false"))

▼実行結果

$ ./test.txt
false


■比較演算

(= 1 1)
(> 1 2)
(< 1 2)
(>= 1 2)
(<= 1 2)
(not (= 1 1))	;notを行う事により実行結果を反転します

■beginは並んだ順番に処理を行い、最後の結果の値を返します

▼プログラム

(display (begin (+ 1 1)(+ 2 2)(+ 3 3)))

▼実行結果

$ ./test.txt
6


上記処理では(+ 1 1)→(+ 2 2)→(+ 3 3)の順に処理を行い一番最後に行った処理(+ 3 3)の結果を返しています


■ループ

ループ命令がありませんので再帰呼び出しによりループを行います

;0から9までループ

(define loop (lambda (a)
	(if(< a 10) 
		(begin (display a)(newline)(loop (+ a 1)))
	)
))

(loop 0)
または、named let を使うことによりスマートにループを書くことが出来ます

▼プログラム

;0から9までループ

(let loop ((a 0))
	(if (< a 10) (begin (display a)(newline)(loop (+ a 1)))
))

▼実行結果

$ ./test.txt
0
1
2
3
4
5
6
7
8
9


■ベクター

▼ベクターの作成
要素を指定して作成します

▽プログラム

(define ar (vector "a" "b" "c" "d" "e"))
(display ar)
または
(define ar #("a" "b" "c" "d" "e"))
(display ar)
▽実行結果

$ ./test.txt
#(a b c d e)


▼サイズを指定してベクターの作成
作成したベクターを x で初期化します

▽プログラム

(define ar (make-vector 5 "x"))
(display ar)
▽実行結果

$ ./test.txt
#(x x x x x)


▼ベクターのサイズを調べる

▽プログラム

(define ar (vector "a" "b" "c" "d" "e"))
(display (vector-length ar))
▽実行結果

$ ./test.txt
5


▼指定したベクターの要素を参照
ベクターの4番目の要素を表示します

▽プログラム

(define ar (vector "a" "b" "c" "d" "e"))
(display (vector-ref ar 4))
▽実行結果

$ ./test.txt
e


▼指定したベクターの要素を書き換え
ベクターの2番目の要素を書き換えます

▽プログラム

(define ar (vector "a" "b" "c" "d" "e"))
(vector-set! ar 2 "x")
(display ar)
▽実行結果

$ ./test.txt
#(a b x d e)


▼ベクターの内容のコピー
(vector-move-left! コピー元 start end コピー先 start)
コピーしたい要素の範囲を指定し、コピー先の指定箇所にコピーします

▽プログラム

(define ar #("a" "b" "c" "d"))	;コピー元
(define ar2 (make-vector 10 "x"))	;コピー先
(vector-move-left! ar 1 3 ar2 2)
(display ar2)
▽実行結果

$ ./test.txt
#(x x b c x x x x x x)
上記プログラムではコピー元ベクターの1〜3の要素を、コピー先の2個目の要素から順にコピーしています


▼ベクター ⇔ リストの変換
vector->list によりベクターからリストに変換します
list->vector によりリストからベクターに変換します

▽プログラム

;ベクターからリストに変換
(display (vector->list #("a" "b" "c" "d" "e")))
(newline);改行
;リストからベクターに変換
(display (list->vector (list "a" "b" "c" "d" "e")))
▽実行結果

$ ./test.txt
(a b c d e)
#(a b c d e)


■コードとデータ

Lispではプログラムを構成するコードもデータもリストで出来ています
そのためコードをデータのように操作することが出来ます

▼変数と関数を入れ替える
下記プログラムでは変数 i と関数 func をローカル変数を介して入れ替えています

▽プログラム

(define i 100)
(define (func a b)(+ a b))

(let ((j 0)) ;ローカル変数jを作成し、iとfuncを入れ替える
	(set! j func)
	(set! func i)
	(set! i j)
)

(display (i 19 20))
(newline)
(display func)

▽実行結果

$ ./test.txt
39
100


▼ベクターに値と関数を入れる

下記プログラムではベクターの要素として値と関数を使用します

▽プログラム

(define (func a b)(+ a b))
(define (func2 a b)(- a b))
(define ar (vector "a" "b" func2))

(vector-set! ar 1 func)
(display (vector-ref ar 0))
(newline)
(display ((vector-ref ar 1) 19 20))
(newline)
(display ((vector-ref ar 2) 19 20))

▽実行結果

$ ./test.txt
a
39
-1


▼無名関数

名前の無い関数が定義でき、直接引数を与えて実行することができます
((lambda(i)(+ i 1)) 2)
上記無名関数では引数として 2 を直接与え実行します
((lambda(i j)(+ i j)) 2 3)
上記無名関数では二つの引数 2 3 を直接与えて実行します

無名関数は名前が無いためプログラムの他の部分から直接呼び出すことはできませんが
Lispの世界ではコードもデータもリストであり違いは無いため、
ベクターなどの要素として関数を使用する事もできます

▽プログラム

(define ar (vector (lambda(i j)(+ i j)) (lambda(i j)(- i j))))
(display ((vector-ref ar 0) 2 3))
(newline)
(display ((vector-ref ar 1) 2 3))

▽実行結果

$ ./test.txt
5
-1

コードもデータも同じであると考えると、関数の引数に無名関数を渡すということも出来ます
上記プログラムでは関数 func の引数に無名関数を渡して実行します

▽プログラム

(define func (lambda (a)
	(a 2 3)
))

(display (func (lambda(i j)(+ i j))))

▽実行結果

$ ./test.txt
5


■cons pair

cons pairとは二つのポインタにより構成された構造体を指します

(cons 要素A 要素B)
または

'(要素A . 要素B)
により cons pair を作成します
各要素は car cdr と言い
要素A を car 要素B を cdr と言います
各要素を参照するには

(car (cons 要素A 要素B))
により 要素A を参照することができ、

(cdr (cons 要素A 要素B))
により 要素B を参照することができます。

各要素を変更するには

(set-car! x 要素)
(set-cdr! x 要素)
により cons pair の要素を変更することができます

▽プログラム

(display (cons "car" "cdr"))
(newline)
(display (car (cons "car" "cdr")))	;carを参照して表示
(newline)
(display (cdr (cons "car" "cdr")))	;cdrを参照して表示
(newline)

(define x '(0 . 0))
(set-car! x "car")	;carを変更
(set-cdr! x "cdr")	;cdrを変更
(display x)

▽実行結果

$ ./test.txt
(car . cdr)
car
cdr
(car . cdr)


▼ネストされた pair を参照する関数が用意されています
caaaar から cddddr までの4段までの深さまで参照する関数があります
(define x '(((1 . 2) . (3 . 4)) . ((5 . 6) . (7 . 8))) )

(display (car x))		;実行結果 → ((1 . 2) 3 . 4)
(newline)
(display (cdr x))		;実行結果 → ((5 . 6) 7 . 8)
(newline)
(display (cadr x))		;実行結果 → (5 . 6)
(newline)
(display (cdar x))		;実行結果 → (3 . 4)
(newline)
(display (cdaar x))	;実行結果 → 2
(newline)
(display (caddr x))	;実行結果 → 7
(newline)
(set-car! (cadr x) "test")	;参照されているcons pairを修正
(display x)		;実行結果 → (((1 . 2) 3 . 4) (test . 6) 7 . 8)


■リスト

リストとはcons pairが連結されつながったものであり
リストを作成するには、
(list 1 2 3)
または
'(1 2 3)
にてリストが作成できます

しかし、見た目の単純さとは異なりリストの本質はcons pairであり、car cdrにより連結されたものです
上記リストをcons pairで表現すると
'(1 2 3) → '(1 .(2 .(3 . ())))
と表現することができ、二つは同一の物であります

リストの最後のcdrに要素を設定するには
'(1 2 3 . 4)
と表記することにより要素を設定できます
上記リストをcons pairで表現すると
'(1 2 3 . 4) → '(1 .(2 .(3 . 4)))
と表現できます

▼プログラム

(display '(1 2 3))
(newline)
(display '(1 .(2 .(3 . ()))))
(newline)
(display '(1 2 3 . 4))
(newline)
(display '(1 .(2 .(3 . 4))))
(newline)

▼実行結果

$ ./test.txt
(1 2 3)
(1 2 3)
(1 2 3 . 4)
(1 2 3 . 4)


■リスト操作

▼リストを集合として操作する
map function リストの各要素に対して function を行う
(display (map - '(1 2 3)))		;実行結果 → (-1 -2 -3)
(display (map + '(1 2 3) '(1 2 3)))	;実行結果 → (2 4 6)

▼リストを先頭から順番に操作する
for-each function リストの各要素に対して function を行う

(for-each display '("a" "b" "c" "d"))	;実行結果 → abcd

▼リストに含まれる cons pair の数を返す
(display (length '("a" "b" "c" "d")))	;実行結果 → 4

▼指定したリストの car を参照
下記ではリストの2番目の car を表示します
(display (list-ref '("a" "b" "c" "d") 2))	;実行結果 → c

▼指定したリストの car を変更
下記ではリストの2番目の car を変更します
(define x '("a" "b" "c" "d"))
(list-set! x 2 "test")
(display x)	;実行結果 → (a b test d)

▼リストの連結
append は要素を複製してリストを作成します
append! は要素は複製せず、元の要素のポインタを引き継ぐことによりリストを作成します。
(display (append '("a" "b" "c" "d") '(1 2)))	;実行結果 → (a b c d 1 2)

▼リストの反転
reverse は要素を複製してリストを作成します
reverse! は要素は複製せず、元の要素のポインタを引き継ぐことによりリストを作成します。
(display (reverse '("a" "b" "c" "d")))	;実行結果 → (d c b a)

▼リストの要素の削除
マッチする要素をリストから削除します
delete はリストを複製して新たなリストを作成し削除します
delete! はリストを複製せず、引数のリストを変更します。
リストを変数に代入している場合、その変数はリストの先頭要素を参照しています
そのため、delete! で先頭要素が削除された場合には引数の内容が破壊されるため、 関数の戻り値で引数の変数に対して代入する必要があります。
(define x '("a" "b" "c" "d"))
(display (delete "b" x))	;実行結果 → (a c d)
(display x)		;実行結果 → (a b c d)
(display (delete! "b" x))	;実行結果 → (a c d)
(display x)		;実行結果 → (a c d)

▼リストの複製
複製を行わずにコピーしたリストを修正した場合において
元のリストも修正されてしまいます
(define x '("a" "b" "c" "d"))
(define i 0)
(set! i x)
(delete! "b" x)
(display i)	;実行結果 → (a c d)

list-copy でリストの複製を行なった場合においては、元のリストは変更されません
(define x '("a" "b" "c" "d"))
(define i 0)
(set! i (list-copy x))
(delete! "b" x)
(display i)	;実行結果 → (a b c d)


▼リストの終端までループで表示する

▽プログラム

(define x '(1 2 3 4))
(let loop ((i x))
	(if (not (equal? i '())) (begin (display (car i))(newline)(loop (cdr i)))
))
▽実行結果

$ ./test.txt
1
2
3
4



▼リストを実行する

lispではプログラム自体もリストで出来ています
そのため、プログラム中の (+ 10 20 30) という式を (+ . (10 . (20 . (30)))) に書き換えても同一の動作をします
(display (+ 10 20 30))		;実行結果 → 60
(display (+ . (10 . (20 . (30)))))	;実行結果 → 60
コード自体がリストで出来ているということが確認できます

コード自体がリストで出来ているため、コードを変数に代入することが出来、
また、コードはリストであるため、動的にリストを編集して実行することが出来ます

しかし、そのままではリストを実行できませんので、リストを実行する eval 関数が用意されています

▽プログラム

(define x (list + 10 20 30))
(display (eval x (interaction-environment)))
▽実行結果

$ ./test.txt
60

変数に代入したリストがコードとして実行されました。
実行時にリストを作成しながら実行できる、人工知能用言語と言われる由縁です。


■文字列をコードとして実行

eval-string により文字列をコードとして実行することができます

▼プログラム

(display (eval-string "(+ 1 2)"))
▼実行結果

$ ./test.txt
3
文字列がコードとして実行されました


■ジャンプ

lispはスタックで動作するため、行という観念が無いです。
そのためジャンプと言っても他の言語のように行単位でジャンプすることは出来ません
この場合のジャンプは現在の処理を中止して上位の括弧に値を返すという意味になります

▼プログラム

(display "start\n")

(call/cc (lambda (name1)	;※ ジャンプ先

(begin
(display "1\n")
(display "2\n")
(display "3\n")
(display "4\n")
(name1 0)			;name1が呼ばれることにより処理が中断され※にジャンプする
(display "5\n")
(display "6\n")
)

))

(display "end\n")

▼実行結果

$ ./test.txt
start
1
2
3
4
end
1〜4までの数字しか表示されず、処理が中止されることが確認できます


■キーボードから文字入力

readline ライブラリを使うため、ホームディレクトリに .guile というファイル名で次の内容を書き込む。
面倒なら、ソースコードの上の方に書き込んでもライブラリを使用できる。
(use-modules (ice-9 readline))
(activate-readline)
▼プログラム

(display (readline))
キーボードから文字を入力してEnterを押すと、入力した文字が表示されます




▲トップページ > その他