AX

電霊ログ

私のやってるサイト、電子の言霊の活動ログ

カテゴリークラウド

タグリスト

最近のトラックバック

月別アーカイブ(タブ)

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。
  • [HSP3]再帰モジュール(マクロ)

去年の末辺りから作ってて1月の末には出来てたモジュールですが、サンプルもうちょっとあった方が良いかなとか思いつつそのまま放置してました。色々考えすぎたらいつまでも公開できないので、そろそろ公開してみます。
まだ出来そうな感もあるけど色々試してこの形に落ち着きまして取り敢えずこんな感じかなと。次のアイデアも少しだけありもしますが今はまだ様子見中。


2015/04/13 追記:
HSPプログラムコンテスト2014 にて当モジュールの更新版を投稿しています、そちらではローカル変数もどきも使えるようになっていたり、ファイルをアーカイブにまとめてあるため より扱いやすくなっています。興味のある方はこちらからどうぞ→http://dev.onionsoft.net/seed/info.ax?id=896


これは HSP3 で再帰処理構文を提供するモジュールです、repeat & loop と同じ感覚で再帰処理が出来ないかと思い作ってみました。昔よりは緩和されましたが現在のHSPのバージョンでは510回までしかサブルーチンを重ねて呼び出すことが出来ません(それでも十分な処理は多いですが)。このモジュールではHSPのサブレベルの上限に左右されないのでより深いレベルが必要な処理にどこかしら使えるかもしれません。標準命令のみで記述されているので色々と使いやすいとかなんとか思ったり思わなかったり。

モジュールと書いてますが機能のほとんどはマクロとして書かれています。内部で使用している変数名をモジュール空間内に閉じ込めてるのと、取り外し可能な構成単位と言う意味で一応モジュールとしています(他にもっと分かりやすい云い方が思いつかなかっただけってのもありますが)。
処理や名称等々おかしい所がありましたらなんでもご指摘や提案いただけるとありがたいです。

使用だけでなく改変(可読性皆無ですが)や再配布も自由にどうぞ!
その際作者名や入手元などの情報を記載してくれたら嬉しいですが強制では無いのでこれもご自由にどうぞ!


再帰構文を提供するモジュール(マクロ)

通常版

2014/03/31 追記:一部記述ミスでネスト出来なかったのを修正しました。

#ifndef m_rec
#module m_rec

// 初期化命令

#define global rec_init(%1=1,%2=64) _rec_init %1, %2
#deffunc _rec_init int _len, int _siz
len = _len
siz = _siz
/* 未初期化変数報告対策 */
_p1 = 0 :_p2 = 0 :_p3 = 0 :_p4 = 0
return


// 再帰マクロ内で使う専用の stack マクロと命令

#deffunc local _rec_stack_new array arr, int type
on type = 2 goto *ON_DIMTYPE, *ON_SDIM
*ON_DIMTYPE
dimtype arr, type, len + 1
return
*ON_SDIM
sdim arr, siz, len + 1
return

#define rec_stack_new(%1,%2,%3,%4,%5)\
%1_arr0@m_rec = 0 :%1_arr1@m_rec = 0 :%1_arr2@m_rec = 0 :%1_arr3@m_rec = 0 :/* 未初期化変数報告対策 */\
_rec_stack_new@m_rec %1_arr0@m_rec, vartype(%2) :_rec_stack_new@m_rec %1_arr1@m_rec, vartype(%3) :\
_rec_stack_new@m_rec %1_arr2@m_rec, vartype(%4) :_rec_stack_new@m_rec %1_arr3@m_rec, vartype(%5) :\
dim %1_idx@m_rec :ldim %1_lba@m_rec, len@m_rec :\
%1_arr0@m_rec = %2 :%1_arr1@m_rec = %3 :%1_arr2@m_rec = %4 :%1_arr3@m_rec = %5

#define rec_stack_winding(%1,%2,%3,%4,%5,%6)\
%1_lba@m_rec(%1_idx@m_rec) = %2 :%1_idx@m_rec ++ :\
%1_arr0@m_rec(%1_idx@m_rec) = %3 :%1_arr1@m_rec(%1_idx@m_rec) = %4 :\
%1_arr2@m_rec(%1_idx@m_rec) = %5 :%1_arr3@m_rec(%1_idx@m_rec) = %6

