Top > ScriptFuClass > Step2

ニ限目 スクリプトを書く前に Edit

では実際にスクリプトを書いてみましょう、とする時に、最低でも知っておかなければいけない Scheme の文法というものがあります。これを知らなければ Script-Fu は書けないというものです。沢山知っていれば知っているほど Script-Fu を書く時に有利ですが、はじめからそれほど沢山理解しようとしても無理があるので、ここでは優先度の高いものから段階的に説明していきます。

手始めに、Scheme での計算方法と変数の代入とリストは Script-Fu を書く上で必ずといっていいほど使われるので、これを説明してから簡単なスクリプトを書いてみます。さらいうと、Gimp で関数を呼び出した時にはほとんどの場合でリストで返ってくるので、リストの考え方を理解せずに Script-Fu を書くのはとても困難です。

2.1 Script-Fu コンソール Edit

このページでは Script-Fu コンソールを使います。メニューから フィルタ/Script-Fu/Script-Fu コンソール... を選ぶと下のようなダイアログが開きます。ここのコマンド欄に色々とコマンドを入力することで対話的に実行結果を得ることができます。

Script-Fu コンソール

ここでは Gimp の関数を直接実行して TinyScheme 出力を得ることができます。そのためスクリプトを作成する時や、ちょっと関数の反応を見てみたい時にとても便利です。

2.2 計算方法 Edit

2.2.1 前置記法と四則演算 Edit

まず最初に、数の計算方法からはじめます。加算、減算、乗算、除算の演算子はそれぞれ +、-、*、/ に割り当てられています。例えば 2 と 3 を足し算してみましょう。コンソール欄に 2+3 と入力すると、エラーが出てしまいます。
注意: ">" の右側に続いて書いてあるものが Script-Fu コンソールに入力したもので、次の行がその出力です

> 2+3

Error: eval: unbound variable: 2+3

2+3 と入力した時のエラー

エラーが出る理由は三つあります。計算をさせるには、Scheme のルールを守らなくてはいけないのです。上の計算で間違っているところはここです。

1. 計算式全体を括弧で囲む
この計算式は計算が始まる前から終るところまで、全体を括弧で囲まなくてはいけません。例: (2+3)
2. 空白スペースが無い
この計算式には、演算子 (+) と被演算項 (2 と 3) との間に空白スペースがありません。スペースで間を区切ってやらなければ、"2+3" という一つの変数であるとみなされてしまいます。例: (2 + 3)
3. 前置記法になっていない
前置記法 (Wikipedia) では全ての被演算項の前に演算子があります。普通は計算する数の間に、足し算するとかかけ算するなどの計算方法を書くのですが、ここでは計算方法を全ての数字の前に書きます。例: (+ 2 3)

よって、2 と 3 を足し算したい場合は (+ 2 3) となります。コンソールのコマンド入力欄に入れてみて下さい。

> (+ 2 3)

5

このルールにより、コンソールに (+ 2 3) と入れると、5 という答えが返ってくると思います。前置記法の書き方はちょっと慣れが必要かもしれません。簡単な練習をしてみましょう。

2.2.2 前置記法と計算の練習 Edit

問題です。正解を見る前に実際にどうなるのか自分で考え、コンソールに入れて試して下さい。

問題

  1. 8 を 2 で割算する
  2. -4 を得る
  3. 2 と 5 と 7 をかけ算する
  4. 4 と 5 を足し算したものから 2 を引く
  5. 2 と 4 を足し算したものと、3 と 5 を足し算したものとをかけ算する

正解

1. (/ 8 2)
四則演算の演算子には、加算には + を、減算には - を、乗算には * を、除算には / を使います。
2. (- 4)
(- 0 4) でも同じ結果が得られますが、符号を反転させたいだけの時はこの様に書くことができます。-4 だけを書くこともできます。
3. (+ 2 5 7)
このように足し算したい数字を並べて書くことができます。並べて書くと、引き算の時は最初の数字から残りの数字を足したもので引き算し、かけ算の時は全ての数を掛けることになり、割り算の時は最初の数字から残りの数字を掛けたもので割ります。
4. (- (+ 4 5) 2)
普通の計算式と同様に括弧には優先順位があり、より内側にある括弧で閉じられたものから計算されていきます。
5. (* (+ 2 4) (+ 3 5))
前置記法で計算式を括弧で囲みながら書いていくと、不慣れなために間違った書き方をしてしまう場合があります。長く複雑な計算式であっても、一つ一つ確認しながら括弧を閉じるようにしましょう。

2.3 変数を使う Edit

変数を宣言することで、計算式などに変数を使用することができます。変数はさまざまなものが入る入れ物で、数値や文字列やリストなどを入れることができます。変数の有効範囲に分けて考えると、大域変数 (グローバル変数) と局所変数 (ローカル変数) とに分けることができます。

GIMP-2.4 以降に SIOD から交換して実装された TinyScheme では大域変数を使えなくなり、使用できるのは局所変数だけになりました。

