Tag: SHIORI’

YAYA Tc555-1で追加されたyaya_emerg.txtとは何なのか

 - by Don

まずは最新のYAYAを入手しよう

2021-06-26現在、最新のYAYAはTc555-3です。releasesの一番上から取得してください。

yaya_emerg.txtって何ですか?

YAYA Tc555-1のリリースノートに書いてある通りです。

辞書読み込みエラーが起きた時の緊急フォールバック機能追加
エラーが起きた際に、yaya_emerg.txtを設定ファイルとしてリトライし、最低限の動作を保つのを目的とします。

YAYAは辞書を正確に書かないとゴーストがまったく無反応になってしまうという、里々とは正反対な厳格なSHIORIです。ゴーストマスタはエラーログを確認しながらエラーが起こった箇所を修正してSHIORIをリロードし、無事にまた起動する、といった具合に開発していくことになっています。

あまり無いとは思いますが、うっかりエラーが起こった状態(ゴーストが無反応の状態)でネットワーク更新用のファイルをアップロードしてしまい、それがユーザさんの環境に届いてしまったら大変です。あるいは特定のフォルダの辞書を更新用ファイルに含めるのを忘れた、という場合も考えられます。無反応ですから再度ネットワーク更新で修正する道も断たれてしまいます(SSPならdescript.txtにhomeurlが書けるので、書いてあればなんとかなりますが)。

で、まったく無反応になってしまうのも困るので、最低限の動作(ネットワーク更新ができる、とか)を保ちましょう、という目的で作られたものなわけです。

対応する方法は?

GCさくらでYAYA製各種テンプレートゴーストやSSPBT所属のYAYAゴースト達が怒涛の更新を捕捉されていますが、まさにこの対応のためのようです。今からYAYAでゴーストを作ろうという人は、最新のテンプレートゴーストを使うことで対応可能です。

では既に各種テンプレートゴーストを利用してゴーストを作っていた人たちはどうするか。自分が利用していたテンプレートゴーストに新たに追加された以下のファイルをコピーして使いましょう。

yaya_emerg.txt
緊急時設定ファイル。これが無いと始まらない。テキストエディタで開いてみるとyaya.txtと同じノリで記述されていることがわかります。システム辞書の他に緊急時用の辞書ファイルを読み込む設定になっていると思います。
yaya_emerg_dic.txt等(ファイル名は任意)の緊急時用の辞書ファイル
緊急時用なので凝ったことをしても仕方ありません。とにかくミスの無い、シンプルで余計な記述のない最低限の辞書を用意しましょう。homeurl等は取得できることが望ましいので、別ファイルで用意して通常時と共有するのも良いですね。
yaya_shiori3.dic
もっとも基本となるシステム辞書が更新されています。主にエラーメッセージをSSPのエラーログに送信する機能が追加されています。現在のものに上書きして保存しましょう。

yaya_shiori3.dicにどういった変更がされているのか興味がある人はyaya-dicあたりを覗いてみると楽しいと思います。

よくわからないけど、対応しないとダメ?

んなこたーないっす。ちゃんとした人ならロクに動作確認もしないままネットワーク更新かけたりしません。ちゃんとした人なら。

私はちゃんとした人ですが対応しました。私は元々里々使いだったので、YAYAでもSHIORIリロードをショートカットキー(rキー)に割り当てているのですが、エラーが起きた状態でもrキーでSHIORIリロードが効くように設定しました。エラーを修正する際に便利なのではないかと思います。

ゴーストと天気予報と灯(akari.dll)のお話

 - by Don

気象庁の中の人より、非公式ながらAPIっぽいものが利用できるとの発信がありました。

天気予報といえば実に伺かっぽいです。天気予報の機能があるゴーストさんもそれなりに見受けられた気がします。しかしおそらく大体のゴーストはどこかのWebページをスクレイピングしていたのではないかと想像します。しかしこの非公式APIを利用できればもう少しまともな実装ができそうです。

JSONパーサSAORIを求めて

2021-03-22追記。黄桃の誄歌-Etu-様にて「YAYA as SAORI jsonパーサ」が公開されております。追記ここまで。

