関数機能(再帰呼出し可能)と配列機能を実装しました。継続してインタープリタでの実装を行っています。PICへの実装が前提なので処理の軽量化を考慮しつつ次のような仕様にしました。
- 関数機能
関数は 「func 関数名(引数) { }」で宣言し、整数の戻り値を返す。関数内では "return" という名前の変数が自動的に生成され関数の戻り値を "return" 変数に設定する(初期値は0)
proc 処理 は戻り値が無いので代入文の右辺に入れることはできないが関数は戻り値があるので右辺で使用できる。
- 配列機能
GAME言語の '&' のようにソース末尾以降のメモリエリアを配列として使用する等、配列領域のアドレスをユーザ側で制御できるようにする。仕様としては次のとおり。
- 配列のアクセス方法
変数名に [] をつけて配列要素をアクセス可能。変数には事前に配列のアドレスを設定する必要がある。1要素のサイズは int サイズ(WindowsのVCでは4バイト、PIC24では2バイト)
例 ArrayA[ 10 ] = ArrayB[ 1 ];
- 組込み変数 Array_ の実装
GAME言語の '&' のようにソース末尾を示す変数。ソースを変更すると自動的に追従する(現時点ではソースエディタは未実装)。プログラム内で代入することで変更することも可能。
- 組込み関数 Array_() の実装
Array_(n) はArray_ [n]要素のアドレスを返す。配列として使用する変数に対しては配列要素をアクセスする前にアドレスを設定する必要がある。
例として Array_ 変数が示している配列領域の Array[10] 以降(配列のインデックスは0から始まる)の配列要素を ArrayA 変数で使用する場合は
var ArrayA; ArrayA = Array_( 10 );
のようにする。
関数、配列、ブロック内ローカル変数テストソース(独自言語)
# recursive call, array and local variable scope test
# by skyriver 2016/01/11
# recursive call test
func fact( n ) {
if ( n = 1 ) {
return = 1;
} else {
return = n * fact( n - 1 );
}
}
# array test
proc arraytest() {
var x,y;
PrnStr_( "\n\nArray_ = " );
PrnDec_( Array_ );
x = Array_( 0 );
y = x[ 1 ];
PrnStr_( "\ny = " );
PrnDec_( y );
PrnStr_( " (comment 1936880995 = 0x73727563 -> 'curs')" );
x[ 1 ] = 1234;
PrnStr_( "\nArray[1] = " );
PrnDec_( Array_[ 1 ] );
}
# local variable scope in block test
proc scope() {
var abc;
abc = "first";
if ( 1 ) {
var abc;
abc = "second";
PrnStr_( "\n\nabc = " );
PrnStr_( abc );
}
PrnStr_( "\nabc = " );
PrnStr_( abc );
}
proc main() {
var x;
x = 5;
PrnStr_( "\n" );
PrnDec_( x );
PrnStr_( "! = " );
PrnDec_( fact( x ) );
arraytest();
scope();
}
|
※本言語では代入文自身は値を持たないのでC言語と違い、イコールの論理演算子を '=' にしても代入文と識別可能 |
Array_ のアドレスをソース読込バッファに仮設定しているので 配列試験での y の値は上記試験ソースの 5~8文字目の値になります。テストソースの実行結果は次のとおりです。
テストソース実行結果
C:\src\vc\2010Express\tscl\tscl\Debug>tscl test.pid
5! = 120
Array_ = 13150888
y = 1936880995 (comment 1936880995 = 0x73727563 -> 'curs')
Array[1] = 1234
abc = second
abc = first
C:\src\vc\2010Express\tscl\tscl\Debug>
|
★2016/01/12 追記
上記試験ソースの階乗計算は最も代表的な再帰処理の一つですが、もう少し複雑な例として「フィボナッチ数列」のサンプルです。
フィボナッチ数列サンプルソース(独自言語)
# Fibonacci
func Fibo( n ) {
if ( n < 2 ) {
return = n;
} else {
return = Fibo( n - 1 ) + Fibo( n - 2 );
}
}
proc main() {
var n;
PrnStr_( "Fibonacci( 0..20 ) =\n" );
for ( n = 0; n <= 20; n = n + 1 ) {
PrnDec_( Fibo( n ) );
PrnStr_( " " );
}
}
|
実行結果
C:\src\vc\2010Express\tscl\tscl\Debug>tscl fibonacci.pid
Fibonacci( 0..20 ) =
0 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597 2584 4181 6765
C:\src\vc\2010Express\tscl\tscl\Debug>
|
★2016/01/15 追記
上のフィボナッチ数列を求めるプログラムを見ているとリカーシブコール数が鼠算式に多くなりスタックがどんどん使われていくさまが目に浮かびます。実際PC上で動かしても最後の方は目視で判るくらい表示前にウェイト時間が発生します。
フィボナッチ数列自体は直前の2つの値を足しているだけなのでリカーシブコールを使わなくても簡単に計算できます(下記)。 リカーシブ版より見た目は悪いですが速度はぜんぜん速いです。
漸化式から一般項を解く方法もあるみたいだけどね。
フィボナッチ数列サンプルソース高速版(独自言語)
# Fibonacci v2
func Fibo( n ) {
if ( n < 2 ) {
return = n;
} else {
var x1,x2;
x1 = 1;
x2 = 0;
do {
return = x1 + x2;
x2 = x1;
x1 = return;
n = n - 1;
} while ( n > 1 );
}
}
proc main() {
var n;
PrnStr_( "Fibonacci( 0..20 ) =\n" );
for ( n = 0; n <= 20; n = n + 1 ) {
PrnDec_( Fibo( n ) );
PrnStr_( " " );
}
}
|
★追記 2022/09/10
より単純化したものを追記します。
フィボナッチ数列サンプルソース高速版2(独自言語)
# Fibonacci v2a
func Fibo( n ) {
var next,tmp;
next = 1;
return = 0;
while ( n ) {
n = n - 1;
tmp = next;
next = next + return;
return = tmp;
}
}
proc main() {
var n;
PrnStr_( "Fibonacci( 0..20 ) =\n" );
for ( n = 0; n <= 20; n = n + 1 ) {
PrnDec_( Fibo( n ) );
PrnStr_( " " );
}
}
|
[ 前へ ] 連載記事
[ 次へ ]
2016-01-12 00:28
nice!(0)
コメント(0)
トラックバック(2)
共通テーマ:趣味・カルチャー
コメント 0