約 7,945 件
https://w.atwiki.jp/rgss2study/pages/27.html
入門編・10 シューティングゲームもどきを作る 敵キャラを表示する1・敵キャラクラスの設計 今回から、敵キャラを作成していきます。が、この部分はかなり管理人のオリジナルな色が強く、 この方法が効率的であるかかなり疑問があります。 詳しい方、よろしければご教授下さいませ……! 敵キャラに求められるものと、配列の使い方 自機と敵の一番の違いは(特にプログラム上の違いは)、敵キャラは複数存在する、ということです。 プログラムをやったことのある方なら聞いたことがあると思いますが、敵キャラを作る際には 敵キャラの『配列』を作ることが好ましいのです。 配列というのは、(1,1,2,4)のように、要素(ここでは数字)を、いくつか並べたもの。 例えば、(1,1,2,4)は、要素数4の配列であると言い、3番目の要素は2である、というような使い方をします。 そして、配列の何が便利かというと、配列にすると、通し番号を使って繰り返しで効率的に処理できるという一点が最も重要です。 例えば、敵キャラを5体出す場合、配列を使わないと、処理内容としては (敵キャラAの処理) (敵キャラBの処理) (敵キャラCの処理) (敵キャラDの処理) (敵キャラEの処理) と、敵キャラの数だけ繰り返して記述する必要があります。これでは、記述が面倒だし、敵キャラ数が変わった場合への対応も明らかに難しくなります。 では、配列を使うとどうなるでしょうか。敵キャラが5体なら、次のような配列を作ります。 (敵キャラ[0],敵キャラ[1],敵キャラ[2],敵キャラ[3],敵キャラ[4],) []の中の数字は通し番号。要素には、それぞれ敵キャラクラスのインスタンスが入ります。 そして、処理内容はこうです。 nを0から4(敵の数-1)まで変化させて、以下の処理を行う。 敵キャラ[n]の処理 ここまでを繰り返す 日本語で書いていますが、こんな感じです。まず、n=0、すなわち敵キャラ[0]の処理が行われ、『ここまで繰り返す』まで行ったら次はn=1となり、敵キャラ[1]の処理。と、敵の数だけ繰り返し、結果、先ほどの単に敵の数だけ処理を書いた場合と同じになります。 冗長な繰り返しが無くなり、さらに敵の数をいくつに変えても応用可能です。 敵キャラの数が100体とかになった場合、どちらが有利かは明らかでしょう。 というわけで、今回、敵キャラクラスの他に、敵キャラの配列を管理する、敵全体クラスを作成します。このクラスには、繰り返し用に、敵が全部で何人いるかといった情報も持たせるようにします。 では、クラスを設計しましょう。 敵キャラクラスを作る 自機と基本的に同じなので、ここでは簡単にいきましょう。 #============================================================================== # ■ Game_Ememy #------------------------------------------------------------------------------ # シューティングゲームの敵キャラを扱うクラスです。 #============================================================================== class Game_Enemy #-------------------------------------------------------------------------- # ● 公開インスタンス変数 #-------------------------------------------------------------------------- attr_accessor x # 左上 X 座標 attr_accessor y # 左上 Y 座標 attr_accessor alive # 生きているか否か #-------------------------------------------------------------------------- # ● 定数 #-------------------------------------------------------------------------- WND_X = 544 #ウィンドウのサイズ WND_Y = 416 SIZE_X = 32 #敵キャラのサイズ SIZE_Y = 32 #-------------------------------------------------------------------------- # ● オブジェクト初期化 #-------------------------------------------------------------------------- def initialize(x,y) @x=x @y=y @alive = true end #-------------------------------------------------------------------------- # ● 更新 #-------------------------------------------------------------------------- def update end end まあ、こんなところでしょうか。インスタンス変数には、自機と同様x座標、y座標のほか、生きているか否かを表す@aliveを用意しました。ここに入る値はtrue(正しい)かfalse(間違っている)で、生きている時trueになります。 これにより、例えばfalseの時は表示しない、あたり判定が無いという使い方をします。 また、敵の位置は敵ごとに自由に決めたいので、initializeの際に座標を渡し、それによって位置が決まるようにしました。 updateは後で必要になりそうだから作っただけで、今はとりあえず何もしません。 敵キャラのスプライトのクラスを作る これも、自機の時と同様です。 #============================================================================== # ■ Sprite_Enemy #------------------------------------------------------------------------------ # 敵キャラ表示用のスプライトです。Game_Enemy クラスのインスタンスを # 監視し、スプライトの状態を自動的に変化させます。 #============================================================================== class Sprite_Enemy Sprite_Base #-------------------------------------------------------------------------- # ● 公開インスタンス変数 #-------------------------------------------------------------------------- attr_accessor enemy #-------------------------------------------------------------------------- # ● オブジェクト初期化 # viewport ビューポート # z スプライト・ビューポートのz座標 # myMachine キャラクター (Game_Enemy) #-------------------------------------------------------------------------- def initialize(viewport,z,enemy) super(viewport) @enemy = enemy self.bitmap = Cache.character("Enemy") self.z=z self.viewport.z=z update end #-------------------------------------------------------------------------- # ● 解放 #-------------------------------------------------------------------------- def dispose super end #-------------------------------------------------------------------------- # ● フレーム更新 #-------------------------------------------------------------------------- def update self.x = @enemy.x self.y = @enemy.y end end というか、MyMachineがEnemyになっただけです。あ、あとグラフィックがenemyに。よかったら下の画像を使って下さい。名前をEnemyとしてインポートして下さい。 ここでは、簡略のため大きさを32×32と定数で決めてしまっているので、オリジナルのものを使う場合もこのサイズでお願いします。 大きさの違う色んな敵を出す場合は、敵キャラの大きさを変数にしたり、画像のファイル名をnewの際に渡したりする必要があるでしょう。あるいは、敵の種類ごとに種類idを振って、それで管理するとか。 使い方も、基本的には同じです。ただ、敵キャラごとにビューポートを作成するのは面倒なので、敵キャラのビューポートは全敵キャラで共通のものを使います。 敵キャラのz座標も、敵キャラごとに変えずに同じ値にします。この場合、後に作成したオブジェクトほど上に表示されます。 敵全体クラスを作る さて、ここが新しい部分です。敵全体クラスを作りましょう。 必要最低限のものは、敵キャラの配列と、敵キャラの数、すなわち配列の要素数です。 今回は、便利かと思って生きている敵の数も一応インスタンス変数にしました。 クラス名は、Game_Enemiesとしました。 #============================================================================== # ■ Game_Ememies #------------------------------------------------------------------------------ # シューティングゲームの敵キャラ全体を扱うクラスです。 #============================================================================== class Game_Enemies #-------------------------------------------------------------------------- # ● 公開インスタンス変数 #-------------------------------------------------------------------------- attr_accessor enemy_num # 敵の総数 attr_accessor alive_num # 存在している(alive)敵の数 attr_accessor enemies # Game_Enemyの配列 attr_accessor ;s_enemies # Sprite_Enemyの配列 #-------------------------------------------------------------------------- # ● オブジェクト初期化 #-------------------------------------------------------------------------- def initialize(enemy_num) # 敵キャラの総数をenemy_numとして渡す @enemy_num = enemy_num @alive_num = enemy_num @enemies = Array.new(enemy_num) @s_enemies = Array.new(enemy_num) viewport = Viewport.new(0,0,544,416) viewport.z = 200 for i in 0..enemy_num-1 do @enemies[i] = Game_Enemy.new(200+rand(312),rand(384)) @s_enemies[i] = Sprite_Enemy.new(viewport,200,@enemies[i]) end end #-------------------------------------------------------------------------- # ● 更新 #-------------------------------------------------------------------------- def update for i in 0..enemy_num-1 do @enemies[i].update @s_enemies[i].update end end end さて、微妙に複雑ですが一つずつ見ていきましょう。 まずはinitialize。 newの際は、数字を渡し、これを敵の総数とします。本当は、変な値が入っていた時にチェックできたりするといいのですが、 今回は面倒なのでそれはしていません。余力があれば挑戦しましょう。 そして、最初敵は全員生きているものとして、enemy_numとalive_numに渡した数、つまり敵の数を入れます。 @enemies = Array.new(enemy_num) @s_enemies = Array.new(enemy_num) 問題が、これです。このArrayというのが、配列という意味です。Array.newによって、配列を作り出しています。()の中に渡すのは、想像できると思いますが、敵キャラの数。敵キャラの配列の要素数ですね。これによって、enemy_numの要素を持つ配列が作られます。@s_enemiesも同様で、こちらは敵キャラのスプライトを配列にしています。 なお、この段階では要素は何も入っておらず、これが敵キャラの配列であることすら未確定です。 続いて、ビューポートを作成し、このz座標を200としています。この値は敵キャラ全体で共通になります。 for i in 0..enemy_num-1 do @enemies[i] = Game_Enemy.new(200+rand(312),rand(384)) @s_enemies[i] = Sprite_Enemy.new(viewport,200,@enemies[i]) end 続くこれが、配列のさっき言った『効率のいい繰り返し』の記述です。 先ほど、配列にはまだ何も入っていないと書きましたが、ここで配列に中身を入れています。すなわち、newによって作成した、Game_EnemyクラスのインスタンスとSprite_Enemyクラスのインスタンス。 iが、0からenemy_num-1まで変化して、この2行を実行します。 "enemy_num-1"になるのは、配列の添え字が0から始まるため、最後の要素の添え字が全体の要素数の数字より1小さくなることによります。 1行目で敵キャラを、2行目で敵キャラのスプライトを、それぞれ割り当てている(自機のcreate_myMachineの場合と似ている)わけですが、微妙に見慣れないものがあります。それは、rand(312)など、rand(数字)という命令。 これは、乱数です。実行のたびに、0以上数字未満(rand(100)なら0から99)の値を返します。 Game_Enemyのinitializeでは、敵の位置を渡すことにしていましたね。 つまり、ここでは敵の位置を乱数を使って決め、配置しているのです(x座標では、定数に乱数を足し、ある程度右の位置に行くようにしています)。 繰り返しの度にrandの値は変わるので、敵にはそれぞれ別のx座標、y座標が割り当てられることになります。 def update for i in 0..enemy_num-1 do @enemies[i].update @s_enemies[i].update end end updateの中でも繰り返しが使われています。(誤りがあったため、4行目を追記しました。) 敵全体クラスのインスタンスのupdateを行うことで、そこに所属する敵全てがupdateされます。 シーン中の記述は、敵全体のupdateのみを行えばいいことになります。 では、次回この敵キャラのクラスをシーンに組み込み、敵キャラを表示してみましょう。 前へ・次へ すごくわかりやすいです!! わかりやすい講座を探していたのでありがたいです。 続き楽しみにしています! -- (名無しさん) 2009-04-11 17 41 26 コメントありがとうございます。 こんなに早く反応があるとは……。 ヘルプの解説を含め、シーンの構造などを説明しているものが ほとんどなかったのが不満で、作ってみました。 本当に実はあんまり分かっていないので、至らないところも 多いかと思いますが、よろしくお願いします。 -- (管理人) 2009-04-11 18 58 22 すいません、『自機を表示する』の記述に誤りがありました。 updateの中身を書かないと、自機の位置が正しく反映されません。 記事を修正しておきます。 -- (管理人) 2009-04-14 21 59 20 ツクール古参ですがRGSSに四苦八苦でした(^^;) が、シーンの基本構造を解説した記事にガチで救われました 応援してるんで、がんばってください!!続き楽しみです。 -- (うきぶくろ) 2009-04-27 12 17 15 コメントありがとうございます。 一番書きたかったそのあたりは書いたもののなんだか忙しい+ 自分でもRGSSの解読に手間取っているためになかなか更新できていません。 どの程度まで書けるか分かりませんがよろしくお願いします。 -- (管理人) 2009-04-28 08 40 35 敵キャラを作成する・2の最後の方に、 updateの際に敵キャラを扱うスプライトの更新を行うのを忘れていたのを修正しました。 イタリック体で修正が入っている部分です。 -- (管理人) 2009-05-02 22 10 23 UFOが見にくかったので画像に縁取りをつけました。 -- (管理人) 2009-05-04 08 56 56 ホントにスゴイと思います。尊敬します。 大変だと思いますが少しずつでいいんで頑張ってください。 -- (ユウ) 2009-05-12 00 07 40 コメントありがとうございます。 恐らくこれを見て作る側はすぐなんでたいそうもどかしいと思いますが……。 ところで、今のところはちゃんとこの講座の通りで動くようになっているのでしょうか? 一応チェックはしてますが、多少ポカはすると思ってたので指摘が無いのがむしろ意外です。 -- (管理人) 2009-05-13 21 11 52 大丈夫です。ちゃんと動いています。 頑張ってください。続きが楽しみです。 -- (ユウ) 2009-05-18 01 06 59 1段目と言われている部分は『RGSS2』 2段目と言われている部分は『プリセットのスクリプト』 と言われると、より解り易い表現なのでは? -- (通行人) 2009-11-05 12 48 40 @enemies[i] = Game_Enemy.new(200 + rand(312), rand(384)) ↑argumentErrorが発生しました。wrong number of arguments(2 for 0) どうすればいいでしょうか、頼みます。 -- (help me!!) 2010-01-05 00 22 50 マウス入力はできますぜ。 Win32とかなんやらをつかって。 キーボードもできるみたいですが見つかりません。 探してます。 -- (シャミラ) 2010-03-24 05 28 28 勉強させてもらってます、とても参考になります。 入門編・10 にあるGame_Ememiesのクラスの attr_accessor ;s_enemies ですが、;を に変えないとエラーを吐きますので コピペでやった人は修正した方がよさそうです -- (邯鄲) 2010-09-04 17 27 18 @enemies[i] = Game_Enemy.new(200 + rand(312), rand(384)) ↑argumentErrorが発生しました。wrong number of arguments(2 for 0) Game_Enemyクラスって最初からありますよね。 それが原因では? -- (ぽん) 2010-10-03 23 11 42 製作初心者なのでとても助かります!! -- (ありがとうございます!) 2011-02-05 12 44 03 セーブ/ロードまわりはMarshalを使ってスクリプトで記述されていますし、 ファイルの読み書きもRubyと同様にできますから、 セーブやロードをいじるのはスクリプト操作でできることでは? -- (ななし) 2011-05-20 13 04 19 ダンジョンなら3D化できると、下記サイトに書いてありました。 -- (だれかさん) 2011-08-18 07 26 19 アルバイトはじめましたd(´∀`*)グッ♪ http //64n.co/ -- (私だ) 2012-01-09 04 49 41 風俗店を探よりココ!!男性は報酬あり!d(´∀`*)→ http //www.44m4.net/ -- (江梨子) 2012-08-20 17 11 02 名前 コメント すべてのコメントを見る
https://w.atwiki.jp/kiuu/pages/102.html
無限ループでCPU高負荷 ページ bugtrack 投稿者 ばびぶべぼん 優先順位 緊急 状態 完了 カテゴリー バグ 投稿日 2009/11/02 (月) 15 22 23 バージョン 0.11β , 0.11 , 0.12テスト版 メッセージ 起動するとCPUが高負荷になります。 (ソースは、なでしこプログラム板のを拝見しています) 原因はステータスバーの時刻表示を更新する処理のようです。 「オンの間」のループですが、waitを入れていないため発生します。 「秒待つ」を入れるか、タイマー部品を使った方が良いと思います。 もし、よかったら開発の方お手伝いしましょうか? 詳細情報 発生するバージョンはv0.11β以降で発生。0.10a以前ではステータスバー時計表示が無いため発生しない。 解決策 問題個所↓ オンの間 下バーは今。 今を「 」で区切って時間配列に代入。 時=INT(時間配列¥0) 分=INT(時間配列¥1) 秒=INT(時間配列¥2) 続ける。 改良案↓ オンの間 下バーは今。 1秒待つ。 続ける。 こうすると1秒間隔で更新することになり、負荷が減る。 問題点:最大1秒の誤差が生じる。(許容範囲と思いますが…) あと、時間配列に代入するところですが利用している所がないので不要では? 改良案2↓ 時計更新とはタイマー その間隔は1 その時満ちた時は〜下バーは今 その開始 こちらはタイマー部品を使ってます。 プログラムの都合上、こちらの方が良いと思われます。 これも1秒間隔ですので誤差が最大1秒生じます。 どうしても正確さを求めるのであれば、間隔の値を小さくします。 でも、あまりにも小さすぎると負荷が大きくなりますので... わかりましたまっててください -- k.k 上のコメントに追記 開発を手伝ってもらってもいいですが、なでしこページにあるソースは古い(今はv0.12テスト版)ので個人ページの方にメールアドレスを書いていただければ幸いです(ソースを添付するので) -- k.k 了解です。 -- ばびぶべぼん v0.12テスト版でも同じ現象を確認 -- ばびぶべぼん じゃあ古いv0.09以前はどうでしょうか -- k.k こちらの問題が解決しなかったら過去ログwikiにおきます -- k.k 確認してみます。 -- ばびぶべぼん v0.09b以前ではステータスバーに時計がないため発生しないですね。 -- ばびぶべぼん 詳細をまとめておきました。 -- ばびぶべぼん 改良案2を適用することにします -- k.k 分かりました。 -- ばびぶべぼん v0.12テスト版2にて修正されたことを確認しました。 -- ばびぶべぼん 名前 コメント
https://w.atwiki.jp/sevenlives/pages/594.html
コレクション・フレームワーク 読み:これくしょんふれーむわーく 英語:Collection Framework 別名:リスト 意味: コレクション・フレームワークとはオブジェクトの集合を柔軟的に扱う仕組みのこと。 配列では1度定義した大きさを変更できないなどの不便があるためオブジェクトを柔軟に扱うために作られました。 VectorクラスやHashTableクラスなどのスレッドセーフなクラスが初期に存在していましたが負荷が大きいため現在はスレッドセーフではない処理の速い柔軟なコレクション・フレームワークを使うのが主流です。 配列とコレクションフレームワークの特徴。 配列 コレクション 型 参照型 参照型(utilパケージのオブジェクト) 要素数 不変(最初に定義された数) 可変(あとから追加、削除可能) 格納値 決められた同じ型のみ java.lang.Object型であるなら自由 他 基本データ型を格納できない 2010年10月27日 コレクション? JCF? キュー? スタック? マップ? ハッシュ・テーブル イテレータ? スレッド・セーフ? 同期? java.utilCollection?List? Set? Map? [Java[]]
https://w.atwiki.jp/suffix/pages/825.html
管理人自身の備忘録を記載。 余談だが、Excelでは文字コードとしてUnicodeを使用しているらしい。 エディタ(VBEの使い方) VBE コーディングガイドライン 基本文法 VBA条件分岐 標準モジュール、クラスモジュール、ユーザーフォームの違い VBAの変数宣言 関数の使い方 関数の引数 VBA配列 VBA動的配列 VBA連想配列 正規表現 空を表現する方法 VBAエラー処理 VBAでオブジェクト指向? VBAコンストラクタ VBAリフレクション GUI周り 実行ボタンの作り方 終了ボタンを作りたい ツールバーを作る カレンダーをユーザフォーム内に加えたい プログレスバーの作り方 リストボックスの選択状態を取得 コントロールを動的に追加する セル、テーブル、操作 セルの指定方法 色がついたセルの数を数えたい セルに色をつけたい セルのコメントを編集する セル内の数式を扱う 列番号を数値に変える セルが非表示かどうか調べる テーブルの指定方法 文字列の一部分だけ変える 高速化関連 画面更新のチラチラをなくす For Eachを使う(処理順が保証されないのは注意) Excelの自動計算を停止する Withを利用する 2次元配列に格納してから、セルに書き出す デバッグ関連 ブレークポイントを設定する デバッグ時のみ動作させたい処理を記述する その他操作 ファイルを開く 曜日を調べる ブックが開かれたor閉じられた時に処理を呼び出す シートが編集されたときに関数の再計算を行うようにする 文字列を探す Variant型変数がどの型で処理されているか調べる 呼び出し元を調べる VBAでOfficeの機能を呼び出す VBAでWindowsAPIを使う 独自アドインを作成する 外部プログラムを呼び出す 複数のオブジェクトについて共通のイベントを起こさせる 分かりやすく説明している他サイトの紹介 ワークシートの最終行の見つけ方 エクセルシートの差分の取り方 フォームコントロール&ActiveXコントロールの違い
https://w.atwiki.jp/bousoku/pages/497.html
高速月配列タイパー 月配列を駆使しTWJKでは上位にランクインしている。 英語ではdvorakを使用。 【TW】 JK 1164253 ZG ZF ZH ZF ZH 13/08/16 EW 1120108 XX XX XS XS XS 14/04/16
https://w.atwiki.jp/projecthikky/pages/41.html
C言語のメモ このページを編集 C言語 入門 入出力 もっとも一般的な方法としてC言語標準ライブラリを利用する。 int putchar(int c) // putc(c,stdout)と同じ int getchar(void) // getc(stdin)と同じ stdout, stdin, stderr ファイルはプログラム実行時に自動的に開かれるため、明示的に開く必要はない。 手元の環境で stdout, stdin, stderr は /dev/std... にシンボリックリンクとしてあった。 関数プロトタイプを見てわかる通り戻り値、引数は int すなわちデータブロックのインターフェイスになる。 ディレクトリ操作(Windows) コンソール操作Unix系(curses, ncurses) Windows(PDCurses) Windows(conio, wincon) 記憶クラス指定子(extern, auto, register, static, typedef) 配列の初期化 組み込みのアルゴリズム実装バイナリサーチ(二分木探索)bsearch クイックソート(高速整列) qsort ディレクトリ操作(Windows) ディレクトリ操作(Windows) コンソール操作 Unix系(curses, ncurses) コンソール上でグラフィカルな操作ができる curses - Wikipedia http //ja.wikipedia.org/wiki/Curses ncurses - Wikipedia http //ja.wikipedia.org/wiki/Ncurses Windows(PDCurses) Windows用のCurses オープンソースのソースコードで自由に改変して使える LinuxのCursesを使ったプログラムの移植などをしやすいのでよく使われている Public Domain Curses http //pdcurses.sourceforge.net/ Windows(conio, wincon) 一部のWindowsのCコンパイラに付属してるライブラリ conio (コンソールIO)。conio.hをインクルードして使う。 (注意:Cの標準規格のライブラリではないので実装は様々、各コンパイラごとに挙動が違う。) もしくはコンソール用Win32APIのwincon.hをインクルードして使う(これは色々行える、ライブラリは kernel32.lib) 詳細は各コンパイラのconioライブラリやコンソール用Win32APIのドキュメントを読むべし。 Conio.h http //en.wikipedia.org/wiki/Conio.h コンソールWin32API http //msdn.microsoft.com/ja-jp/library/cc429123.aspx コンソールWin32APIの使い方 http //eternalwindows.jp/windevelop/console/console00.html 記憶クラス指定子(extern, auto, register, static, typedef) 記憶クラス指定子 配列の初期化 配列の初期化 組み込みのアルゴリズム実装 バイナリサーチ(二分木探索)bsearch たぶんきっと高速だろう 任意の型の配列を探索できる クイックソート(高速整列) qsort たぶんきっと高速だろう クイックソートは安定ソートでは無いから注意 任意の型の配列を整列できる
https://w.atwiki.jp/isoroku_be/pages/78.html
情報 作者名:moka 引用元:日本語プログラム言語なでしこ公式バイブル「なでしこの育て方」 概要 二次元配列をテーブルタグで囲んで返します。 解説 引数 S:二次元配列 A:タグ属性 返り値 HTMLソース サンプルプログラム S=「a,b c,d」をCSV取得。 Sを「border=1 cellpadding=5」でテーブルタグ作成。 それを言う。 // table border=1 cellpadding=5 // tr td a /td td b /td /tr // tr td c /td td d /td /tr // /table //本体 ●テーブルタグ作成({配列=?}Sを{文字列=""}Aで) Rとは文字列 #結果 Tとは文字列 #一時格納用 もし、S=「」ならば、S=それ もし、A 「」ならば、A=「 {A}」 R=「」 Sを反復 T=「」 対象を反復 Tに「 td {対象} /td 」を追加 Rに「 tr {T} /tr 」を一行追加 R=「 table{A} {~}{R} /table {~}」 Rで戻る。 名前 コメント
https://w.atwiki.jp/compatible/pages/14.html
ステートメント 変数 配列 Foreach文 関数 URLエンコード 表示・出力 文字列と数字の変換 ファイルのダウンロード データ送信(GET・POST) データ受信(GET・POST) 多倍長演算 配列の要素数
https://w.atwiki.jp/imops-forth/pages/21.html
序 データスタックが変数の役割を果たすとはいえ、forthでも変数は定義できる。 けれども、構文上は、値はすべて一旦スタックにおかれ、スタックを通じて処理されるような形になる。 変数の種類としては、ワードとして定義するパブリックな変数と、1ワード定義内でだけ有効な局所変数がある。 Mopsにはこれらに加えて Local Section (ローカルセクション)という機構があり、近接する特定範囲の複数のワード定義に渡ってのみ存続する変数を定義することができる。 このページでは長くなり過ぎないようForthコアに属するものだけを説明し、オプションである局所変数とMops特有のローカルセクションはそれぞれ別ページに回すこととする。 パブリック変数(VARIABLE, VALUE) VARIABLE VARIABLE V1 と宣言することで、V1という名前の変数が定義される。これは普通のワードと同じ有効範囲をもつ。つまり、宣言がロードされた直後から、同じ名前のワードが重複定義されるまで、この変数は同一性を保ち、その名で呼び出される。普通に大域変数(グローバル)といっても良いのだが、序章でも説明したように、プログラムの全域で有効という意味での普通の大域変数とは微妙に異なる。というか、むしろ、変数もまたまさにワードであるということなのだが。 VARIABLEの宣言(定義)は、実行状態でなされなければならず、コンパイル状態(コロン定義内)では所期の効果は得られない。 このタイプの変数がforthでは最も古典的なものである。 VARIABLEは、変数として利用され、そのように理解されてはいるものの、実体としては変数というよりアレイ(配列)である。長さが1セル長(forth標準規格:1セルはCPUの標準メモリー幅—普通、8ビットコンピュータなら1バイト、16ビットなら2バイト、32ビットなら4バイト、64ビットなら8バイト)で固定されている配列、と考えても大過ない。第一、プログラム上名前が値とは同一視されない。名前と同一視されるのは、その配列オブジェクトのポインタである。そして、値を取り出したり、値を格納したりするために、それぞれ固有のワードを呼び出さなければならない。 値の受け渡しは、もちろん、スタックを経由して行う。 VARIABLEの値は、定義の時点で0に初期化されることになっている。全ビット0である。 VARIABLEから値を取るワードは @ 、アットマークである。単なる特殊文字のように見えるかも知れないが、forthには特殊文字というものは無く、1文字の名前を持つワードは数多くある。これもその一つである。フェッチ (fetch) と読む。 例えば、上の変数V1の値を取るときは、 V1 @ とするわけである。まず変数名、そして動作、である。変数名をコードに書いたとき、つまり、VARIABLEを呼び出したときにも、実は固有の動作がある。それは、その変数のために割り当てられたデータ領域(メモリ)のアドレス値をデータスタック上に置く、ということである。だから、V1と書いた時点で、V1に割り当てられたデータ領域のメモリーアドレス値がデータスタックに押し込まれているのである。そこに、@を作用させると、@の動作は、スタック上のメモリーアドレス値を消費し、それが指している場所から1セル分の値を取ってスタックに押し込む、ということになっている。結果として、上のコードの結果、V1に格納されていた値だけがスタックに残されるのである。 したがって、VARIABLE V1と@のスタック効果を書くと: V1 ( -- addr ) @ ( addr -- x ) xが格納されていた値である。なお、V1に格納された値は、スタック上にコピーされるのであって、取り出されるとV1の中身がなくなってしまうのではない。念のため。 VARIABLE に値を格納するワードは ! 、びっくり(エクスクラメーション)マークである。ストア(store)と読む。値はもちろんスタック経由で取る。 例えば、- 7 を V1に格納(代入)するには、 -7 V1 ! と書く。日本語的には「-7 を V1 に代入」とそのまま読める。 ! のスタック効果は、 ! ( x addr -- ) である。x はスタック上にある値なら何でも良い(タイプは制限ない)し、addr は、1セル分のメモリースペースのアドレスであれば何でも良く、いつもどれかのVARIABLE名を書かなければならないわけではない。この辺の抽象性から、いかにもforth風の展開が可能になっていくのである。 VALUE VALUEもVARIABLEと同じように実行状態で宣言することで定義されるパブリック変数である。 データ領域の大きさも同じように1セルである。 ただし、宣言時から既に、VARIABLEとは挙動が異なる。というのは、VALUEの場合、宣言時にスタック上にある値を用いてデータ域が初期化されるのである。初期値とする値は、ロード中にプログラムが部分的に実行された結果の値でもよい。ともかく宣言時にスタックに値が一つ必要であり、それなしに宣言すると、スタックアンダーフローエラーが出てロードも中断されてしまう。 例えば、初期値-1のVALUE、VL1を定義するには、 -1 VALUE VL1 と書く。 VALUEでは、その名前と格納された値とが同一視できる。つまり、一般のプログラミング言語の変数のように見える。動作としては、名前を書くと、格納されている値のコピーを一つスタック上に置く。値取り出し用ワードは不要である。 スタック効果を書けば: VL1 ( -- x ) また、新しい値に替えるときは、プレフィクス (prefix) を使う。つまり、前置するワードである。こういうものはforthには珍しい。これがかなり後になって追加された機能であることを物語っている。標準のプレフィクスはTOである。値はスタックから取る。 例えば、VL1に37を代入したいときには、 37 TO VL1 と書く。 TOの同義語として、 - が定義されている場合も多い。マイナスに不等号をつなげた、まあ、要するに矢印である。このときは、 37 - VL1 と書ける。Mopsではむしろこちらが本体であって、直観的にも分かり易いので個人的にはこちらを愛用している。 Gazinta(ガジンタ?)という呼び名がある。初めは謎だったが、"goes into"のスランギッシュな言い方であろうと推測される。 TOないし- のスタック効果は、次のように表記する。 TO ( x “ spaces name” -- ) 引用マークに囲まれている入力項は、これはデータスタックから取るのではなく入力テキストから取るのだ、ということを意味する。何を取るかというとVALUEの名前である。つまり、プレフィクスTOにとっては次にあるVALUE名VL1は必要な入力項目なのである。実際、TOや- の後に何も書かないとエラーが出る。後方に書くのに入力としてコメントの左辺に寄せるのは、今ひとつ直観的ではないが、そういう表記法が規則であるようだ。 spaces というのは、はじめの空白文字はいくつあっても飛ばして、空白文字ではない文字列をVALUEの名前として解釈するという意味である。 実は、TOは使用法が拡張されていて、それが受け付けるのはVALUEだけではない。そのため、forth規格書では数値xが複数のこともあり得るような書き方になっている。TOが受け付けないワード名の場合は、エラーがでて中止になる。 ただし、上のTOのスタック効果は、解釈実行時のものである。他のワードの定義(後述)内において利用された場合は、効果が異なる。それを書いてみると、 TO コンパイル時 ( " spaces name" -- ) 実行時 ( x -- ) となる。つまり、ワード定義内でも、TOや- の直後にはValue変数の名前が必要であり、その定義内には「実行時にスタックの値を取って、その変数に格納する」という内容のコードがコンパイルされるのである。TOないし- だけをコンパイルしようとするとエラーになる。 配列(Array) 配列としてのVARIABLE 1バイト配列 VARIABLEは1バイト配列として利用することができる。全体長は1セル分である。ここでは現状一番多いであろう32ビットシステムで考える(2019年現在CPUは64ビットが圧倒的に多くなったが、Forthシステムは32ビットに止まっているものがまだ多いように思われる。)。この場合、1セルは4バイトであるから、1バイトなら4項目配列になる。 メモリーアドレスの個所で、1バイト数を出し入れするワードは、Cを付け足したフェッチ/ストアがある。つまり、c@、c!である。 c@ ( c-addr -- uc ) c! ( c c-addr -- ) 使用法は、 7 8 9 10 V1 3 + c! V1 2 + c! V1 1 + c! V1 c! V1 3 + c@ \ 10 V1 2 + c@ \ 9 V1 1 + c@ \ 8 V1 c@ \ 7 "x +"という形で普通に数値を加算することで配列の項目が進められるわけである。 頭に付け足されるcはcharacterの頭文字である。1文字1バイトとは限らない現在では、やや場違いな命名に見えなくもない。 1バイト数値をスタックにコピーするとき、より一般には1セルより小さいバイト数の数値をコピーするときには、正負の符号をどうするかという問題が出てくる。上のc@は全てを正の値として取り出す(0~255の値になる)。コメントのucのuはunsigned(符号なし)の意味である。正負を反映したい場合は、c@Xを用いる(-127~126の値になる)。最後のXはextend(拡張)を意味し、符号のビットを拡張するということである。 c@X ( c-addr -- c ) 2バイト配列 VARIABLEは、2バイト、2項目配列としても利用できる。その場合の操作ワードは、Wを付け足した、w@, w!, w@Xである。 頭のwは、wordの頭文字だったようだが、ダブルバイトと考えても良いと思う。 もっとも、これらはforth標準のワードではない。いくつかの環境では定義されており、Mopsでも利用できる。 w@ ( w-addr -- uw ) w! ( w w-addr -- ) w@X ( w-addr -- w ) Forthでは、配列も、原則として、項目番号ではなくてアドレスのバイト数値で位置を特定するので、2バイト配列を1項目進めるには、2を足さなければならない。例えば: -1 3 V1 w! V1 2 + w! V1 w@ \ 3 V1 2 + w@X \ -1 V1 2 + w@ \ 65535 (数値の理由が分らない人はコンピュータ内の数値表現法の基礎を勉強しましょう。) CREATE(クリエイト) ForthにはCREATEというワードがある。これを用いた宣言により、VARIABLEと同じように、利用可能なデータ域のメモリーアドレスと結びつく変数ワードを作りだすことができる。ただし、メモリー内に領域を確保しない。そこで、宣言の後に、必要分の領域を確保するためのワードを実行することで、任意の長さのメモリー領域、基本的にはバイト配列、を作りだすことができる。 ALLOT データ領域を確保する最も基本的なワードはALLOTで、確保すべきバイト長に当たる数値をスタックから入力として取る。 以上のような仕組みで、512バイト幅の配列theArrayを作るとすると、 CREATE theArray 512 ALLOT とすればよい。定義された配列theArrayはパブリックワードとなる。 値の出し入れは、項目を何バイトにするか、負の数を必要とするか、に応じて、上で説明したフェッチ/ストア用ワードを使えば良い。 項目位置の計算は、いつもベタのアドレスのバイト数値で計算する。 ALLOTはデータ領域は確保するが、内容の初期化はしない。新規定義の場合はメモリー領域は0で初期化されているのが普通だが、メモリーは前に何かで使ったゴミが残っていて不規則な値になっている場合もあり得る。 RESERVE 内容を消去、つまり、0に初期化して確保するワードとして、MopsにはRESERVEがある。RESERVEという語はデータ域を確保するという意味で通用してはいるようだが、forth規格にはこのワードは見当たらない。RESERVEが定義されている場合は、 CREATE theArray2 256 RESERVE とすれば、theArray2は、256バイト幅の配列として定義され、確実に0で初期化される。 CELLS さらに、1セル単位の配列を作るときは、CELLSというワードが便利である。これは、スタック上の値をセル幅倍する。 例えば、64セルある配列theArray3を定義するには、 CREATE theArray3 64 CELLS ALLOT とすればよい。見た目は理解し易いと思う。 2! , 2@ セルが2つ分のアレイ、あるいはダブルセル変数については、直接に値の出し入れができるワードがある。2@と2!である。これは、アイテム2つを一度にフェッチ/ストアする。 2@ ( addr -- x1 x2 ) 2! ( x1 x2 addr -- ) 格納の順番は、スタックの上の方がデータ域の前のセル、下の方が後のセルに入る。取り出しは、値をスタックから格納したときの順序を保つ。 これらは、ダブルセル数値(例えば、32ビット環境なら64ビット数まで)の取り扱いに適するもののように思われるが、なぜかforth標準では、COREワードとして規定されており、オプショナルではない(forth 94からそうであり、forth200xでも維持されている)。文字列などにも使えるからであろうか。 基本はこんなところと思われる。次は、格納された値を変化させるワードについて、ページを変えて説明しようと思う。 Forthの変数(続)、定数 Forth言語概説Forth開発環境の初歩的使用法
https://w.atwiki.jp/bousoku/pages/335.html
ACTとは、拡張ローマ字入力である。 AZIKとの違いは、DVORAK配列をベースにしている点である。 この配列を扱うタイパー 外部リンク ACT(AZIK on Dvorak