この非公式APIはJSON形式で出力しているようです。ゴーストから利用するにはJSONを解析したいところです。JSONが扱えるSAORIを探してみましたがなかなか見つかりません。しかしJSONを扱えるSHIORIを見つけました。

夜天燈火

灯(akari.dll)という名のSHIORIで、ドキュメントを見るに、辞書型の変数をサポートしているようです。そして_json2azvという内部関数を使えば辞書型や配列型の変数として読み込んでくれると。また_customrequest関数を独自に定義することでSAORIとして使用することも可能なようです。

ワクワクが止まりません。もはや天気予報などそっちのけでこのSHIORIへの興味が湧いてきました。

サンプルゴーストの639C 灯花さんの中身を見て構造を理解しようと思っていたら、サンプルとしてこんな機能が用意されていました。

akari-weather-forecast

気象庁のWebページをスクレイピングして天気予報してる…。運命に導かれたかのような錯覚に陥ります。

do-you-wanna-do-like-this

「こういうことがやりたいんでしょ?」と言われているような気分です。いや、当初はそうだったけど。今は興味の対象は貴女なのです。

しかも現在の気象庁のWebページは昔と構造が変わっており天気予報機能は正常に動いておりません。スクレイピングの宿命ですね。冒頭の非公式APIを読み取るように変更すれば動きそうです。

fix-bug-if-you-want-akari

ああ、そうか。何と素晴らしいサンプルゴーストなのでしょう。灯の使い方を指南してくれるばかりでなく、練習問題まで用意してくれていたのですね。やってやろうじゃないの。

ghost\master\res\util.azr を以下のように書き換えます。

// 地域を選択させるサクラスクリプトを返します。
string weather_news_arealist()
{
	string	strAreas;
	array	arKeys	=	_dickeyget( dictWeatherAreas );

	strAreas	+= "\_q";
	for( int i=0; arKeys[i]!=nil; i++ )
	{
		if( i%2 == 0 )	strAreas += "\n";
		strAreas	+= "\![*]\q["+arKeys[i]+",OnWeatherNewsAreaSelected,"+arKeys[i]+"]  ";
	}

	return strAreas;
}

// weather_news_arealist()によるサクラスクリプトから、地域を選択した時に呼ばれます。
OnWeatherNewsAreaSelected( dict ref )
{
	_create_thread( "TH_OnWeatherNewsAreaSelected", ref["Reference0"] );
}

// OnWeatherNewsAreaSelectedから別スレッドで起動される関数です。
// 天気予報を取得してキャラクターに喋らせます。
TH_OnWeatherNewsAreaSelected( string strArea )
{
	dict	dictWN	=	weather_news( strArea );
	
	string	strTalk	=	"\0\s[0]\b[2]"+strArea+"の天気は…\n";
	strTalk	+=	dictWN["text"];
	strTalk	+=	"\n\n(※出典:気象庁ホームページ)\e";

	_speak( strTalk );
}

