再帰関数の不具合

 投稿者:山中和義  投稿日:2009年12月 8日(火)11時04分8秒
  (外部、内部)再帰関数を定義した場合、以前の値がクリアされず今回に反映される。
LET t=fnV("1234",10)
PRINT t
LET t=fnV("567",10)
PRINT t

LET s$=fnS$(11,2)
PRINT s$
LET s$=fnS$(14,2)
PRINT s$

END

EXTERNAL FUNCTION fnV(s$,p)
LET L=LEN(s$)
IF L=0 THEN EXIT FUNCTION
LET fnV=fnV(s$(1:L-1),p)*p + VAL(s$(L:L))
END FUNCTION

EXTERNAL FUNCTION fnS$(n,p)
IF n=0 THEN EXIT FUNCTION
LET fnS$=fnS$(INT(n/p),p) & STR$(MOD(n,p))
END FUNCTION

実行結果
 1234
 1234567
1011
10111110
 

Re: 再帰関数の不具合

 投稿者:白石 和夫  投稿日:2009年12月 8日(火)16時52分24秒
  > No.778[元記事へ]

もっと単純な例です。
10 DECLARE EXTERNAL FUNCTION f
20 PRINT f(0)
30 PRINT f(1)
40 PRINT f(0)
50 END
60 EXTERNAL  FUNCTION f(x)
70 IF x=0 THEN EXIT FUNCTION
80 LET f=1
90 END FUNCTION
十進BASICを作り始めたころ(たぶん現在配布しているWindows95版まで)は関数定義の戻り値をスタック上に確保していましたが,それでは規格に合わないので,現在のバージョンは静的な変数を用いています。
規格では,「定義関数名に最後に代入された値」となっています。また,定義関数名は文法上,変数ではありません(だから局所変数でもない)。したがって,今回の呼び出しで値を設定しないと,前回設定した値を返すことになります。
 

Re: 再帰関数の不具合

 投稿者:白石 和夫  投稿日:2009年12月 8日(火)18時26分27秒
  > No.779[元記事へ]

最後に定義関数名に代入された値を関数値とするため,
10 DECLARE EXTERNAL FUNCTION f
20 PRINT f(2)
30 END
40 EXTERNAL FUNCTION f(x)
50 LET f=x
60 IF x=2 THEN LET y=f(x-1)
70 END FUNCTION
の実行結果は2でなく1になります。

これは,たとえば,再帰処理を行うとき,デフォルト値を定義関数名に与えておいて,再帰がうまくいかないときは定義関数名への代入をせずに関数から抜けるとその値が返ると想定すると,予期しない結果が得られることになります。
 

Re: 再帰関数の不具合

 投稿者:山中和義  投稿日:2009年12月 8日(火)20時11分24秒
  > No.780[元記事へ]

了解しました。 、、、と言う事は、BASICAccが(FullBASIC準拠なら)不具合となります。
 

Re: 再帰関数の不具合

 投稿者:白石 和夫  投稿日:2009年12月 8日(火)21時01分22秒
  > No.781[元記事へ]

BASICAccは,定義関数名への代入をDelphi語のresult変数への代入に変換しているので,厳密にはFull BASIC規格とは異なる動作をすることになります。
 

Re: 再帰関数の不具合

 投稿者:白石 和夫  投稿日:2009年12月 9日(水)17時39分53秒
  > No.782[元記事へ]

「最後に代入された値」にこだわる規格の意図が読めません。
定義関数名への代入の形で関数の結果を返す言語で関数の結果を広域的に保持することを要求するのは不可解な仕様で,「各回の呼び出しにおいて」という限定を書き損ねた,規格のバグである可能性も考えられるので,JIS合致にするためのオプションを新設し,通常の動作は関数の結果を局所変数に保持するように変更します。
 

Re: 再帰関数の不具合

 投稿者:SECOND  投稿日:2009年12月 9日(水)23時13分10秒
  > No.778[元記事へ]

!釈迦に説法で恐縮ですが、戻り値は、漏らさずセットが原則だと思います、すみません。

LET t=fnV("1234",10)
PRINT t
LET t=fnV("567",10)
PRINT t

LET s$=fnS$(11,2)
PRINT s$
LET s$=fnS$(14,2)
PRINT s$

END

EXTERNAL FUNCTION fnV(s$,p)
LET L=LEN(s$)
IF 0< L THEN LET fnV=fnV(s$(1:L-1),p)*p + VAL(s$(L:L)) ELSE LET fnV=0
END FUNCTION

EXTERNAL FUNCTION fnS$(n,p)
IF 0< n THEN LET fnS$=fnS$(INT(n/p),p) & STR$(MOD(n,p)) ELSE LET fnS$=""
END FUNCTION

!  1234
!  567
! 1011
! 1110
 

戻る