FreeBASIC マニュアルのトップに戻る

FreeBASIC 連想配列(Dictionary Class)

目次→フォーラム→FreeBASIC→補足Dictionary Class←オリジナル・サイト

連想配列(Dictionary Class) 左にメニュー・フレームが表示されていない場合は、ここをクリックして下さい

←リンク元に戻る プログラム開発関連に戻る

 このページは、アルゼンチンの paul doe さんが、掲示板に投稿された「Dictionary Class」を日本語化して紹介するものです。

注意1:これは、FreeBASIC 1.05 でしか使えません。1.07 だとコンパイル・エラーになります。
注意2:FreeBASIC 1.07 〜で使える連想配列に、連想配列(containers の hashtable) があります。

 プログラムは、下記でダウンロードできます。
https://github.com/glasyalabolas/fb-dictionary
追記:ファイルが消えているようなので、ここ fb-dictionary-master20180519.zip に置きます。

ライブラリとして提供されているコードを使う手順は簡単です。

- FreeBasic の32ビット64ビットに対応する 'libfb-dictionary-xx.a' と 'libfb-dictionary-xx-mt.a' を、FreeBasic 登録フォルダのライブラリ・フォルダにコピーします。
(Windowsユーザの場合、FreeBasic 32ビットでは通常 /lib/win32フォルダ、FreeBasic 64ビットの場合は /lib/win64フォルダです)
- 'fb-dictionary.bi' ヘッダファイルを FreeBasic 登録フォルダのインクルードフォルダ(通常は/incフォルダ)にコピーします。

 ディクショナリを使うには、コードのヘッダーを、コードの先頭に追加します。
#include once "fb-dictionary.bi"

"fb-dictionary-src.bi" は dictionary class のソースコードです。
ライブラリとして使わない場合は、このフォルダと 'fb-dictionary-src.bi' ファイルを FreeBasicプログラム登録の /inc フォルダにコピーして、コードに次のように追加してください:
#include once "fb-dictionary-src.bi"

"fb-dict-reference.bas" は、インクルード用の関数一覧です。
IDictionaryKey インターフェイスの参照実装を含みます。
プログラムで利用する場合は、作成するプログラムと同じフォルダに "fb-dict-reference.bas" を保存した上で、作成するプログラムに下記を記述します。
#Include Once "fb-dict-reference.bas"

英文字の出現頻度を数える

 次の使用例は、英文のテキスト・ファイル中の文字と出現頻度を調べるものです。

 探偵小説 シャーロック・ホームズシリーズの「踊る人形」 では、換字暗号が使われています。
 小説の中で、シャーロック・ホームズ は、英文中の文字の出現頻度から、「踊る人形」の暗号を解読します。

 「踊る人形」の解読方法
http://www.comm.tcu.ac.jp/~math/hnakai/infomath/sherlockholmes/dance_decording.html
 頻度分析 (暗号)
https://ja.wikipedia.org/wiki/%E9%A0%BB%E5%BA%A6%E5%88%86%E6%9E%90_(%E6%9A%97%E5%8F%B7)

TheAdventureOfTheDancingMen
 
'文字の出現頻度を数える

#Include Once "fb-dictionary-src.bi"
#Include Once "fb-dict-reference.bas"

#Include "window9.bi"

/'
	******************************************
	文字の出現頻度を数える
	******************************************
	このプログラムは paul doe さんの下記プログラムをベースに修正したものです。
	https://www.freebasic.net/forum/viewtopic.php?f=7&p=247578&sid=1e219112395b8599409f858ab0d58029#p247578
	Dictionary Class
	by paul doe ≫ May 20, 2018 16:05 
	
   '変数変更
   '   name = pName→	Character = pCharacter
   '   address = pAddress→削除
   '   age = pAge	CountsOfCharacter = pCountsOfCharacter
   '   RegisterDictionary →RecordDictionary
	
	このコードは、Dictionaryクラスの使用例を示しています。
	複合型('database register' のように Register と呼ぶ)を定義し、
	この型に特定の辞書を継承します。
	
	また、この辞書に新しい登録を追加するので、
	インスタンス削除時に、基本クラスのガベージコレクション機能を使って
	登録を破棄します。
'/

/'
  複合型を定義します。複合型の全メンバーを公開します。
  これは、アプリが処理する 'データ'を表します。