/*
weather_news
・この関数の結果を使用するときは、「出典:気象庁ホームページ」を記載してください。
・引数であるstrAreaNameには転記を知りたい地域の名前を指定して下さい。
 指定できる地域の名前は、dictWeatherAreasという辞書型変数のキー値です。
・dictWeatherAreasはこの関数の後ろにグローバル変数として定義されています。
*/ 
dict weather_news( string strAreaName )
{
	string strAreaFileName = dictWeatherAreas[strAreaName];
	if( strAreaFileName == "" ) strAreaFileName = dictWeatherAreas["東京都"];
	
	array	arHtml = _httpget( "https://www.jma.go.jp/bosai/forecast/data/overview_forecast/"+strAreaFileName+".json", "utf8" );
	dict	dictWeather;
	dictWeather = _json2azv(arHtml);
	
	return dictWeather;
}
	dict dictWeatherAreas = ${
		$("宗谷地方", "011000" ), $("上川・留萌地方", "012000" ),
		$("網走・北見・紋別地方", "013000" ), $("釧路・根室地方", "014100" ),
		$("胆振・日高地方", "015000" ), $("石狩・空知・後志地方", "016000" ),
		$("渡島・檜山地方", "017000" ), $("青森県", "020000" ),
		$("秋田県", "050000" ), $("岩手県", "030000" ),
		$("山形県", "060000" ), $("宮城県", "040000" ),
		$("福島県", "070000" ), $("茨城県", "080000" ),
		$("群馬県", "100000" ), $("栃木県", "090000" ),
		$("埼玉県", "110000" ), $("千葉県", "120000" ),
		$("東京都", "130000" ), $("神奈川県", "140000" ),
		$("山梨県", "190000" ), $("長野県", "200000" ),
		$("新潟県", "150000" ), $("富山県", "160000" ),
		$("石川県", "170000" ), $("福井県", "180000" ),
		$("静岡県", "220000" ), $("岐阜県", "210000" ),
		$("愛知県", "230000" ), $("三重県", "240000" ),
		$("大阪府", "270000" ), $("兵庫県", "280000" ),
		$("京都府", "260000" ), $("滋賀県", "250000" ),
		$("奈良県", "290000" ), $("和歌山県", "300000" ),
		$("島根県", "320000" ), $("広島県", "340000" ),
		$("鳥取県", "310000" ), $("岡山県", "330000" ),
		$("香川県", "370000" ), $("愛媛県", "380000" ),
		$("徳島県", "360000" ), $("高知県", "390000" ),
		$("山口県", "350000" ), $("福岡県", "400000" ),
		$("佐賀県", "410000" ), $("長崎県", "420000" ),
		$("熊本県", "430000" ), $("大分県", "440000" ),
		$("宮崎県", "450000" ), $("鹿児島県", "460100" ),
		$("沖縄本島地方", "471000" ), $("大東島地方", "472000" ),
		$("宮古島地方", "473000" ), $("八重山地方", "474000" )
	};

これで無事に天気予報ができるようになりました。

weather-forecast-fixed

改善点があるとすればdictWeatherAreasの内容もベタ書きするのでなく非公式APIからリストとして取得してくるのが良さそうですが、今回はここまで。習作としてsupplementのNARにしておきます。自分が書いたコード部分のライセンスはパブリックドメインです。

639C_tomoka_supplement.nar

参考

新しい気象庁サイトからJSONデータが取得できる件 | MindTech

\![embed,...]の使い方がわかった

 - by Don

今更ながら\![embed,…]の使い方を把握したので覚書です。

基本

単純な例

里々でやろうとするときは\![embed,…]の呼び出し先イベントは\eで閉じないよう留意しないといけない。

*
:あいうえお\![embed,OnHoge]さしすせそ

*OnHoge
:かきくけこ

#これを実行すると以下のようになって「あいうえおかきくけこ」と表示された時点で再生が終わる
#\0\s[0]\1\s[10]\0あいうえお\0\s[0]\1\s[10]\0かきくけこ\eさしすせそ\e

単語群なら前後に余計なスクリプトは付かない。

*
:あいうえお\![embed,OnHoge]さしすせそ

@OnHoge
かきくけこ

#これを実行すると以下のようになる
#\0\s[0]\1\s[10]\0あいうえおかきくけこさしすせそ\e

里々であれば以下のように書いても同じ結果が得られる。

*
:あいうえお(OnHoge)さしすせそ

@OnHoge
かきくけこ

#これを実行すると以下のようになる
#\0\s[0]\1\s[10]\0あいうえおかきくけこさしすせそ\e

これでは面白くないので、同じ結果が得られない例を挙げる。

SakuraScriptの再生と同期して呼び出しが実行される例

*
:時計の秒針を一秒おきに数えてみよう!
(現在秒)、\_w[1000](現在秒)、\_w[1000](現在秒)…。

これは期待した動作にならない。「13、14、15…。」みたいになってほしいが、「13、13、13…。」となってしまう。里々が全ての括弧を展開した結果のSakuraScriptをSSPに渡しているだけなので、SSPに届く頃にはすべて13になっている。

しかし、\![embed,…]を使えば期待した動作が可能となる。

*
:時計の秒針を一秒おきに数えてみよう!
\![embed,OnGetSecond]、\_w[1000]\![embed,OnGetSecond]、\_w[1000]\![embed,OnGetSecond]…。

@OnGetSecond
(現在秒)

この例だと「13、14、15…。」みたいに再生される。SakuraScriptの再生と同期してイベント呼び出しが実行され、返されたScriptをその場で埋め込みながら再生を続ける。すごい。