2.3.1 局所変数 Edit

変数を使用する前には、まず変数の使用を宣言しなければいけません。これは let* 構文を使用して行います。これは let* に続けて変数名と、その変数に代入したいもの (式の結果であってもよい) を書きます。このように書きます。

(let* ((変数名 代入するもの) ...) ...)

この変数は局所変数なので、let* 構文の外で変数を使うことはできません。

変数 a に 6 を入れるには次のようにします。

> (let* ((a 6)))

()

括弧 () が表示されました。まだ何も評価していないためです。評価の対象として、変数 a には何が入っているのかを評価させてみます。

> (let* ((a 6)) a)

6

最後に a を追加することで、そこで a を評価した結果が返ってきて 6 を出力しました。初めてやる人には意味がわからないと思いますが、そういうものだと思ってください。ここではまだ重要なことではないので気にしなくていいです。大事なことは、構文の書き方です。

次に、変数 a に 6 を入れ、変数 b に 8 を入れ、その二つを足し算してみます。

>(let* ((a 6) (b 8)) (+ a b))

a には 6 が、b には 8 が入っているので、最後の (+ a b) では (+ 6 8) という計算が行われて 14 が返ってきます。

Script-Fu コンソールでは文章を 1 行で書き切らないといけないために構造が分かりにくいです。理解しやすいように、上の計算を段落をつけて整形して現すと次のようになります。

(let*                 ← let* 構文の始まり
      (               ← 変数宣言の始まり
       (a 6)          ← 変数 a の初期値は 6
       (b 8)          ← 変数 b の初期値は 8
      )               ← 変数宣言の終わり
  (+ a b)             ← 変数の有効範囲内で変数を使っている
)                     ← let* 構文の終わり=変数の有効範囲終わり

ここでプログラミングの経験がある人は、変数の宣言の時に変数の型を指定していないことに気付くかもしれません。Scheme の変数は型の無いものなので、整数型であるとか文字列型であるとかを指定する必要はありません。例えば変数 moji に文字列 "The Gimp" を入れる場合はこのようになります。

> (let* ((moji "The Gimp")) moji)

"The Gimp"