#define rec_stack_unwinding_and_goto(%1,%2,%3,%4,%5)\
%1_idx@m_rec -- :rec_stack_peek@m_rec %1, %2, %3, %4, %5 :goto %1_lba@m_rec(%1_idx@m_rec)

#define rec_stack_peek(%1,%2,%3,%4,%5)\
%2 = %1_arr0@m_rec(%1_idx@m_rec) :%3 = %1_arr1@m_rec(%1_idx@m_rec) :\
%4 = %1_arr2@m_rec(%1_idx@m_rec) :%5 = %1_arr3@m_rec(%1_idx@m_rec)

// _REC_NOTRELEASE_ 定数を定義すると、rec_end 終了時に内部変数を開放しないようになる。若干の高速化。
;#define _REC_NOTRELEASE_@ // 有効化でオフになる。ここで有効にするか、#include より前に置く。
#ifndef _REC_NOTRELEASE_@
#define rec_stack_del(%1)\
dim %1_idx@m_rec :dim %1_lba@m_rec :\
dim %1_arr0@m_rec :dim %1_arr1@m_rec :dim %1_arr2@m_rec :dim %1_arr3@m_rec
#else
#define rec_stack_del(%1) /* 使用しない */
#endif


// 再帰マクロ内で使う 戻り値設定マクロ

// _REC_NOTUSESETREF_ 定数を定義すると、戻り値マクロを使用しないようになる。若干の高速化。
;#define _REC_NOTUSESETREF_@ // 有効化でオフになる。ここで有効にするか、#include より前に置く。
#ifndef _REC_NOTUSESETREF_@
#define global setref(%1) goto *%tlbl_ref1%i :*%tlbl_ref2%i :return %1 :*%tlbl_ref1%o :gosub *%tlbl_ref2%o
#else
#define global setref(%1) /* 使用しない */
#endif


// 再帰マクロ

#define global rec_prm1 %tprm%p0
#define global rec_prm2 %tprm%p1
#define global rec_prm3 %tprm%p2
#define global rec_prm4 %tprm%p3
#define global rec_re1 re1@m_rec
#define global rec_re2 re2@m_rec
#define global rec_re3 re3@m_rec
#define global rec_re4 re4@m_rec
#define global rec_brcode code@m_rec

#define global rec_begin(%1=_p1@m_rec,%2=_p2@m_rec,%3=_p3@m_rec,%4=_p4@m_rec)\
%tprm%s4%s3%s2%s1 %trec_lbl_rtn%i0 %trec_lbl_end%i0 \
rec_stack_new@m_rec %trec_var%i, %tprm%p0, %p1, %p2, %p3 :\
rec_call %tprm%p0, %p1, %p2, %p3 :\
rec_break :\
*%trec_lbl_begin%i :\
rec_stack_peek@m_rec %trec_var%p, %tprm%p0, %p1, %p2, %p3

#define global rec_call(%1=rec_prm1,%2=rec_prm2,%3=rec_prm3,%4=rec_prm4)\
rec_stack_winding@m_rec %trec_var%p, *%trec_lbl_back%i, %1, %2, %3, %4 :\
goto *%trec_lbl_begin%p :\
*%trec_lbl_back%o

#define global rec_return(%1=rec_re1,%2=rec_re2,%3=rec_re3,%4=rec_re4)\
rec_re1 = %1 :rec_re2 = %2 :rec_re3 = %3 :rec_re4 = %4 :goto *%trec_lbl_rtn%p

#define global rec_break(%1=0) rec_brcode = %1 :goto *%trec_lbl_end%p
#define global rec_level (%trec_var%p _idx@m_rec)

#define global rec_end(%1=rec_re1,%2=rec_re2,%3=rec_re3,%4=rec_re4)\
rec_return %1, %2, %3, %4 :\
*%trec_lbl_rtn%p :\
setref rec_re1 :\
rec_stack_unwinding_and_goto@m_rec %trec_var%p, %tprm%p0, %p1, %p2, %p3 :\
*%trec_lbl_end%p :\
rec_stack_del@m_rec %trec_var%p :\
rec_release_mcrtag

#define global rec_release_mcrtag %trec_var%o0 %tprm%o0%o0%o0%o0 %trec_lbl_begin%o0 %trec_lbl_end%o0 %trec_lbl_rtn%o0

#global
rec_init
#endif