'/
Type Record
   Public:
      Declare Constructor()
      Declare Constructor( _
         ByRef As Const String, ByVal As Integer )
      
      Declare Destructor()
      
      As String      Character
      As Integer   CountsOfCharacter
End Type

Constructor Record()
  Character = "Unknown"
  CountsOfCharacter = 0
End Constructor

Constructor Record( _
   ByRef pCharacter As Const String, ByVal pCountsOfCharacter As Integer )

   Character = pCharacter
   CountsOfCharacter = pCountsOfCharacter
End Constructor

Destructor Record()

End Destructor

/'
  レコードを処理するための辞書実装
'/
Type RecordDictionary extends Dictionary
   Public:
      Declare Constructor()
      Declare Destructor()
      
      Declare Function Add( ByRef As Const String, ByVal As Record Ptr ) As boolean
      Declare Function item( ByRef As Const String ) As Record Ptr
      Declare Sub remove( ByRef As Const String )
      Declare Function keyExists( ByRef As Const String ) As boolean
End Type

Constructor RecordDictionary()
   base()
End Constructor

Destructor RecordDictionary()

End Destructor

Function RecordDictionary.add( _
   ByRef key As Const String, ByVal value As Record Ptr ) As boolean
   
   /'
     廃棄コールバックを指定しないことに注意してください。
     これは、* index *データではなく、* store *データにしたいだけです。
     これは実際にこのクラスの元の意図です
     (廃棄コールバックは、集中管理されたデータ管理メカニズムがない場合に備えられています)。
   '/
   Return( base.add( key, value ) )
End Function

Sub RecordDictionary.remove( ByRef key As Const String )
   base.remove( key )
End Sub

Function RecordDictionary.item( ByRef key As Const String ) As Record Ptr
   Return( Cast( Record Ptr, base.item( key ) ) )
End Function

Function RecordDictionary.keyExists( ByRef key As Const String ) As boolean
   Return( base.keyExists( key ) )
End Function

'' レコードの配列を文字の出現頻度順にソートする
Sub sortArray( ByVal startIndex As Integer, ByVal endIndex As Integer, array() As Record Ptr )
   Dim As Integer firstHalf, secondHalf
   Dim As Integer pivot
   
   firstHalf = startIndex
   secondHalf = endIndex
   
   '' Use the median value as pivot
   pivot = array( ( firstHalf + secondHalf ) \ 2 )->CountsOfCharacter

  Do
    '' Find < pivot
    Do While( array( firstHalf )->CountsOfCharacter < pivot )
      firstHalf = firstHalf + 1
    Loop
      
      '' Find > pivot
      Do While( array( secondHalf )->CountsOfCharacter > pivot )
         secondHalf = secondHalf - 1
      Loop
      
      '' Swap if needed
      If( firstHalf <= secondHalf ) Then
         Swap array( firstHalf ), array( secondHalf )
         
         firstHalf = firstHalf + 1
         secondHalf = secondHalf - 1
      End If
   Loop Until( firstHalf > secondHalf )
 
  '' Repeate sort until each half is sorted
   If( secondHalf > startIndex ) Then
      sortArray( startIndex, secondHalf, array() )
  End If
   
   If( firstHalf < endIndex ) Then
      sortArray( firstHalf, endIndex, array() )
   End If
End Sub

/'
	★★★★★★★★★★★★
	辞書の使用例
'/

'' 文字と頻度の辞書を作成する
   '★CharacterIndexを作成★

   ''キーでデータをインデックス化するための辞書を作成する
   Dim As RecordDictionary Ptr dic = New RecordDictionary()
   
   '' ソート用の配列を作る
   Dim As Record Ptr Records( 0 To 100 )

   Dim INPUTFILE As HANDLE 'ファイルのハンドル
   Dim FullPass As String  '対象ファイルのフルパス
   Dim Filehandle As Integer
   Dim StringVariable As String
   Dim CountsOfCharacter As Integer
   Dim CountsOfLine As Integer
   Dim TotalCountOfCharacters As Integer
   Dim CharacterSymbol As String
   Dim Position As Integer
   Dim Counter As Integer
   Dim NumberOfCharacterTypes As Integer
   Dim Number As Integer
   
 
Counter = -1

   '調べたい文字列を含むファイルを入力する
'********************* Window9 のファイルを開くダイアログを使う *********************

