トップ > Script-Fuスクリプトの制作 >
プログラミング言語Schemeの基礎を学ぶ(その5)

  

プログラミング言語Schemeの基礎(続き)

前の記事の『プログラミング言語Schemeの基礎を学ぶ(その4)』からの続きです。 引き続き、Script-Fuスクリプトを制作する上で知っておくべきSchemeの基礎知識について解説します。

この記事では、スクリプトの流れを制御するための "制御文" について解説します。

  
プログラミング言語での "制御文" の仕組みは "分岐" とも呼ばれます。

制御文とは

制御文は、スクリプトの流れを制御するためのものです。 例えば、

  1. 入力された数値が偶数なら○○をしなさい
  2. そうでなければ△△をしなさい

のように、条件に応じて実行させたい内容が変わることもあるでしょう。 そのような場合に利用するのが制御文です。

プログラミング言語Schemeにはいくつもの制御文が用意されていますが、本ウェブサイトでは、

  1. 手続き if
  2. 手続き while

の2つについて紹介します。

手続き ifを使ってみる

手続き ifはとても利用頻度の高い制御文で、

  1. もし○○なら△△をしなさい
  2. そうでなければ□□をしなさい

という制御を行うためのものです。

では、手続き ifを実際に利用してみましょう。 スクリプトファイル myscript.scm を以下のように書き換えてください。

  1. (define (myscript)
  2. (let
  3. (
  4. (innumber 3)
  5. )

  6. (if (even? innumber)
  7. (begin
  8. (gimp-message (string-append (number->string innumber) " is Even."))
  9. )
  10. (begin
  11. (gimp-message (string-append (number->string innumber) " is Odd."))
  12. )
  13. )
  14. )
  15. )

  16. (script-fu-register
  17. "myscript" ; 登録する関数の名前
  18. "My Script" ; メニュー項目のラベル
  19. "自作の練習用スクリプトです" ; メニュー項目の説明
  20. "My Name" ; 作成者の名前
  21. "My Name" ; 著作権者の名前
  22. "January 1, 2023" ; 作成日(改訂日)
  23. "" ; メニュー項目を有効にするための条件
  24. )

  25. (script-fu-menu-register
  26. "myscript" ; 対象の関数の名前
  27. "<Image>/Filters" ; メニュー項目の位置
  28. )
 

7行目から14行目の、

(if (even? innumber)
    (begin
        (gimp-message (string-append (number->string innumber) " is Even."))
    )
    (begin
        (gimp-message (string-append (number->string innumber) " is Odd."))
    )
)

という部分が、新たに登場した手続き ifの部分です。 7行目の手続き ifに続く、

(even? innumber)

という部分が条件になります。 カッコで囲まれていることからわかるように、この条件の部分も手続き even?から始まる命令です。

手続き even?は、引数で与えられたデータが偶数かを判定するためのものです。 今回の例では、変数 innumberの値が偶数かを判定します

条件を満たしている場合、つまり、変数 innumberの中身が偶数であれば8行目から10行目が実行されます。 それ以外なら11行目から13行目が実行されます。 つまり、今回のスクリプトは変数 innumberが偶数か奇数かを表示するだけのものです。

  
条件を満たす場合も満たさない場合も、それぞれ手続き beginの内側が実行されます。

では、実行しましょう。

1. ウィンドウ下部に "3 is Odd." と表示される
1. ウィンドウ下部に "3 is Odd." と表示される

上図のようにウィンドウ下部に "3 is Odd." と表示されます。 ちゃんと偶数・奇数が判定されました。

では、偶数の場合も試してみましょう。 スクリプトファイル myscript.scm を以下のように修正します。

  1. (define (myscript)
  2. (let
  3. (
  4. (innumber 10)
  5. )

  6. (if (even? innumber)
  7. (begin
  8. (gimp-message (string-append (number->string innumber) " is Even."))
  9. )
  10. (begin
  11. (gimp-message (string-append (number->string innumber) " is Odd."))
  12. )
  13. )
  14. )
  15. )

  16. (script-fu-register
  17. "myscript" ; 登録する関数の名前
  18. "My Script" ; メニュー項目のラベル
  19. "自作の練習用スクリプトです" ; メニュー項目の説明
  20. "My Name" ; 作成者の名前
  21. "My Name" ; 著作権者の名前
  22. "January 1, 2023" ; 作成日(改訂日)
  23. "" ; メニュー項目を有効にするための条件
  24. )

  25. (script-fu-menu-register
  26. "myscript" ; 対象の関数の名前
  27. "<Image>/Filters" ; メニュー項目の位置
  28. )
 

4行目を、

(innumber 3)

から、

(innumber 10)

に修正しただけです。 では、実行しましょう。

2. ウィンドウ下部に "10 is Even." と表示される
2. ウィンドウ下部に "10 is Even." と表示される

上図のようにウィンドウ下部に "10 is Even." と表示されます。 偶数の場合もきちんと判定されました。