応用

複雑な例

\![embed,…]とはどんなものか、ということについては上述の例さえわかれば十分である。それ以上でもそれ以下でもない。\![embed,…]はそんなに難しいものではない。

\![embed,…]ワカラナイ、というのは往々にして\![embed,…]以外のところに原因がある。例えば、UKADOCの説明には以下のように書いてある。

プロパティシステムの内容によって分岐など、イベントを実行して情報を取得する仕様を、1回のスクリプト実行で有効に使用するための仕様。

何言ってるかワカラナイ。

プロパティシステムというのは、SSPが知っている情報を教えてもらうための仕様だ。例えば現在時を教えてもらうには以下のように書く。

*
\![get,property,OnGetHour,system.hour]

*OnGetHour
:現在(R0)時です。

里々なら里々自身の力で(現在時)と書けば取得できるけど、簡単な例として。

ここで重要なのが、情報を取得するためには別イベントをコールする必要があるということだ。(R0)に入っている情報を(またはそれを使用/加工した文字列を)\![get,property]を実行した直後に埋め込んで再生したい場合もあるのかもしれない。そういうことができますよ、と\![embed,…]の説明には書いてある。

例えば以下のように書いてもkero側のScriptは再生されない。

*
:\![get,property,OnGetHour,system.hour]
:もうそんな時間か。#←これは再生されない

*OnGetHour
:現在(R0)時です。

イベントを呼び出す系のSakuraScriptは、それが実行された時点でそのイベントにおける再生は強制終了されるのが特徴である。

…と思っていた。昨日までは。例外があったのだ。知らなかった。

\![get,property]した後もScriptの再生が続く例

答えを言ってしまうと、以下のように書けばUKADOCの説明にあるようなことが可能となる。

*
:現在\![get,property,OnGetHour,system.hour]\![embed,OnShowHour]時です。
:もうそんな時間か。#←これが再生される!

*OnGetHour
$現在の時間【タブ】(R0)

@OnShowHour
(現在の時間)

\![get,property]した後もScriptの再生が続く条件は、呼び出したイベントで「何も返さない」ことである。

上記の例では(R0)の情報を変数に保存してはいるものの、再生するScriptは何もない。この場合、例外的に呼び出し元のScriptは再生を続けるのだ。知らなかった。

変数に保存したは良いが、再びSSPに処理が戻ってScriptの再生中である。保存した情報を再びSHIORI側で処理し、Scriptを生成し、呼び出し元に埋め込みたい。そこで\![embed,…]の登場だ。

\![embed,…]は極めて単純な仕様であり難しいことはない。\![get,property]の仕様が謎すぎるんだ。「何も返さない」場合のみScriptの再生を続けるとか誰が知ってるんだ。

そもそも\![embed,…]以外のことがあんまりわかってなかった

補足

イベントを呼び出す系のSakuraScriptの代表と言えば\![raise,...]だ。きっと\![get,property]と同様な仕様となっているに違いない。

*
:艦隊のアイドル、\![raise,OnNakachan1]那珂ちゃんだよー!\![raise,OnNakachan2]よっろしくー!

*OnNakachan1
#何も返さない

*OnNakachan2
カーン...カーン...カーン...

予想通りOnNakachan2でScript再生が中断され、カーンカーンとなる。

ちなみに\![raise,...]はMateriaでもサポートされており、Materiaで実行しても同じ結果となった。なので、SSPは本家の仕様を忠実に再現しているものといえる。

複数のゴーストとコミュニケートする

 - by Don

うかべん 横浜#8の発表資料とヒノハルさんのテキスト実況を拝読しているところです。今回もためになる発表が多数行われたようで、こうした資料が公開されることはとてもありがたいことであると感じます。関係者の皆様、大変お疲れ様でした。

コミュニケートとraiseother

もっしょくしさんの発表資料「ゴースト間の交流について ~暦にしき宣伝添え~」は、ゴースト間での情報送受信を含む「絡み」について、利用できそうな現状の仕様をまとめた素敵な資料になっています。複数ゴーストの連携に関して興味のあるゴーストマスタさんは是非ご一読を。

その中で触れられていたコミュニケートとraiseotherの相違点の項目で、以下のように触れられていました。