FullPass = OpenFileRequester("Specify English text file",ExePath,"Text File(*.txt;*.csv;*.htm*)"_
+Chr(0)+"*.txt;*.csv;*.htm*"+Chr(0)+"All File(*.*)"+Chr(0)+"*.*"+Chr(0))

If FullPass<>"" Then
  '' 入力のテキストのファイルを開きます。
   INPUTFILE = Read_File(FullPass)

   If INPUTFILE <> -1 Then
      'ファイルが開いたことを確認します
      Close_File(INPUTFILE)
   EndIf
   
   ' テキストファイルの内容読み込み処理
   CountsOfLine = 0
   TotalCountOfCharacters = 0

   Filehandle = FreeFile
   Open FullPass For Input As #Filehandle
   'テキストファイルのオープン

   If Lof(Filehandle) > 0 Then
      Do Until EOF(Filehandle )     

         Line Input #Filehandle, StringVariable
         '1行読み込み
         StringVariable = Trim(StringVariable)
         CountsOfLine = Len(StringVariable)
         TotalCountOfCharacters = TotalCountOfCharacters + CountsOfLine
         
         If CountsOfLine >0 Then
            ?
            ? "CountsOfLine = " , CountsOfLine
            ? "TotalCountOfCharacters = ", TotalCountOfCharacters
            ? "StringVariable = " , StringVariable
            ?
            For Position = 1 To CountsOfLine
               CharacterSymbol = Mid(StringVariable, Position, 1)
               If Trim(CharacterSymbol) <> "" Then
                  '' 'keyExists' メソッドを使ってキーが存在するかどうかを確認します。
                  
                  If( dic->keyExists(CharacterSymbol) = TRUE ) Then
                     '既存の場合は文字の出現頻度を追加します
                     Number = dic->item(CharacterSymbol)->CountsOfCharacter
                     Number = Number + 1
                     '? CharacterSymbol;
                     '? Number;
                     '? " ";
                     dic->item(CharacterSymbol)->CountsOfCharacter = Number
                  Else
                    ' この文字は辞書に登録されていません。辞書に追加します。
                    Counter =Counter +1
                     '? Counter ;
                     '? CharacterSymbol ;
                    dic->Add( CharacterSymbol, New Record( CharacterSymbol, 1 ) )
                    Records( Counter ) = New Record( CharacterSymbol, 1 )
                  End If
                  
               End If
            Next Position
         End If

      Loop     
   Else
           GoTo HandleErrors
   End If
   
   Close #Filehandle
   'ファイル番号を通したファイルを閉じます。
   
Else
   MessBox("メッセージ","ファイルが選択されませんでした!")
   End
EndIf
    
'配列の出現頻度を更新する
NumberOfCharacterTypes = Counter

For Counter = 0 To NumberOfCharacterTypes
   CharacterSymbol = records( Counter )->Character
   Number = dic->item(CharacterSymbol)->CountsOfCharacter
   Records( Counter ) = New Record(CharacterSymbol ,Number )
Next
?
? "入力を終了しました。何かキー入力で継続します。"
?
Sleep()

For Counter = 0 To NumberOfCharacterTypes
  ? records( Counter )->Character ;
  ? records( Counter )->CountsOfCharacter;
  ? " " ;
Next

?
? "ソート用の配列を更新しました。何かキー入力でソートを開始します。", NumberOfCharacterTypes ,UBound( records )
?
Sleep()

'' レコードの配列をソートする
sortArray( 0, NumberOfCharacterTypes, records() )

''文字の出現頻度順に出現数と文字を表示します
For Counter= 0 To NumberOfCharacterTypes
  ? records( Counter )->Character ;
  ? records( Counter )->CountsOfCharacter
Next

' 入力ファイルの総文字数
? "TotalCountOfCharacters = " & TotalCountOfCharacters
?
? "文字の出現頻度順に文字と出現数を表示しました。何かキー入力でプログラムを終了します。"
Sleep()

'' Cleanup
For Counter = 0 To UBound( Records )
  Delete( Records( Counter ) )
Next
   
Delete( dic )

End

HandleErrors:
Print "File not found"
Sleep

ページの頭に戻る
 
補足 に戻る
←リンク元に戻る プログラム開発関連に戻る
ページ歴史:2018-05-23
日本語翻訳:WATANABE Makoto、原文著作者:paul doe

ホームページのトップに戻る

Copyright (c) 2018 paul doe
このページの内容は、MITライセンスで公開します。
https://opensource.org/licenses/mit-license.php