11. Lisp(for Common Lisp)とフローチャート

ここでは、具体的にLispとフローチャートの対応関係について書きます。ソースコードとフローチャートを順番に書くことで、ソースコードの処理の流れを明確にします。

Common LispはCLISPと、xyzzyの二つで動作を確認しています。

11.1. goto文

gotoは指定した個所に処理をジャンプさせることができます。

Lispでは gotoはないが tagbodyと goを使うことで類似の処理を記述できます。

gotoの一般的な動作については VBAの goto を参照してください。

(tagbody タグ名または命令文... )

(tagbody
    タグ名
    命令文
    (go タグ名) ;go は tagbodyの中で使う。
)
タグ名 処理のジャンプ先
命令文 処理全般

go のフローチャート

_images/lisp_go.gif

goの例

lisp_go.lisp

(tagbody
  (go B)
  A
  (print "01")
  B
  (print "02")
  (go D)
  C
  (print "03")
  D)
;02を表示

goto文例のフローチャート

_images/bat_goto_ex.gif

11.2. 変数宣言

変数の宣言は letを使います。

(let ((変数1 初期値1)(変数2 初期値2)...) 命令文...)
変数 ローカル変数を宣言
初期値 変数の初期値
命令文 変数のスコープ内の処理

letのフローチャート

_images/variable_declaration.gif

letの例

(let ((x 1) (y 2))
  (print (+ x y))) ;3を表示

例のフローチャート

_images/variable_declaration_exa.gif

11.2.1. 変数の値の変更

変数の値を変更する。

(setq 変数 値)
変数 変数を指定
変数に代入する値

setqのフローチャート

_images/g_variable_declaration.gif

setqの例

(let ((i 0))
  (setq i 2)
  (print i)) ;2を表示

setqのフローチャート

_images/value_chg.gif

11.3. 三項の条件演算子

Lispではifを利用します。

三項の条件演算子の構文

(if 条件 Then部 Else部)

条件 真偽を返す式
Then部 条件が真のとき実行される
Else部 条件が偽のとき実行される

※Else部を省略することができ、このときはnilが返します。


三項の条件演算子の例

lisp_conditional_op.lisp

(let ((sum 10)(str ""))
  (setq str
        (if (>= sum 60) "合格" "不合格" ))
  (print str))

三項の条件演算子フローチャート

_images/sanko.gif

11.4. if...else文

ifについては 3項演算子の章を参照 してください。

ここでは、類似の機能の condについて説明します。

condは elseifが使える ifと考えればよいです。

(cond (条件式1 命令文1 ... )
      (条件式2 命令文2 ... )
      ...
      (t       命令文3 ... ))
条件式 以下の式を実行する条件
命令文 条件で実行される命令
t 条件に当てはまらないときに実行される(else節)

condのフローチャート

_images/ifelse.gif

condの例

lisp_if_else.lisp

(let ((age 16) (result ""))
  (cond ((>= age 70) (setq result "心の欲する所に従って矩を踰えず"))
        ((>= age 60) (setq result "耳順う"))
        ((>= age 50) (setq result "天命を知る"))
        ((>= age 40) (setq result "惑わず"))
        ((>= age 30) (setq result "立つ"))
        ((>= age 15) (setq result "学に志す"))
        (t           (setq result "無し")))
  (print result)) ;"学に志す" を表示する

if..elseの例のフローチャート

_images/rongo.gif

11.5. switch文

Lispではswitch文には caseを使います。

case の書式

(case 式
      ( 値1 命令文1 ... )
      ( 値2 命令文2 ... )
        ・・・・・
      ( t   命令文3 ... ))
値を返す式
式で返された値と同じ場合命令文を実行する
命令文 実行される処理
t 式が値で指定された以外場合ここになる。(else、default部)

case のフローチャート

_images/switch.gif

caseの例

lisp_switch.lisp