手続き beginは省略できることもある

手続き ifの中には手続き beginがありますが、手続き beginは省略できることもあります。 手続き beginを省略できるのは、命令を1つしか含まない場合です

作成中のスクリプトは、

(begin
    (gimp-message (string-append (number->string innumber) " is Even."))
)
(begin
    (gimp-message (string-append (number->string innumber) " is Odd."))
)

のように、条件を満たす場合も満たさない場合も、それぞれ1つの命令しか実行していません。 手続き gimp-messageによるメッセージの表示しか行っていません

このような場合には手続き beginを省略することができます。 では、スクリプトファイル myscript.scm を以下のように修正してください。

  1. (define (myscript)
  2. (let
  3. (
  4. (innumber 10)
  5. )

  6. (if (even? innumber)
  7. (gimp-message (string-append (number->string innumber) " is Even."))
  8. (gimp-message (string-append (number->string innumber) " is Odd."))
  9. )
  10. )
  11. )

  12. (script-fu-register
  13. "myscript" ; 登録する関数の名前
  14. "My Script" ; メニュー項目のラベル
  15. "自作の練習用スクリプトです" ; メニュー項目の説明
  16. "My Name" ; 作成者の名前
  17. "My Name" ; 著作権者の名前
  18. "January 1, 2023" ; 作成日(改訂日)
  19. "" ; メニュー項目を有効にするための条件
  20. )

  21. (script-fu-menu-register
  22. "myscript" ; 対象の関数の名前
  23. "<Image>/Filters" ; メニュー項目の位置
  24. )
 

修正したのは8・9行目で、手続き beginによる囲みを無くしました。


それでは、実行してみましょう。

1. ウィンドウ下部に "10 is Even." と表示される
1. ウィンドウ下部に "10 is Even." と表示される

上図のようにウィンドウ下部に "10 is Even." と表示されます。 8行目だけが実行され、9行目は実行されていないことがわかります。

では、奇数の場合も試してみましょう。 スクリプトファイル myscript.scm を以下のように修正します。

  1. (define (myscript)
  2. (let
  3. (
  4. (innumber 3)
  5. )

  6. (if (even? innumber)
  7. (gimp-message (string-append (number->string innumber) " is Even."))
  8. (gimp-message (string-append (number->string innumber) " is Odd."))
  9. )
  10. )
  11. )

  12. (script-fu-register
  13. "myscript" ; 登録する関数の名前
  14. "My Script" ; メニュー項目のラベル
  15. "自作の練習用スクリプトです" ; メニュー項目の説明
  16. "My Name" ; 作成者の名前
  17. "My Name" ; 著作権者の名前
  18. "January 1, 2023" ; 作成日(改訂日)
  19. "" ; メニュー項目を有効にするための条件
  20. )

  21. (script-fu-menu-register
  22. "myscript" ; 対象の関数の名前
  23. "<Image>/Filters" ; メニュー項目の位置
  24. )
 

4行目を、

(innumber 10)

から、

(innumber 3)

に修正したのみです。 では、実行しましょう。

2. ウィンドウ下部に "3 is Odd." と表示される
2. ウィンドウ下部に "3 is Odd." と表示される

上図のようにウィンドウ下部に "3 is Odd." と表示されます。 偶数の場合もきちんと判定されました。

条件を満たさない場合の命令は書かなくてもいい

条件を満たさない場合の命令が不要な場合には、

(define (myscript)
    (let
        (
            (innumber 3)
        )

        (if (even? innumber)
            (begin
                (gimp-message (string-append (number->string innumber) " is Even."))
            )
        )
    )
)

のように、条件を満たす場合の命令のみ記述すればよく、

(define (myscript)
    (let
        (
            (innumber 3)
        )

        (if (even? innumber)
            (gimp-message (string-append (number->string innumber) " is Even."))
        )
    )
)

のように、手続き beginを省略することももちろんできます。

手続き whileを使ってみる

続いて紹介するのは手続き whileです。 手続き whileは、

  1. もし○○なら△△を繰り返しなさい

という制御を行うためのものです。 条件を満たしている間は処理を繰り返します

では、手続き whileを実際に利用してみましょう。 スクリプトファイル myscript.scm を以下のように書き換えてください。

  1. (define (myscript)
  2. (let
  3. (
  4. (innumber 5)
  5. (outnumber 9)
  6. )

  7. (set! outnumber 1)

  8. (while (> innumber 0)
  9. (set! outnumber (* outnumber innumber))

  10. (set! innumber (- innumber 1))
  11. )

  12. (gimp-message (string-append "Number is " (number->string outnumber)))
  13. )
  14. )

  15. (script-fu-register
  16. "myscript" ; 登録する関数の名前
  17. "My Script" ; メニュー項目のラベル
  18. "自作の練習用スクリプトです" ; メニュー項目の説明
  19. "My Name" ; 作成者の名前
  20. "My Name" ; 著作権者の名前
  21. "January 1, 2023" ; 作成日(改訂日)
  22. "" ; メニュー項目を有効にするための条件
  23. )

  24. (script-fu-menu-register
  25. "myscript" ; 対象の関数の名前
  26. "<Image>/Filters" ; メニュー項目の位置
  27. )
 