ライト版

一部命令のパラメータ数が少ないだけでそれ以外は同じ、少ない分通常版より若干速い。

#ifndef m_rec
#module m_rec

// 初期化命令

#define global rec_init(%1=1,%2=64) _rec_init %1, %2
#deffunc _rec_init int _len, int _siz
len = _len
siz = _siz
/* 未初期化変数報告対策 */
_p1 = 0 :_p2 = 0 :_p3 = 0 :_p4 = 0
return


// 再帰マクロ内で使う専用の stack マクロと命令

#deffunc local _rec_stack_new array arr, int type
on type = 2 goto *ON_DIMTYPE, *ON_SDIM
*ON_DIMTYPE
dimtype arr, type, len + 1
return
*ON_SDIM
sdim arr, siz, len + 1
return

#define rec_stack_new(%1,%2,%3)\
%1_arr0@m_rec = 0 :%1_arr1@m_rec = 0 :/* 未初期化変数報告対策 */\
_rec_stack_new@m_rec %1_arr0@m_rec, vartype(%2) :_rec_stack_new@m_rec %1_arr1@m_rec, vartype(%3) :\
dim %1_idx@m_rec :ldim %1_lba@m_rec, len@m_rec :\
%1_arr0@m_rec = %2 :%1_arr1@m_rec = %3

#define rec_stack_winding(%1,%2,%3,%4)\
%1_lba@m_rec(%1_idx@m_rec) = %2 :%1_idx@m_rec ++ :\
%1_arr0@m_rec(%1_idx@m_rec) = %3 :%1_arr1@m_rec(%1_idx@m_rec) = %4

#define rec_stack_unwinding_and_goto(%1,%2,%3)\
%1_idx@m_rec -- :rec_stack_peek@m_rec %1, %2, %3 :goto %1_lba@m_rec(%1_idx@m_rec)

#define rec_stack_peek(%1,%2,%3)\
%2 = %1_arr0@m_rec(%1_idx@m_rec) :%3 = %1_arr1@m_rec(%1_idx@m_rec)

// _REC_NOTRELEASE_ 定数を定義すると、rec_end 終了時に内部変数を開放しないようになる。若干の高速化。
;#define _REC_NOTRELEASE_@ // 有効化でオフになる。ここで有効にするか、#include より前に置く。
#ifndef _REC_NOTRELEASE_@
#define rec_stack_del(%1)\
dim %1_idx@m_rec :dim %1_lba@m_rec :dim %1_arr0@m_rec :dim %1_arr1@m_rec
#else
#define rec_stack_del(%1) /* 使用しない */
#endif


// 再帰マクロ内で使う 戻り値設定マクロ

// _REC_NOTUSESETREF_ 定数を定義すると、戻り値マクロを使用しないようになる。若干の高速化。
;#define _REC_NOTUSESETREF_@ // 有効化でオフになる。ここで有効にするか、#include より前に置く。
#ifndef _REC_NOTUSESETREF_@
#define global setref(%1) goto *%tlbl_ref1%i :*%tlbl_ref2%i :return %1 :*%tlbl_ref1%o :gosub *%tlbl_ref2%o
#else
#define global setref(%1) /* 使用しない */
#endif


// 再帰マクロ

#define global rec_prm1 %tprm%p0
#define global rec_prm2 %tprm%p1
#define global rec_re1 re1@m_rec
#define global rec_brcode code@m_rec

#define global rec_begin(%1=_p1@m_rec,%2=_p2@m_rec)\
%tprm%s2%s1 %trec_lbl_rtn%i0 %trec_lbl_end%i0 \
rec_stack_new@m_rec %trec_var%i, %tprm%p0, %p1 :\
rec_call %tprm%p0, %p1 :\
rec_break :\
*%trec_lbl_begin%i :\
rec_stack_peek@m_rec %trec_var%p, %tprm%p0, %p1

#define global rec_call(%1=rec_prm1,%2=rec_prm2)\
rec_stack_winding@m_rec %trec_var%p, *%trec_lbl_back%i, %1, %2 :\
goto *%trec_lbl_begin%p :\
*%trec_lbl_back%o

#define global rec_return(%1=rec_re1)\
rec_re1 = %1 :goto *%trec_lbl_rtn%p

#define global rec_break(%1=0) rec_brcode = %1 :goto *%trec_lbl_end%p
#define global rec_level (%trec_var%p _idx@m_rec)

