シナリオ記述言語 Q

Qについてチュートリアルダウンロード関連論文連絡先

<English>

言語仕様

1.   変数と値
2.   キューとアクション
3.   制御構造
4.   シナリオと関数
5.   エージェント,アバター
6.   環境

言語仕様書のダウンロードはこちら (PDF)

1.変数と値

変数は予約語以外のアルファベットで始まるシンボルである.変数は宣言をしてから用いる.変数の宣言には次の2通りの方法がある.

変数への値の代入は次の3通りの方法がある.

この中で, 唯一キューやアクションの実行による代入が明示的でない.このようにキューやアクションの内部で代入される変数をパターン変数と呼ぶ.パターン変数の値は常に参照値であり,参照値を扱うために以下の操作が用意されている.

パターン変数の記法として$で始まるシンボルの利用を特に勧めている.パターン変数も変数であるので, 他の変数と同様に宣言が必要である.

2.キューとアクション

インタラクションのきっかけはキューと呼ばれ,エージェントに観測の依頼を行うものである.キューは以下のdefcueで定義される.defcueはキューのインタフェースを定義するものであり,実体はエージェントシステムで定義される.なおcueの記法としてcueの前には?をつける.

Syntax 1  −キューの定義−

(defcue name
         {(:keyword in|out|inout)}*)

例1:キューの定義

(defcue ?position (:name in) (:distance in) (:angle in))
(defcue ?observe (:name in) (:gesture in))
(defcue ?hear (:from out) (:word in))
(defcue ?see (:object in) (:direction in ))

in,out,inoutはキーワード引数の属性を指定する.in,out属性はエージェントから見た表現である.inは,キーワード引数の値がシナリオ側からエージェント側に送られる向き(エージェントへ渡す),outはエージェント側からシナリオ側に送られる向き(エージェントから受け取る)を意味する.inoutはin,out両方向(エージェントに渡した後,受け取る)を意味する.

外界への作用はアクションと呼ばれ,エージェントに実行の依頼を行うものである.アクションには実行の終了を待たない非同期型アクションと,実行の終了を待つ同期型アクションがある.アクションは以下のdefactionで定義される.defactionはアクションのインタフェースを定義するものであり,実体はエージェントシステムで定義される.なお,アクションの記法として,同期型アクションには!をつけ,非同期型アクションには!!をつける.両方の特性を備えたアクションについては,同期型と非同期型の両方の定義が必要である(例:!walkと!!walk).

Syntax 2  −アクションの定義−

(defaction name
             {(:keyword in|out|inout)}*)

in,out,inoutはキーワード引数の属性を指定する.in,out属性はエージェントの立場で考えている.inは,キーワード引数の値がシナリオ側からエージェント側に送られる向き(エージェントに渡す),outはエージェント側からシナリオ側に送られる向き(エージェントから貰う)を意味する.inoutはin,out両方向(エージェントに渡した後,貰う)を意味する.また,引数の属性がoutもしくはinoutの場合は,参照型を指定しなくてはならない.

例2:アクションの定義

(defaction !walk (:from in) (:to in))
(defaction !!walk (:from in) (:to in))
(defaction !speak (:to in) (:sentence in))
(defaction !approach (:to in) (:distance in) (:angle in))
(defaction !!approach (:to in) (:distance in) (:angle in))

{:keyword value}は,エージェントの生成に必要なデータを定義するために,エージェントシステムごとに任意に決定される.なお,Qではこのようなキーワード引数が多用される.キーワード引数は :(コロン)で始まるキーワードシンボルに続いて値が指定される形式で利用される.例えば":to Jerry"という記述があった場合,"to"がキーワードシンボル,"Jerry"が値となる.

下記の例では,エージェントは"Jerry"が"Hello"というのを待ち,バスターミナルから駅まで歩き,Jerryに"Hello"と言う.そして,駅が南の方角に見えるかどうかを観測している.この例で非同期アクション"!!walk"を用いることで,エージェントは歩き出した直後にJerryに"Hello"と言う.非同期アクションはエージェントのシナリオの記述の柔軟性(と同時に複雑さ)を大きく高めている.

例3:キューとアクションの実行

(?hear :from Jerry :word "Hello")
(!walk :from "bus_terminal" :to "railway_station")
(!speak :to Jerry :sentence "Hello.")
(?see :object "railway_station" :direction "south")

プログラミング言語における関数呼び出しと異なり,Qはキューやアクションの意味を定義しない.キューやアクションは,それぞれのエージェントがそれぞれに実行するため,その意味はエージェントの実装に完全に依存している.

3.制御構造

制御構造はSchemeに従う.それに加えて, 以下のguard付きコマンドの制御構造も提供されている.

Syntax 3  −ガード付きコマンド−

(guard {(cue {form}*)}*
        (otherwise {form}*)])