コミュニケート
送信対象は一度に一人まで
raiseother
その場にいる全員に同時に発信できる(__SYSTEM_ALL_GHOST__)

実は現在(2013-11-03)、最新のSSP(2.3.12)では送信対象に関してコミュニケートもraiseotherと同等の仕様となっています。

via Satori/里々の小技 – PukiWiki

複数のゴーストに同時に話しかける

*AとBとCに話しかける
$Value0【タブ】A(sprintf,%c,1)B(sprintf,%c,1)C
:4人でマージャンしよー!

同時起動中のゴースト全員に話しかける

*全員に話しかける
$Value0【タブ】__SYSTEM_ALL_GHOST__
:みんなでじゃんけんしよー!

「複数の送信相手」を指定する場合はバイト値1区切り、「その場にいる全員」に送信する場合は「__SYSTEM_ALL_GHOST__」を使います。これはコミュニケートの場合はValue0ヘッダに、raiseotherの場合は第一引数に指定することができ、両者の送信対象に関する仕様の差はなくなっています(私がぽなさんの背中を優しくツンツンしてそうして頂きました)。

もしかしたら発表会場でぽなさんが指摘されたかもしれないですが念のため、現状の仕様はこのようになっていますので複数相手のコミュニケートも利用可能です。

里々は何故遅いのか

 - by Don

「loop関数は毎回呼び出し先の名前探してるから遅いのでは」的な見解を目にしたので、それなら名前参照せず処理直打ちのtimesなどの関数は早かろう、と思って計測してみました。

実験

*loopテスト
:[開始](現在分):(現在秒)(loop,my_loop,1000000)
[終了](現在分):(現在秒)
@my_loop
【処理】

*timesテスト
:[開始](現在分):(現在秒)(times,1000000,【処理】)
[終了](現在分):(現在秒)

*whileテスト
:[開始](現在分):(現在秒)(while,(C0)< 1000000,【処理】)
[終了](現在分):(現在秒)

*forテスト
:[開始](現在分):(現在秒)(for,1,1000000,【処理】)
[終了](現在分):(現在秒)

【処理】のところは以下の3パターンを試しました。

  • 空白(空回し)
  • (nop,)
  • (set,hoge,)

結果がこちら。1000000ループ処理にかかった秒数。

ループ関数 空回し nop set
loop 0 86 140
times 0 35 87
while 104 138 191
for 0 36 88

考察

whileは空回し中もカウンタのインクリメントや条件式判定を行なっているので時間がかかっています。nopやsetは隣同士の差を見ると純粋な関数自身の処理コストが読み取れるっぽいですね。平均してnopは処理コスト35、setは処理コスト88くらい?

loopは空回しでは名前参照しないようですが処理が存在すれば名前参照のコストも含んでいるようです。大体50程度。

timesやforを使えばループ自体のコストはほぼ0と言えそうです。ただ内部関数の呼び出しにかかるコストそのものがloopで浪費していた名前参照のコストに比べて大きなウェイトを占めている印象を受けます。括弧の展開と内部関数のコールは同じくらいのコストと考えても良いのかもしれません。

それから通常ループでカウンタを使わないということはほぼあり得ないので結局はwhileと同じ程度の処理を自前で行うことになるでしょうし、1回のsetで済む単純なループ処理なども実用上は少ないでしょうから結局は内部関数の呼び出しコストが積もり積もって遅くなる、という結論です。塵も積もれば山となる。

里々は重い処理に向かないという今更なお話でした。

ファイル読み書きはどれくらい遅いのか(追記あり)

 - by Don

YAYAで測定

ファイルの読み書きは遅いというのは体感的に何となく知っているのですが、どのくらい遅いのか調べてみようと思いました。

実験で使用するSHIORIはYAYA。関数のFREADFWRITEをそれぞれ1,000,000回実行して掛かった時間を測定します。ループ自体に掛かる時間や代入処理に掛かる時間なども比較のために測定します。

ループだけの処理//2秒
{
  _n = 1000000

  _start = GETTIME[6]
  for _i = 0; _i < _n; _i++ {
    //何もしない
  }
  _end = GETTIME[6]

  '\0\s[0]\_qstart: ' + _start + '\nend: ' + _end
}