#define global rec_end(%1=rec_re1)\
rec_return %1 :\
*%trec_lbl_rtn%p :\
setref rec_re1 :\
rec_stack_unwinding_and_goto@m_rec %trec_var%p, %tprm%p0, %p1 :\
*%trec_lbl_end%p :\
rec_stack_del@m_rec %trec_var%p :\
rec_release_mcrtag

#define global rec_release_mcrtag %trec_var%o0 %tprm%o0%o0 %trec_lbl_begin%o0 %trec_lbl_end%o0 %trec_lbl_rtn%o0

#global
rec_init
#endif

使い方

通常のユーザー定義命令を使った再帰と、当モジュールでの再帰の書き方を見比べることで簡単に使い方を見てみます。
処理自体意味は無いですが、数字の 0 から指定した値まで1つずつ表示する処理になっています。

#module
#deffunc func int a
if a = 0 :return
func a - 1
mes a
return
#global

func 25

当モジュールを "m_rec.hsp" として保存してあるとすると
上記の再帰処理は以下のように書き直すことが出来ます。

#include "m_rec.hsp"

a = 25
rec_begin a
if a = 0 :rec_return
rec_call a - 1
mes a
rec_end

これでなんとなく使い方の感じを掴んで頂けるのでは無いかと思います。
もう少し詳しい仕様についてはこの後の「詳細」をご覧ください。

詳細

命令一覧

このモジュールをインクルードすることで以下の命令などが使えるようになります。

rec_init
rec_begin
rec_end
rec_return
rec_break
rec_call
rec_level
rec_re1 ~ rec_re4(ライト版は rec_re1 のみ)
rec_brcode

#それぞれのネーミングは結構悩んで今もまだこれでよいのかと思っている状態だったりします。より良い案があれば是非。

説明

repeat と loop の関係と同じ様に、まず rec_begin と rec_end で再帰処理をしたい範囲を囲みます。そして rec_begin のパラメータには再帰呼び出し毎にパラメータを受け取るための変数を指定します。ここに指定する変数は予め必要な値で初期化しておく必要があります、この時初期化する値の型は dimtype に指定できる型と同じです(未検証の物もあり)。しかし配列変数にする必要はありません。通常版では4つまでライト版では2つまでパラメータを指定できます。

// このスクリプトは何もしません、直ぐに再帰範囲を通り抜けます。
#include "m_rec.hsp"
a = 0
b = 0.0
c = ""
rec_begin a, b, c
rec_end

上のスクリプトでは再帰呼び出しが無いため再帰範囲の中を通っても直ぐに通過してしまっています。再帰呼び出しを実行するには rec_call を使います。
rec_call が実行されると rec_begin のパラメータに指定していた変数に rec_call のパラメータが渡されもう一度 rec_begin の次の位置から処理が再開されます。つまりこの rec_call が再帰で言う自分自身を呼んでいる相当の意味になります(自分自身とは rec_begin から rec_end までの範囲の事)。rec_call に指定するパラメータは rec_begin のパラメータと対応しているため、通常版は4つまで ライト版は2つまでのパラメータを使用できます。

もちろん rec_call だけでは永遠にループし続ける事になるので戻るための手段が必要です、rec_return は再帰モジュールにおいて HSP標準命令の return 相当の処理をし、一番最後に実行された rec_call の次の位置に実行位置を戻します。この時 rec_begin のパラメータに指定した受け取り変数の内容も自動的に1つ前の状態に戻されます。

// 9 からカウントダウンする1
#include "m_rec.hsp"
a = 0
rec_begin a
if a = 10 :rec_return
rec_call a + 1
mes a
rec_return
rec_end

rec_end の内部で rec_return が実行される様になっているので最後の rec_return は省略可能。

// 9 からカウントダウンする2
#include "m_rec.hsp"
a = 0
rec_begin a
if a = 10 :rec_return
rec_call a + 1
mes a
rec_end

極端な話 rec_return を使わなくても良いという。

// 9 からカウントダウンする3
#include "m_rec.hsp"
a = 0
rec_begin a
if a < 9 :rec_call a + 1
mes a
rec_end

