忍者ブログ

VB.NET-TIPS などプログラミングについて

VB.NETのTIPS(小技集)を中心に、Javascript、PHP その他のプログラミングについて少し役に立つ情報を発信します。いわゆる個人的な忘備録ですが、みなさんのお役に立てれば幸いです。

BHT-BASIC4.0:拡張関数のバイナリサーチ処理関数(BSEARCH.FN3)の使い方
マスタデータファイルを検索する場合によく使われるバイナリサーチがあります。 バイナリサーチとは日本語では二分検索と言われるもので、 プログラマの方ならば説明の必要もないぐらい一般的なものだと思います。

前提条件として検索対象となるコードを、昇順ソートしてファイルに登録しておきます。
バイナリサーチでは最初に先頭レコード(左側)と最終レコード(右側)の中央に位置するコードが、 検索コードより大きいかまたは、小さいかを比較します。
中央のコードが大きい場合は、中央の位置を次の処理の右側とします。 また、中央のコードが小さい場合は、中央の位置を次の処理の左側とします。 再度、左側と右側の中央の位置を求めて、中央のコードと検索コードの比較を行います。
これを繰り返すことで領域を狭めていき最終的に目的のコードが見つかるまで行います。 (結果的に見つからない場合もありますが)

これらの一連の処理を BSEARCH.FN3 は行っています。
■バイナリサーチ処理関数(BSEARCH.FN3)について
CALL "BSEARCH.FN3" .fcBSrcOp FILENO%, FIELDNO%, STRING$, RECORDNO

<引き数>
  .fcBSrcOp: オープンされているファイルのバイナリサーチ指定
  FILENO% : ファイル番号
  FIELDNO%: フィールド番号
  STRING$ : 検索文字列
  FILENUM%: 検索ファイル数

<戻り値>
  RECORDNO:検索結果(レコード番号)
  ・RECORDNO には、検索条件に一致するデータが見つかったレコード番号が返されます。
   見つからなかった場合、0 が返されます。
  ・RECORDNO は、整数型の最大値(32767)を超える場合あるので、
   変数に代入する場合、長整数型変数か実数型変数を推奨します

オープンされていないファイルのサーチもできるのですが、検索した結果、対象のレコードデータを 取得して何かの処理を行うのが普通だと思いますので、オープン済みファイルが対象のものだけを扱います。
それではこれを使った検索の関数をテストデータファイルを例に使ったものが以下に様になります。
'---------------------------------------
'データバイナリ検索
'---------------------------------------
'Function MfBSearchData%(Byval pstrCD$, Byref plngNum&, Byref plngRecNo&)
'引 数:
'	pstrCD$		:コード文字列
'	plngNum&	:数値を返す
'	plngRecNo&	:レコードNOを返す
'戻り値:
'	MfBSearchData%	:検索OK:GcTrue%, NG:GcFalse%
'---------------------------------------
	Function MfBSearchData%(Byval pstrCD$, Byref plngNum&, Byref plngRecNo&)
		'エラー処理宣言
		On Error Goto MfBSearchData.ErrProc
'		Const COL.CD% = 10	'[MfPutData%]で定義済み
'		Const COL.NUM% = 26
		'戻り値の初期化
		MfBSearchData% = GcFalse%
		plngRecNo& = 0

		PRIVATE FILENO%, FIELDNO%, STRING$, RECORDNO&, REC.CD$, REC.NUM$
		'TEST.DATファイルを、ファイル番号#1としてオープンします
		FILENO% = 1
		OPEN GcTEST.DAT$ AS FILENO% RECORD 2147483647
		FIELD #FILENO%, COL.CD% AS REC.CD$, COL.NUM% AS REC.NUM$
		'検索文字列を既定の桁数分スペースを付加
		REC.CD$  = LEFT$(pstrCD$ + GfSpc$(COL.CD%), COL.CD%)	'既定の桁数分スペースを付加
		FIELDNO% = 1
		'バイナリ検索
		CALL "BSEARCH.FN3" .fcBSrcOp FILENO%, FIELDNO%, REC.CD$, RECORDNO&
		If RECORDNO& > 0 Then
			'レコードNOが返された場合,レコードの取得
			GET FILENO%, RECORDNO&
			'数値文字列を返す
			plngNum& = VAL(REC.NUM$)
			plngRecNo& = RECORDNO&
			'正常を返す
			MfBSearchData% = GcTrue%
		Endif
MfBSearchData.Return	'関数戻り
		If FILENO% > 0 Then
			Close FILENO%
		Endif
		On Error Goto 0
		Exit Function
'-----
'エラー処理
'-----
MfBSearchData.ErrProc
		plngNum& = 0
		Resume MfBSearchData.Return
	End Function

■検索関数の利用
上記の検索関数の動作をテストするソースを以下に記します。
	SCREEN 1				'漢字モード
	LOCATE , , 2			'カーソルをブロック表示
	'最初は書き込み処理の連続
	W% = MfPutData%("CD0001", 100, 0)
	W% = MfPutData%("CD0002", 102, 0)
	W% = MfPutData%("CD0003", 1030, 0)
	W% = MfPutData%("CD0101", 201, 0)
	W% = MfPutData%("CD0201", 202, 0)
	W% = MfPutData%("CD1001", 500, 0)
	'バイナリサーチでの検索各種
	PRIVATE CD$, RNO&, NUM&
	CD$ = "CD0101"
	W% = MfBSearchData%(CD$, NUM&, RNO&)
	IF W% = GcTrue% THEN
		PRINT CD$ + "(" + STR$(RNO&) +  "):" + STR$(NUM&)
	ELSE
		PRINT CD$ + ":Not Found"
	ENDIF
	CD$ = "CD0003"
	W% = MfBSearchData%(CD$, NUM&, RNO&)
	IF W% = GcTrue% THEN
		PRINT CD$ + "(" + STR$(RNO&) +  "):" + STR$(NUM&)
	ELSE
		PRINT CD$ + ":Not Found"
	ENDIF
	CD$ = "CD0002" 
	W% = MfBSearchData%(CD$, NUM&, RNO&)
	IF W% = GcTrue% THEN
		PRINT CD$ + "(" + STR$(RNO&) +  "):" + STR$(NUM&)
	ELSE
		PRINT CD$ + ":Not Found"
	ENDIF
	'コードを発見できない検索
	CD$ = "CD0005" 
	W% = MfBSearchData%(CD$, NUM&, RNO&)
	IF W% = GcTrue% THEN
		PRINT CD$ + "(" + STR$(RNO&) +  "):" + STR$(NUM&)
	ELSE
		PRINT CD$ + ":Not Found"
	ENDIF
	WAIT 0, &h01 'キー入力待ち
	END

最初のテストデータの書き込みは、コードが重複せずに昇順に行っています。 このファイルはマスタデータとして考えていますので、コードの重複はあり得ません。

尚、ユーザ関数 MfPutData% は以下の記事にありますので、参照して下さい。
BHT-BASIC4.0:データファイルの取り扱いについてその2(書込み・読込みの実用的な関数)

バイナリサーチの3番目まではレコードの内容を読めていますが、 4番目のコード"CD0005"はテストデータ書込みで行っていないので レコードは検索できません。
このソースの実行結果は以下の図の様になります。



=====
2016/04/02:の時の情報











PR

コメント

コメントを書く