Guard形式は, キューとフォームからなる節の並びである.すべてのキューは一括してエージェントに送られ,エージェントは全てのキューを並行して観測する.観測しているキューのうちいずれかのキューが真になると,そのキューに後続するフォームを順に評価する.キューがひとつでも真になった時点で,キューの並行観測を終了する.いずれのキューも真とならない場合,otherwise節が指定されていれば, 後続するフォームが評価される.ただし,otherwise節の実行のタイミングは,各エージェントシステムに任される.Guard形式の返却値は最後のフォームの評価結果となる.もし複数のキューが成功した場合,どれを選択するかはエージェントシステムに任される.

例4:ガード付きコマンドの記述

(guard
  ((?hear :from $agent :word "Hello")
   (!speak :to (refval $agent) :word "Hello"))
  ((?hear :from $agent :word "Fire")
   (!!approach :to "exit" :distance "near")
   (!speak :to $agent :sentence "Here is an exit!")))

(guard
  ((?hear :name $agent :word "What’s that?")
   (guard
     ((?position :name "Kyoto_station" :distance "near")
      (!speak :to (refval $agent):sentence "That’s Kyoto station."))
     ((?position :name "Kyoto_university" :distance "near")
      (!speak :to (refval $agent) :sentence "That’s KyotoUniversity"))
     (otherwise
      (!speak :to (refval $agent) :sentence "I can't see anything.")))))

ひとつ目の例は,"Hello"という単語を聞くか"Fire"という単語を聞くかという,ふたつの事象を並行観測している例である.ふたつ目の例はガード付きコマンドを入れ子にした例である.エージェントが"Whta's that?"という文を聞くと,京都駅に近いか,京都大学に近いか,あるいはどちらとも近くないかという判断をするための並行観測が実行される.

4.シナリオと関数

インタラクションのシナリオはdefscenarioを用いて定義する.シナリオは他のシナリオあるいは関数から呼び出すことができる.シナリオは関数とは異なり状態遷移を記述するためのものである.

Syntax 4 −シナリオの定義−

(defscenario name
               
({variable}*
                {&key (variable value)}*
                {&pattern (variable value)}*
                {&aux (variable value)}*)
        {(state
           {(testform {form}*)}*
           {(cue {form}*)}*
           [(otherwise {form}*)])}*)

キーワード引数は&keyを用いて定義する.パターン変数を受け取るキーワード引数は&patternを用いて定義する.パターン変数宣言を用いて定義された引数は,キューやアクションのout,inout属性の引数として利用する事が出来る.シナリオローカルな変数は&auxを用いて定義する.

各状態では,testformcueを記述することができる.testformにはSchemeの式が書かれ,cond節(条件分岐)として処理される.一方,cueとotherwiseはguard付きコマンド(3章制御構造を参照)として処理される.したがって,testformが真であれば後続する{form}*を実行し,偽であれば観測されたcueに後続する{form}*を実行する.{form}*の中では状態の遷移を記述するために(go state)を用いることができる.また,シナリオの実行主体のエージェントIDをselfで参照することもできる.なお,state1, state2…は状態を表すシンボルであり,状態を的確に示す名前をつけることを薦める.明確な状態遷移の指定なく{form}*の実行が終われば,シナリオを終了する.シナリオの返却値は最後に実行されたformの返却値となる.

例5:シナリオの定義