代入処理//4秒
{
  _n = 1000000

  _start = GETTIME[6]
  for _i = 0; _i < _n; _i++ {
    _hoge = _i //代入
  }
  _end = GETTIME[6]

  '\0\s[0]\_qstart: ' + _start + '\nend: ' + _end
}

読み//10秒
{
  _filename = 'xxx'
  _n = 1000000

  _start = GETTIME[6]
  if !FOPEN(_filename, 'r')
    return
  for _i = 0; _i < _n; _i++ {
    _line = FREAD(_filename) //読む
  }
  FCLOSE(_filename)
  _end = GETTIME[6]

  '\0\s[0]\_qstart: ' + _start + '\nend: ' + _end
}

書き//10秒
{
  _filename = 'xxx'
  _n = 1000000

  _start = GETTIME[6]
  if !FOPEN(_filename, 'w')
    return
  for _i = 0; _i < _n; _i++ {
    FWRITE(_filename, 'hoge') //書く
  }
  FCLOSE(_filename)
  _end = GETTIME[6]

  '\0\s[0]\_qstart: ' + _start + '\nend: ' + _end
}

ループだけでもカウンタ_iがインクリメントされるのでその処理時間が掛かっているように見受けられます(代入処理を加えるとちょうど2倍の時間になっています)。ファイルのオープン/クローズのオーバーヘッドの影響がこの例ではほぼ無視されてしまいますがそれでもそれなりに時間が掛かっているようです(ファイルの開け閉めを同じ回数だけ回すとさらに2倍くらいかかります)(←勘違い)

追記

やっぱりFOPEN/FCLOSEまで含めての実験が必要ですね。ということで

読み//202秒
{
  _filename = 'xxx'
  _n = 1000000

  _start = GETTIME[6]
  for _i = 0; _i < _n; _i++ {
    if !FOPEN(_filename, 'r')
      return
    _line = FREAD(_filename) //読む
    FCLOSE(_filename)
  }
  _end = GETTIME[6]

  '\0\s[0]\_qstart: ' + _start + '\nend: ' + _end
}
書き//1510秒?(100,000回ループの値を10倍しました)
{
  _filename = 'xxx'
  _n = 1000000

  _start = GETTIME[6]
  for _i = 0; _i < _n; _i++ {
    if !FOPEN(_filename, 'w')
      return
    FWRITE(_filename, 'hoge') //書く
    FCLOSE(_filename)
  }
  _end = GETTIME[6]

  '\0\s[0]\_qstart: ' + _start + '\nend: ' + _end
}

他のSHIORIとの比較

前回の里々での実験においてループ回数を揃えて並べれば、SHIORIの処理能力の比較ができそうな気がします。

*nop関数
:[開始](現在秒)(loop,loop_nop,1000000)
[終了](現在秒)#82秒

*set関数
:[開始](現在秒)(loop,loop_set,1000000)
[終了](現在秒)#138秒

@loop_nop
(nop,)

@loop_set
(set,hoge,)

遅いですね。びっくりです。

ついでに華和梨でも調べてみます。

# ループのみ(1秒)
sample1: \0\s[0]\_qstart: $(date %S)$(loop 1000000 "")\nend: $(date %S)\e

# エコー関数(6秒)
sample2: \0\s[0]\_qstart: $(date %S)$(loop 1000000 $(echo ""))\nend: $(date %S)\e

# 代入関数(8秒)
sample3: \0\s[0]\_qstart: $(date %S)$(loop 1000000 $(setstr foo bar))\nend: $(date %S)\e

ループ自体では関数呼び出し1回のみで他の関数呼び出しが無い分高速のようです。代入などの関数をコールすればそれなりの時間を要するみたいです。

まとめ

表にすると見やすいかもしれない。

処理内容(1,000,000回) 時間(秒)
YAYA ループのみ(for) 2
YAYA 代入 4
YAYA ファイル読み(FREAD) 10
YAYA ファイル書き(FWRITE) 10
YAYA ファイル読み(FOPEN,FREAD,FCLOSE) 202
YAYA ファイル書き(FOPEN,FWRITE,FCLOSE) 1510(?)
里々 何もしない関数(nop) 82
里々 代入関数(set) 138
華和梨 ループのみ 1
華和梨 エコー関数(echo) 6
華和梨 代入関数(setstr) 8