rec_return と rec_end のパラメータには再帰モジュール用戻り値を指定することが出来ます。通常版では4つ、ライト版では1つの戻り値が設定可能で、専用の rec_re1~rec_re4 を使う事で設定した戻り値を取得できます。rec_re1~4 は rec_return, rec_end のパラメータと対応しているので、通常版では rec_re1~4 まで ライト版では rec_re1 のみが用意されています。また初期状態では一番目のパラメータは HSP の戻り値用システム変数にも反映される様になっています。パラメータを省略した場合はそれ以前の戻り値が維持されます。

// 戻り値取得1
#include "m_rec.hsp"
rec_begin
rec_end 1
mes rec_re1
mes stat

HSP の戻り値には触らないようにすることも出来ます。
インクルードするより前に _REC_NOTUSESETREF_ という名前で定数を定義することによって HSP の戻り値に関するシステム変数の値を変更しません。その場合 rec_re1~4 を使って取得します。

// 戻り値取得2
#define _REC_NOTUSESETREF_
#include "m_rec.hsp"
rec_begin
rec_end 1
mes rec_re1
mes stat

使う機会は余りなさそうですが再帰の範囲をネスト出来ます。

// マクロをネスト(ネスト出来ることの例で処理に意味はありません)
#include "m_rec.hsp"
a = 0
rec_begin a
mes "" + a
if a >= 4 :rec_return
rec_begin a
mes "." + a
if a >= 3 :rec_return
rec_call a + 1
rec_end
rec_call a + 1
rec_end

rec_break で再帰範囲を一つ分まるっと抜けることが出来ます。再帰の中止などしたい場合にお使いください。パラメータに値(型問わず)を指定することで中断理由をブレーク後 rec_brcode で取得できます。
更に rec_level で再帰レベルを取得出来ます。

// レベルとブレーク
#include "m_rec.hsp"
a = 0
rec_begin a
mes a
if rec_level > 10 :rec_break "深さが 10 を超えた"
rec_call a + 1
mes "ここは実行されない"
rec_end
mes rec_brcode

rec_init は、再帰が深くなる事があらかじめ分かっている場合に、内部で使っている配列変数の確保サイズを前もって指定することでHSPの配列の自動拡張を抑制し速度低下を抑える事ができます。何も指定しない場合 rec_init 1 が指定されています。

// 要素数を指定しない時と指定した時の速度差を見る
#uselib "winmm.dll"
#cfunc timeGetTime "timeGetTime"
#include "m_rec.hsp"

time = timeGetTime()
rec_begin a
if a = 9999 :rec_return
rec_call a + 1
rec_end
mes "" + (timeGetTime() - time) + "ms"

time = timeGetTime()
rec_init 10000 ; 内部で使っている配列を rec_begin 時に 10000要素分確保する事を指示する
rec_begin a
if a = 9999 :rec_return
rec_call a + 1
rec_end
mes "" + (timeGetTime() - time) + "ms"

ちなみに rec_end を正規の方法で抜けると内部で使用された変数は再確保され配列のサイズが初期化されます。
その際インクルードより前に _REC_NOTRELEASE_ という名前で定数を定義することによってそれをしないようにすることもできます。ほんの微々たるほど速くなりますがほぼデバッグ用途です。


あと注意というか補足の様な物をいくつか。
  • 基本的にこれらの命令は rec_begin と rec_end の範囲内でのみ使用可(rec_re1~4とrec_brcodeは別)。
  • それぞれの命令へのパラメータは省略できるが、その分速くなったりはしない。
  • 再帰処理中に rec_begin へ指定した変数に値を代入しても、内部の配列の要素は書き換えられることはない。
  • rec_begin のパラメータに指定した変数の型と rec_call に渡すパラメータの型は合わせること。
  • rec_begin, rec_call のパラメータにモジュール変数を使う場合は工夫が必要な事がある。
  • 処理はほぼマクロで書いてあるので沢山使うとオブジェクトファイルのサイズが増える。
  • rec_begin に入った時点の最初の再帰レベルは1。
  • 内部で沢山の変数を使っているのでデバッグウィンドウでモジュールのチェックを入れると一杯出てきて驚くかも。
  • バランスの中でなるべく速くを心がけたものの普通にユーザー定義命令で再帰するのに比べて一桁くらい遅い。

こんなところでしょうか。

散々書き散らかしてきましたが
やっぱりヘルプファイル作った方が良いかな
というかファイルにまとめてダウンロード出来る様にした方が良いかな。