(defscenario chat (&key (message "Oh! Jiro, good boy!") &pattern ($agent #f))
  (greeting
    ((?hear :from $agent :word "Hello")
     (go messaging))
    ((?hear :from $agent :word "Bye")
     (go farewell)))
  (messaging
    ((equal? (refval $agent) Jiro)
     (!speak :to Jiro :sentence message))
    (otherwise
     (!speak :to (refval $agent) :sentence "Hello")))
  (farewell
    (#t
     (!speak :to (refval $agent) :sentence "Bye"))))

chatシナリオはgreeting,messaging,farewellの3つの状態から構成され,greeting状態から開始される.greeting状態では誰かからHelloもしくはByeと言われるのを待ち続ける.Helloと言われれば,messaging状態に遷移し,Byeと言われれば,farewell状態に遷移する.messaging状態では,Helloと声を掛けてきたのがJiroであれば,Jiroにキーワード変数messageに束縛されたメッセージを話し,Jiroでなければ,その相手にHelloと返事をする.一方,farewell状態では声を掛けてきた相手にByeと返事をする.

定義したシナリオをあるエージェントに実行させるには,エージェントの定義時に実行させるシナリオを:scenarioキーワード引数を用いて指定するか,次のように実行させるかどちらかである.

Syntax 5 −シナリオの実行−

(scenario_name agentID arguments)

scenario_nameは呼び出すシナリオの名前であり,agentIDはシナリオの動作主体である.また,argumentsはシナリオの実行に必要な引数である.

例6:シナリオの実行

(chat Taro :message "Hi Jiro")

上記の例は,エージェントTaroにchatシナリオを実行させることを意味する.また同様な記法で,シナリオ内もしくは関数内でも他のシナリオを呼び出すことができる.

例7:シナリオ内におけるシナリオ呼び出し

(defscenario chat_1 ()
  (small_talk
    (#t
     (chat self :message "Hi, Jiro."))))

例8:関数内におけるシナリオ呼び出し

(defscenario chat_2 ()
  (small_talk
    (#t
     (fchat self "Hi, Jiro."))))

(define (fchat agent message)
  (chat agent :message message))

ただし,関数内でシナリオを呼び出す場合には注意が必要である.シナリオの呼び出しにはそのシナリオの実行主体を指定しなければならないが,シナリオと異なり関数にはその関数の実行主体を参照する方法が存在しない.そこで,関数の実行時に引数として,関数の実行主体のエージェントIDを渡しておくと便利である.例えばchatシナリオを実行する関数は,上記のように引数として実行主体のエージェントIDが渡されている.なお,上記の両方の例に現れるselfは,各シナリオを実行しているエージェントIDを表す.

一方,関数内にはシナリオだけでなく,キューやアクションを記述することも可能である.ただし,キューやアクションは暗黙のうちにselfを参照するので,関数内でselfにエージェントIDを束縛する必要がある.そのため関数の引数に実行主体となるエージェントのIDを渡さなければならない.

例9:関数内におけるキュー・アクションの利用

(defscenario greeting ()
  (greeting
    (#t
     (fhello self 3)))

(define (fhello agent distance)
  (let ((self agent)
         ($agent (make-ref #f)))
    (?position :name $agent :distance distance)
    (!speak :to (ref-val $agent) :sentence "Hello.")))

上記の例では,アクションやキューが暗黙の内にselfを参照するので,引数agentで渡されたエージェントIDをlet 文によりselfに束縛している.

5.エージェント,アバター

エージェント,アバターは以下の記法に従って定義される.

Syntax 6  −エージェント,アバターの定義−

(defagent name
              [:scenario value];; エージェントに割り当てたシナリオ
              [:population value];; 同じ定義によって生成されるエージェントの個体数
              {:keyword value}*)

(defavatar name         
              [:scenario value];; アバターに割り当てたシナリオ
              [:population value];; 同じ定義によって生成されるアバターの個体数
              {:keyword value}*)

あらかじめQの言語仕様によって用意されているキーワード引数は:scenarioと:populationの二つだけである.前者はエージェント・アバターに割り当てるシナリオを指定するために用いられ,後者は同じ定義によって生成されるエージェント・アバターの個体数を指定するために用いられる.ただし,これらのキーワード引数の利用はオプショナルである.:scenarioが省略された場合は,後で明示的にエージェントにシナリオを実行させる必要がある(4章シナリオと関数を参照).一方,:populationが省略された場合は,個体数1を意味する.

例10:エージェントの定義

(defagent evacuation_leader
  :scenario fire_navigation
  :population 1
  :location ’(100.0 -10.0 5.0 45.0) ;; Agent’s initial coordinate.
  :shape ’("body1.wrl" "body2.wrl")) ;; Agent’s image data.

(defagent evacuees:scenario fire_drill
  :population 10
  :location ’((100.0 -10.0 5.0) (50.0 10.0 1.0)) ;; Area where agents are to show up.
  :shape ’(("body1.wrl" "body2.wrl"))) ;; Agents’ image data.

ひとつ目の例では,シナリオfire_navigationを実行するエージェントevacuation_leaderを定義している.キーワード引数locationとshapeによりエージェント作成のためのより詳しい情報を与えている.loationとshapeの値はエージェントの初期位置と画像データを表している.ふたつ目の例では,10体のエージェントevacueesを定義し,同じシナリオfire_drillを実行させている.

defagent,defavatarで定義されたエージェント/アバターにはIDが振られnameに割り当てられる. populationが省略されるか,またはその値が1である場合は,エージェントIDがアトムで割り当てられ,値が2以上の場合はエージェントIDがリストで割り当てられる.なお,割り当てられたエージェントIDはnameを評価することによって,そのIDを得ることができる.さらに,list-refを使うことによって,populationの値が2以上で定義されたエージェント群の個々のエージェントIDを得ることができる.

例11:エージェントのIDリストの参照方法

(list-ref evacuees 3)

 上記の例は,10体のエージェント群を表すevacueesから4番目のエージェントのIDを参照している.

6.環境

エージェント以外の環境について定義したい場合は,以下のようにdefenvを用いて定義することができる.ただし,defenvの利用はオプショナルである.

Syntax 7 −環境の定義−

(defenv name
              {:keyword value}*)

{:keyword value}は,環境の設定に必要な情報を得るために,エージェントシステムごとに決定できる.

例12:FreeWalkにおける環境の定義

(defenv virtual_space
         :max_entry 100;; Maximum number of agents (integer)
         :ip_address "i.kyoto-u.ac.jp";; IP address to connect (string)
         :port_number 12345;; port number to connect (integer)
         :vrml_url "http://www.ai.i.kyoto-u.ac.jp/shijo_street/";; URL of VRML source codes(string)
         :vrml_path "data\shijo_street\");; Relative path to the resource directory (string)

 上記の例では,virtual_spaceという環境を定義している.virtual_spaceという環境ではエージェントを最大100体まで存在させることができ,この環境へアクセスするために必要なIPアドレス,ポート番号,またこの環境で利用できる描画データのURLやローカルディレクトリへの相対パスが定義されている.

 

Copyright(C) Ishida lab. All rights researved.