10行目から14行目の、

(while (> innumber 0)
    (set! outnumber (* outnumber innumber))

    (set! innumber (- innumber 1))
)

の部分が、新たに登場した手続き whileの部分です。 10行目の手続き whileに続く、

(> innumber 0)

という部分が条件になります。 カッコで囲まれていることからわかるように、この条件の部分も手続き >から始まる命令です。

手続き >は、引数で与えられた2つの数値の大小を判定するためのものです。 今回の例では、変数 innumberの値が 0(ゼロ) より大きいかを判定します

条件を満たしている間、つまり、変数 innumberの中身が1以上である間は11行目から13行目が繰り返し実行されます。

繰り返しの内部では、

  1. 変数 outnumberに変数 innumberの値を乗算する
  2. 変数 innumberから 1 を引く

という命令が実行されています。 つまり、変数 innumberの初期値である 5 の階乗(5 x 4 x 3 x 2 x 1)を求めています。


では、実行しましょう。 階乗の結果は 120 になるはずです。

1. ウィンドウ下部に "Number is 120" と表示される
1. ウィンドウ下部に "Number is 120" と表示される

上図のようにウィンドウ下部に "Number is 120" と表示されます。 希望していた通りの結果が得られました。

制御文の条件は複数指定することもできる

制御文には複数の条件を指定することもできます。 例えば、

  1. 変数 innumberが偶数
  2.       かつ
  3. 変数 innumberが 0(ゼロ) より大きい

という具合です。 これを実際のスクリプトで記述するには、

(if (and (even? innumber) (> innumber 0))
    (gimp-message "Yes!!")
)

と記述します。 手続き andで囲むことで、複数の条件を "かつ" でつなげることができます。

同様に、

  1. 変数 innumberが偶数
  2.       または
  3. 変数 innumberが 0(ゼロ) より大きい

としたい場合には、

(if (or (even? innumber) (> innumber 0))
    (gimp-message "Yes!!")
)

と記述します。 手続き orで囲めば "または" でつないだことになります。

条件は否定で記述することもできる

条件は否定で記述することもできます。 例えば、

  1. 変数 innumberが偶数ではない

という具合です。 これを実際のスクリプトで記述するには、

(if (not (even? innumber))
    (gimp-message "Odd!!")
)

と記述します。 手続き notで囲むことで、条件を否定で記述することができます。

  

条件の記述に使えるその他の手続き

この記事では制御文の条件で使える手続きとして、

  1. even?
  2. >

の2つを紹介しました。 もちろんそれ以外にも条件を記述するための手続きが用意されています。 まずは、数値の判定に使える手続きから紹介します。

手続き 説明
even? 偶数かを判定する
odd? 奇数かを判定する
zero? 0(ゼロ)かを判定する
positive? 正の数かを判定する
negative? 負の数かを判定する

続いては大小を比較する手続きです。

手続き 説明
= 等しいかを判定する
< 小さいかを判定する
> 大きいかを判定する
<= 以下かを判定する
>= 以上かを判定する

文字列の比較のための手続きもあります。

手続き 説明
string=? 文字列が等しいかを判定する

リストが空かどうかを判定するための手続きもあります。

手続き 説明
null? リストが空かどうかを判定する
  

まとめ

条件に応じて実行させる内容を変えたい、と考えるのは珍しいことではありません。 例えば、

  1. 範囲選択されているならその部分を塗りつぶしなさい

のようなことです。 また、

  1. レイヤを探し、見つかる間はそのレイヤのサイズをキャンバスに合わせなさい

のようなこともよくあることでしょう。

そのような場合に必要になるのが、"制御文" に分類される手続きです。

条件を満たすかどうかによって流れを分岐するには手続き ifを、条件を満たしている間は処理を繰り返させたいなら手続き whileを使います。

手続き 説明
if 条件を満たすかどうかで流れを分岐する
while 条件を満たしている間は処理を繰り返す

手続き ifも手続き whileも直後のカッコで条件を指定します。 条件の判定に使える手続きには以下があります。

手続き 説明
even? 偶数かを判定する
odd? 奇数かを判定する
zero? 0(ゼロ)を判定する
positive? 正の数かを判定する
negative? 負の数かを判定する
= 等しいかを判定する
< 小さいかを判定する
> 大きいかを判定する
<= 以下かを判定する
>= 以上かを判定する
string=? 文字列が等しいかを判定する
null? リストが空かどうかを判定する

また、複数の条件をつなげたり、否定するための手続きとして以下のものが用意されています。

手続き 説明
and 複数の条件を "かつ" でつなぐ
or 複数の条件を "または" でつなぐ
not 条件を否定で記述する
メニュー