サンプルスクリプト

一応実用的?なサンプルスクリプトを作って見ました。

このページの上の方にある再帰モジュールと、下記のサンプルスクリプトを同じフォルダに保存して実行してください。
再帰モジュールのファイル名は、
通常版が m_rec を、ライト版は m_rec_lite を推奨します。

最大公約数

以下のサイトを参考に当モジュールで書いてみました。
ほぼコピペに近いという・・・。

参考にしたサイト:
http://fs-cgi-basic01.freespace.jp/~hsp/ver3/hsp3.cgi?print+201107/11080014.txt
http://rpen.blogspot.jp/2007/05/blog-post_06.html

#include "m_rec.hsp"

a = 15
b = 6

rec_begin a, b
if b = 0 :rec_return a, rec_level - 1
rec_call b, a \ b
rec_end

mes a
mes b
mes "最大公約数:" + rec_re1
mes "最小公倍数:" + (a * b) / rec_re1 ; おまけ
mes "要したレベル:" + rec_re2


階乗

2014/04/22 追記:
再帰との相性が良いらしいのでサンプルとして階乗を追加。
これもほぼウィキペディアのサンプルのコピペ状態だったり。
参考にしたサイト:
http://ja.wikipedia.org/wiki/%E5%86%8D%E5%B8%B0

#include "m_rec_lite.hsp"

n = 6

rec_begin n
if n = 0 :rec_return 1
rec_call n - 1
rec_end n * stat

mes stat


塗りつぶし

深いレベルの再帰が出来るのを活かして塗りつぶし処理をやってみました。
一応VRAMを使って高速化してたり。
2014/03/31 追記:スクリプトの一部が古いままで実行できなかったのを修正しました。

// m_rec のサンプルスクリプト
// 塗りつぶしモジュール


;#include "m_rec.hsp" // どっち使っても良いけど、
#include "m_rec_lite.hsp" // 今回は lite で十分。
rec_init 1048576 // 予め確保しておく配列変数のサイズ


#ifndef RGB
// 色の並びは vram に合わせて 0x00RRGGBB
#define global ctype RGB(%1=0,%2=0,%3=0) (((%1) & 0xff) << 16) | (((%2) & 0xff) << 8) | (((%3) & 0xff))
#endif


// 塗りつぶしモジュール
#ifndef mod_gfill
#module mod_gfill

#define ctype VRAM_INDEX(%1,%2,%3,%4) ( (( (%4) - 1 - (%2) ) * (%3) ) + ( (%1) * 3 ) ) // VRAM 書き込み用にインデックスを計算 VRAM_INDEX(x,y,w,h)
#define ctype VRAM_WIDTH(%1) ( ( ( (%1) * 3 ) + 3 ) & $fffffffc ) // VRAM 書き込み用に4の倍数になるように切り上げる
#define VRAM_SEL(%1) gsel %1 :mref vram@mod_gfill, 66 // VRAM_PGET, VRAM_PSET で使う VRAM 変数指定
#define VRAM_PGET(%1,%2,%3,%4,%5) memcpy %1, vram@mod_gfill, 3, 0, VRAM_INDEX(%2, %3, %4, %5) // pget の VRAM 版
#define VRAM_PSET(%1,%2,%3,%4,%5) memcpy vram@mod_gfill, %1, 3, VRAM_INDEX(%2, %3, %4, %5), 0 // pset の VRAM 版

#deffunc gfill int _x, int _y
x = _x
y = _y
sx = GINFO_SX
sy = GINFO_SY
sx2 = VRAM_WIDTH(sx)
clr_get = 0
clr_fill = RGB(GINFO_R, GINFO_G, GINFO_B)
VRAM_SEL GINFO_SEL
VRAM_PGET clr_bg, x, y, sx2, sy
if clr_bg = clr_fill :return

rec_begin x, y
if x < 0 | y < 0 | x >= sx | y >= sy :rec_return
VRAM_PGET clr_get, x, y, sx2, sy
if clr_get ! clr_bg :rec_return
VRAM_PSET clr_fill, x, y, sx2, sy

rec_call x, y - 1
rec_call x + 1, y
rec_call x, y + 1
rec_call x - 1, y
rec_end
return

#global
#endif