(let ((month 2) (result ""))
  (case month
    ( 1 (setq result "January"))
    ( 2 (setq result "February"))
    ( 3 (setq result "March"))
    ( 4 (setq result "April"))
    ( 5 (setq result "May"))
    ( 6 (setq result "June"))
    ( 7 (setq result "July"))
    ( 8 (setq result "August"))
    ( 9 (setq result "September"))
    (10 (setq result "October"))
    (11 (setq result "November"))
    (12 (setq result "December")))
  (print result))   ;Februaryを表示

case例のフローチャート

_images/switch_ex.gif

11.6. for文

Lisp では for はありませんが類似の処理に do があります。

※参考までに通常の for 文については Javaのfor文を参照 してください。

do の書式

(do ((変数宣言 初期値 再初期化)...) (終了判定 終了処理...)
  命令文...)
   
変数宣言 カウンター用変数を宣言している
初期値 宣言した変数に設定する初期値
終了判定 ループ処理を終了する条件を書く
終了処理 ループ終了時に実行される処理。省略可能。
再初期化式 宣言した変数再設定する
命令文 ループで処理する本体

do のフローチャート

_images/lisp_do.gif

11.6.1. do を for として使う

do を forとして使う場合には 終了判定に not をつけて継続判定とし、終了処理を省略した以下のような書式にします。

(do ((変数宣言 初期値 再初期化)...) ((not 継続判定))
  命令文...)

do を使った for のフローチャート

_images/lisp_do_for.gif

1~10の合計を求めるfor文の例

lisp_for.lisp

(let ((sum 0))
  (do ((i 1 (+ i 1))) ((not (<= i 10)))
    (setq sum (+ sum i)))
  (print sum))  ;55を表示

for文例のフローチャート

_images/for_ex.gif

11.7. for-each文

Lispにfor-each文はありませんが、dolist をつかって同様の処理を記載できます。

※for-each文については Javaのfor-each文を参照 してください。

dolistの書式

(dolist (変数 リスト 終了処理) 命令文 ... )

dolistはリストの要素の数だけ、変数に配列の要素を順番に格納しながら処理をを繰り返します。

変数 ループ内で使う変数を宣言する。変数にはリストの要素が順次、格納される。
リスト 変数に順次格納されるリスト
終了処理 ループ終了時に実行される処理。省略可能。
命令文 ループ中の実行される処理

dolist のフローチャート

_images/lisp_dolist.gif

1~10の合計を求める。for-each文の例

lisp_for_each.lisp