ファイル読み書きの遅さの検証のはずが里々の遅さにびっくりする結果となりました。ファイル読み書きが逆に高速に見えてしまいます。こんなはずでは。

複数のファイルを開け閉めするのはそれなりに時間がかかるようですが、1つのファイルに何万行読み書きしてもそれ自体はあまり時間はかからないようです。

でも実用上1,000,000回もループする処理なんて普通のゴースト開発ではまずあり得ないですね。

SAORI呼び出しはどれくらい遅いのか

 - by Don

里々で測定

内部関数 V.S. SAORI

SAORI呼び出しは遅いというのは体感的に何となく知っているのですが、どのくらい遅いのか調べてみようと思いました。

実験で使用するSHIORIは里々。内部関数のwhenとSAORIであるssu.dllのifをそれぞれ同じ回数実行して掛かった時間を測定します。里々で時間を取得するために(現在秒)を使います。本当はミリ秒まで見たいけど仕方ない。

*
:[開始](現在秒)(loop,loop_when,100000)
[終了](現在秒)#9秒

*
:[開始](現在秒)(loop,loop_if,100000)
[終了](現在秒)#17秒

@loop_when
(when,1,)

@loop_if
(if,1,)

環境にもよるのでしょうが100000回だとwhenで9秒、ifで17秒ということで大体2倍程度の違いが見られます。

ssu.dll V.S. YAYA as SAORI with ssu.aym

応答速度はSAORIにもよります。ssu.dllのsplitとssu.aymのsplitで比較してみます。

*
:[開始](現在秒)(loop,loop_split,30000)
[終了](現在秒)#6秒

*
:[開始](現在秒)(loop,loop_split_yaya,30000)
[終了](現在秒)#77秒

@loop_split
(nop,(split,))

@loop_split_yaya
(nop,(aya,split,))

nopが含まれているので正確ではないですが30000回だとssu.dllで6秒、ssu.aymで77秒と10倍近く違います。

10回程度のループ処理をYAYAに投げるくらいならssu.dllを10回呼び出してもさほど変わらないようです。

他に時間のかかることとの比較

HTTPリクエストやファイルの読み書きとの比較も面白そうですが、また今度。

れしばを作った

 - by Don

とある事情により、「れしば」の.NET版「れしば.NET」を作りました。Worksからダウンロードできます。

表示制限とかも無いので、若干使いやすいかも使いづらいかもしれません。

YAYAからPLUGINを呼び出す

 - by Don

SAORIみたいに呼ぶ

YAYAからSAORIを呼び出す関数(FUNCTIONEX)は大抵システム辞書(yaya_shiori3.dic)に書いてあります。

そこでPLUGINを呼び出す関数も書いてみました。

https://gist.github.com/1048890

はろーYAYAわーるど」のシステム辞書に追加する形で利用できます。

PLUGINは通常ユーザが実行する、もしくは![raiseplugin]などを使ってSSP経由でゴーストが呼び出すものですが、何らかの事情でゴーストに同梱してSHIORIから直接呼び出したい場合もあるかもしれません。usage.txtの例にある通り、SwissArmyKnifeなど多機能なPLUGINもありますし、ユーザがPLUGINを無効化していた場合などでもゴーストに同梱しておけばいつでも使えて安心です。(メジャーな機能を持つSAORIは各ゴーストに積むよりもPLUGINにしてみんなで利用しよう、という精神に反しますが。)

里々からPLUGINを呼び出したい場合は、satori.dll =(SAORI)=> yaya.dll =(PLUGIN)=> SAKNIFE.DLL といった具合にYAYA as SAORI経由で二段重ねで呼び出せます。

もちろん、SAORIのように単一機能を呼び出すだけの単純な構成のPLUGINに限られます。PLUGINはGHOSTと一緒で、常にSSPからイベントを受信しているので、通常は複雑な機能を実現する場合が多いです。

何故こんなものを

gomi.dllがバグってると聞いたので、SAORIでないとダメなのかなと思って書いてみました。でもやはりPLUGINは![raiseplugin]で利用した方がいいですね。