#if 01
randomize
// 適当な模様を描画
max = 3
xsize = GINFO_WINX / max, GINFO_WINX / (max - 1)
ysize = GINFO_WINY / max, GINFO_WINY / (max - 1)
repeat max
c = cnt
repeat max
line GINFO_WINX, ysize * cnt + ysize / 2, 0, ysize * c + ysize / 2
line xsize * cnt + xsize / 2, GINFO_WINY, xsize * c + xsize / 2, 0
circle xsize(1) * cnt - xsize(1) / 2, 0, xsize(1) * cnt + xsize(1) - xsize(1) / 2, GINFO_WINY, 0
circle 0, ysize(1) * c - ysize(1) / 2, GINFO_WINX, ysize(1) * c + ysize(1) - ysize(1) / 2, 0
loop
loop
// クリックで塗りつぶし
*MAIN
stick key, 256, 1
if (key & 256) ! 0 {
pget mousex, mousey
if RGB(GINFO_R, GINFO_G, GINFO_B) > 0 {
redraw 0
hsvcolor rnd(192), rnd(160) + 96, 255
gfill mousex, mousey
redraw 1
}
}
await 16
goto *MAIN
#endif
関連記事

コメント

いくつか本文の内容を手直ししました。
ついでにサンプルスクリプトも追加。
rec_return の省略時の説明を加筆。
サンプルの最大公約数の一部に余分な記述があったので訂正。
確認と報告のコメント有難うございます、また返事が遅くなってしまってすみません。
上手く文章が書けなくて時間を置いてる間にコメント消しちゃったんですね。もうご覧になられるか分かりませんが、消えたコメントに返答するのも失礼じゃないかとも思いますが、一応お答えさせていただきます。

当初は、サイト上でHSPのコードがそのまま実行されることも、同時にソースが閲覧できる様になることも全く想定していなかったので、同様の疑問も最もかと思われます。
更にこのブログ版とコンテスト版のモジュールでは扱いに少し違いがあり紛らわしいのも不安になる要因かも知れません。ブログ版ではクレジットは任意ですがコンテスト版では必須と読める。

サイトの性質的に事実上再配布となるとのことですが、当モジュールはブログ版、コンテスト版ともに、この様なケースにも問題なく自由に使用と公開が出来ることをここに明示いたします。
また公開先も確認させていただきましたが、作者名も入手元も明記してあったので必要十分によって全くもって問題ないです。

本当はと言うと使用してくれただけで嬉しいのであまり細かいことは気にならないのですが使う側は気になりますね。素直に何か適当なライセンスでも適用しておいたほうが使いやすかったでしょうか、当初は NYSL で良いかなと思ってましたがその時決めきれず保留にしたのが災いした様です。
既に公開されている物の扱いを今更変えるのも不便なのでそのままにしておきます、今後更新版を出す場合にあたってはそこら辺も考慮した上で公開していきたいと思います。

最後に話はずれますが、開発時ちょっとずつ処理が違う数十パターンで簡潔性や実効速度など見比べてたりして結構苦労して作っていたので、ただ使って頂けるだけで嬉しいです。作ったかいがありました。
気に入って頂けたら幸いです。
脱線したところで、ではではこのへんで。
あ、すみません、m_rec.hspを含まない形でソースコードを公開することができたため、
一度コメントを削除しました。

> ライセンス
確かに、ライセンス表記があるとより気軽に使えるかもしれないです。

改めてもう一度。
HSPでシダを描画する - HSP部屋
http://hsproom.me/program/view/?p=78
にてこちらのm_recを使わせていただきました。
再帰無しで作ろうとしたところ、難易度が跳ね上がってしまって困っていた際にm_recを見かけまして、
m_recを使ってみたところ簡単に書くことができました。ありがとうございます。
どうもご確認頂き有難うございます、

ライセンスについては確かにそうですね、今後は利便性を考えて明記していきたいと思います。
改めてのページへのリンクと報告もありがとうございました。
また余計な手間をかけさせてしまった様で恐縮です。

実際の所、このリンク先の処理ではNの値が20と深さが20以上にならないので通常のユーザー定義関数を使った再帰で十分というかより楽で高速に動くので、当モジュールの必要性はあまりないと言うのは内緒ですが(笑)

またまた脱線してしまいましたが、ご丁寧に書き直し報告していただいて有難うございました。

コメントの投稿

   管理者にだけ表示を許可する
上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。