(let ((nums '(1 2 3 4 5 6 7 8 9 10)) (sum 0))
  (dolist (num nums)
	(setq sum (+ sum num)))
  (print sum)) ;55を表示

for-each文のフローチャート

_images/for_each.gif

11.8. while文

while文の構文

(while 継続条件式 命令文... )
継続条件式 ループを継続する条件式を書く
命令文 継続条件式が真の間、命令文を繰り返し実行する。省略可能。

while文のフローチャート

_images/while.gif

1~10の合計を求める。while文の例

lisp_while.lisp

(let ((i 1) (sum 0))
  (while (<= i 10)
    (setq sum (+ sum i))
    (setq i (+ i 1)))
  (print sum))

while文例のフローチャート

_images/while_ex.gif

11.9. do..while文

do-while文の構文

Lispに do-whileはありません。whileと prognを組み合わせて類似の処理を書きます。

while

whileについては前節の whileを参照 してください。

progn

prognは一つしか処理を書けない場所でも、複数の命令文を書くことできます。

(progn 命令文1  命令文2...)

progn のフローチャート

_images/lisp_progn.gif

do-while

whileと prognを組み合わせることで do-whileの同等の処理を実現します。

(while (progn 命令文 ... 継続条件))

do-while のフローチャート

_images/do_while.gif

1~10の合計を求める。do-while文の例

lisp_do_while.lisp

(let ((i 1) (sum 0))
  (while (progn
           (setq sum (+ i sum))
           (setq i (+ i 1))
           (<= i 10)))
  (print sum)) ;55を表示

do-while文のフローチャート

_images/do_while_ex.gif

11.10. continue文

Lispには continue文はないため、goと tagbodyを利用して類似処理の記述します。

goと tagbodyは前節を 参照 してください。

continueの構文

Lispに continueはないため、goを使って類似の処理を実装します。

(ループ制御
    (if 条件 (go continue01))
    その後の処理
    continue01
)

goを使った continue動作は while、do、dolist などのループ制御の「その後の処理」をスキップさせ、ループ処理を継続させます。

continue文の一般的な動作については Javaのcontinue文を参考 にしてください。


continueのフローチャート

_images/continue.gif

continue文の例

lisp_for_continue.lisp

(let ((sum 0) (i 1))
  (while (<= i 10)
      (cond ((= i 3)  
             (go continue01)))
      (setq sum (+ i sum))
      continue01
      (setq i (+ i 1)))
  (print sum))  ;52を表示

continue文のフローチャート

_images/bat_for_continue_ex.gif

11.11. break文

break文はLispでは returnを使います。

break → return

returnはループ制御文を抜けだします。

returnの書式

(ループ制御
    (if 条件 (return)) ;break → return
    その後の処理))

returnはdo、dolist、whileのループ制御を抜け出します。


returnのフローチャート

_images/break.gif

returnの例

lisp_for_break.lisp

(let ((sum 0) (i 1))
  (while (<= i 10)
    (setq sum (+ sum i))
    (cond ((>= sum 30)
           (return))) ;break → return
    (setq i (+ i 1)))
  (print sum)) ;36を表示

break文のフローチャート

_images/bat_for_break_ex.gif

11.12. ラベル付きcontinue文

Lispにはラベル付continueの構文はありません、goを利用して対応します。

Javaにはラベル付continueがありますので、どのようなものか 参考にしてください。

(ループ制御
    (ループ制御
        (if 条件 (go continueラベル))
        その後の処理
    )
    その後の処理1
    continueラベル
)

「continue ラベル」はdo、dolist、whileの内側の「ループ制御」の「その後の処理」、外側の「ループ制御文」の「その後の処理1」をスキップします。


continue文のフローチャート

_images/label_continue.gif

ラベル付きのものは、主に多重ループを抜ける処理をスマートの書くために使います。

C言語同様に、Lispもラベル付き continueの構文がないため、go(goto文)を使って記述する。


ラベル付きcontinueの例

lisp_for_double_continue.lisp

(let ((sum 0)(i 1)(j 0))
  (while (<= i 10)
    (setq j 1)
    (while (<= j 10)
      (if (= i 3)
          (go continue01))
      (setq sum (+ sum (* i  j)))
      (setq j (+ j 1)))
    continue01
    (setq i (+ i 1)))
  (print sum)) ;2860を表示

ラベル付きcontinue文のフローチャート

_images/bat_for_double_continue_ex.gif

11.13. ラベル付きbreak

Lispではラベル付break構文はありません、goと tagbodyを使って同じ処理を書きます。

ラベル付breakの構文

(tagbody
    (ループ制御
        (ループ制御
            (if 条件 (go breakラベル))
            その後の処理
        )
        その後の処理
    breakラベル
)

「breakラベル」はdo、dolist、whileなどのループ制御をすべて抜けだして終了します。


ラベル付きbreak文のフローチャート

_images/label_break.gif

ラベル付きのものは、主に多重ループを抜ける処理をスマートの書くために使います。

C言語同様に、Lispもラベル付き breakの構文がないため、go(goto文)を使って記述する。

ラベル付きbreak文の例

lisp_for_double_break.lisp

(let ((sum 0) (i 1) (j 0))
  (tagbody
    (while (<= i 10)
      (setq i 1)
      (while (<= j 10)
        (setq sum (+ sum (* i j)))
        (if (>= sum 15)
            (go break01))
        (setq j (+ j 1)))
      (setq i (+ i 1)))
    break01)
  (print sum)) ;15を表示

ラベル付きbreak文のフローチャート

_images/bat_for_double_break_ex.gif