文字列では文字を二重引用符 (") で囲まなくてはいけないことに注意して下さい。もし囲まなかった場合、変数 moji に変数 The を入れようとしているのだと解釈され、エラーになってしまいます。

> (let* ((moji The Gimp)) moji)

Error: eval: unbound variable: The

2.3.2 変数の代入 Edit

let* 構文は、let* 構文の範囲内でのみ有効な変数の宣言と初期値の代入を行うことができることは上に説明したとおりです。すでに宣言した変数に別の値を代入するには set! 構文を使います。

(set! 変数 代入するもの)

例えば、このように使います。

(let*
      (
       (a 6)          ← 変数 a の初期値は 6
       (b 8)          ← 変数 b の初期値は 8
      )
  (+ a b)             ← 6+8 の計算を行う
  (set! a 4)          ← 変数 a の値が 4 に変更
  (+ a b)             ← 4+8 の計算を行う
)

まだ宣言されていない変数を set! で使おうとするとエラーになるので注意してください。

2.4 リスト Edit

Script-Fu を扱う上で、リスト (list) という考えは非常に重要です。なぜなら Gimp で関数を呼び出した時、返ってくるものは大抵リストでカプセル化されているからです。リストで返された場合、リストの中から必要な値を取り出してやらなければなりません。

2.4.1 リストの作り方 Edit

リストは要素の並びで構成されています。要素には数値や文字列や真偽やリストなどが入ります。リストは括弧の前に引用符 (') を使ってこのようにして作られます。

'(要素1 要素2 要素3 ... )

例えば 2 と 4 と 6 の数値が並んでいるリストを作るにはこうします。

> '(2 4 6)

(2 4 6)

コンソールからはこのように数値を括弧で閉じて並べたものが返ってきますね。リストの先頭から 2 4 6 と順番に入っていることが分かります。リストの中にさらにリストを作るには、引用符無しの括弧で並べます。色々なタイプを並べてみます。

> '(5 "The Gimp" #t (7 8))

(5 "The Gimp" #t (7 8))

5 が数値、"The Gimp" が文字列、#t が真偽値 (#t が真を現し、#f の偽を現す) で、(7 8) が 7 と 8 で構成されるリストです。

list でリストを作ることもできます。これは list に続けて要素を書くだけでリストができます。

(list (要素1) (要素2) (要素3) ... )

もし要素を一つも書かなかった場合、空っぽのリストが作られます。

=> (list 1 3 5 7)

(1 3 5 7)

=> (list)

()

2.4.2 car と cdr Edit

それではリストの中に入っているものを取り出してみましょう。リストの先頭の要素を取り出すには car を、リストの残りを取り出すには cdr を使います。

> '(1 2 3)

(1 2 3)

> (car '(1 2 3))

1

> (cdr '(1 2 3))

(2 3)

まずはリスト '(1 2 3) を確認しています。ここから car を使ってリストの先頭の要素を取り出してやると 1 が返ってきます。cdr を使えば (2 3) と、リストの残りがリストで返ってきます。リストの残りの要素はリストになることに注意してください。

次にリスト (2 3) から 2 と 3 を取り出してみましょう。2 を取り出すには先ほどの結果からさらに car を使えばいいですね。3 を取り出すには追加して cdr の次に car を使えばできますね。

> (car (cdr '(1 2 3)))

2

> (cdr (cdr '(1 2 3)))

(3)

> (car (cdr (cdr '(1 2 3))))

3

2 を取り出すには括弧で閉じて cdr を先に使い、その後で car を使うことに注意して下さい。3 を取り出すために cdr を二回使うと、要素が一つのリストになります。このリストから car を使って中にある 3 を取り出します。cdr を三回使うと要素が無くなり、空っぽのリストになります。これからはもう何も取り出すことはできません。

> (cdr (cdr (cdr '(1 2 3))))

()

2.4.3 連続する car と cdr の短縮形 Edit

car と cdr を何回か組み合わせて使うことでリストの中の要素を取り出すのですが、何度も (car (cdr ... )) と書くのは面倒です。そこで car と cdr をまとめて書くやり方が用意されています。それは c と r の間に a と d を書くのです。a は先頭を取り出すことを、d は残りを取り出すことを示しています。例えばこのようにします。

> (car (cdr '(1 2)))

2

> (cadr '(1 2))

2

> (car (cdr (cdr '(1 2 3 4))))

3

> (caddr '(1 2 3 4))

3

二つの要素が入っているリストの後ろを取り出すには cadr を、三番目を取り出すには caddr を使います。四番目を取り出そうと cadddr を使うこともできます。しかし五番目の要素を取り出そうと caddddr を使うとエラーとなってしまいます。残念ながらあまり長いものはサポートされていないようです。cdr と car を組み合わせて使うなどするようにしましょう。

> (caddddr '(1 2 3 4 5))

Error: unbound variable: caddddr

> (cadr (cdddr '(1 2 3 4 5)))

5

> (caddr (cddr '(1 2 3 4 5)))

5

リストの中の要素の一つとしてリストを入れることもできます。リストの中に入っているリストから要素を取り出すには、まず外側から中のリストを取り出し、その次に内側のリストから要素を取り出します。例えば、このようになります。

> (caddr '(1 5 (7 6) 4))

(7 6)

> (car (caddr '(1 5 (7 6) 4)))

7

'(1 5 (7 6) 4) がリストです。(7 6) はリストの中には入っているリストです。そしてリスト '(1 5 (7 6) 4) を caddr してやると三番目の要素、つまりリスト (7 6) が取り出せます。このリストの中の 7 を取り出すには、さらにcar してやればいいわけです。

これらを踏まえて、リストから要素を取り出すための練習をちょっとしてみましょう。

練習問題

  1. '(3 5 7) から 5 を取り出す
  2. '(4 5 (6 7) 8) から 7 を取り出す

car と cdr の仲間を使用して、なるべく少ない手順で取り出せるようにしましょう。

正解(例)

  1. (cadr '(3 5 7))
  2. (cadr (caddr '(4 5 (6 7) 8)))

2.5 評価式と返り値 Edit

評価式、というのは何かというのは難しいのですが、前後を括弧で囲んだ式であると簡単に考えてください。最初に出てきた計算の式も評価式の一種です。式を評価すると、通常は何らかの値が返ってきます。(+ 5 3) の結果として 8 が返ってきたことを思い出して下さい。

> (+ 5 3)

8

これは (+ 5 3) の計算式を計算したのですが、ここでは計算したことを評価したのだといいます。そして式を評価した結果、8 が返ってきたのだと考えられます。let* を使った場合は、最後の式を評価したものが返ってきます。

> (let* ((a 6)) a)

6

まず変数 a に 6 を入れて、a だけを書いて評価しています。そのまま a の内容が出力されます。リストを作成したときの結果はリストが返ってきます。つまり、評価したときの返り値はリストなのです。

> '(3 7 2)

(3 7 2)

ほとんどの場合、Gimp の関数を呼び出した時で返り値があるなら、その返り値はリストになっています。リストになっている場合、car などを使って必要な値を取り出してやる必要があります。

駆け足で Scheme の説明をしてきましたが、難しかったでしょうか? 簡単な規則で作られているので、ちょっとコツをつかんでしまえばすぐに理解できると思います。これまでのところをまとめてみます。

  • Scheme の構文は前後を括弧で括る
  • 括弧内の最初の要素が手続き (関数名) で、残りの要素は引数という前置記法
  • 評価式を評価した場合、評価の結果が返される


Diff Freeze Rename Backup   RSS of recent changes
Last-modified: (847d)