FC2ブログ

スポンサーサイト 


上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

--/--/--

Category: スポンサー広告

TB: --  /  CM: --

top △

再帰処理にはまる(その1) 


再帰処理(呼出し)は過去記事の中でも使ってました。

同じ処理するので、関数にまとめる・・・・ とか
ベタで書くと、
その関数自体が何をやっているのか分かりにくくなるので、関数で外出し・・・

それら関数の1つに、条件を変えて自分自身を呼出す・・・・再帰処理(呼出し)があると思います。

無理やり再帰処理にしてみたけど、
何をやっているか、わかり難くなった・・・ とか、
記述する処理が増えた・・・・ とか、

それならベタで記述した方が良いように思います。
特に、その関数が Function で戻り値を返すような時には・・・・
最近そういう記述を見る機会がありました。(サブフォームを辿る にベタでの記述を追加しました)
(私は我流でやってますが、まねたいと思えるものでは・・・・)
(かといって、私のが・・・・・)

ということは置いといて・・・・

「A」「B」の2文字を使って、6文字のパターンをすべて求めたい。
例えば、
 AAAAAA とか AAAAAB とか BAAABA とか・・・・・

再帰処理にはまる(その2) も読んでいただければと
再帰処理にはまる(その3) も読んでいただければと

ポイントは、いつ再帰呼び出しをやめるか・・・・ですね。
 

1)ベタで考える【標準モジュール:Pat1n】


1-1)For を必要分組み込む その1

単純に6つの For を組み込んで、
値が「0」なら「A」、「1」なら「B」に置き換えた文字を作りましょうというもの。
「A」を数値に変換して、「0」or「1」を足して文字に・・・・という方法になります。

Private Function ToMoji(ParamArray vAry() As Variant) As String
  Dim v As Variant
  
  ToMoji = ""
  For Each v In vAry
    ToMoji = ToMoji & Chr(Asc("A") + v)
  Next
End Function

Public Sub Sample1()
  Dim i1 As Long
  Dim i2 As Long
  Dim i3 As Long
  Dim i4 As Long
  Dim i5 As Long
  Dim i6 As Long

  For i1 = 0 To 1
    For i2 = 0 To 1
      For i3 = 0 To 1
        For i4 = 0 To 1
          For i5 = 0 To 1
            For i6 = 0 To 1
              Debug.Print ToMoji(i1, i2, i3, i4, i5, i6)
            Next
          Next
        Next
      Next
    Next
  Next
End Sub

 
で、次の その2 は、For の時の値を「A」「B」を数値にしたものにしましょう。
(考え方は、その1と大差ありません)

1-2)For を必要分組み込む その2

Private Function ToMoji2(ParamArray vAry() As Variant) As String
  Dim v As Variant
  
  ToMoji2 = ""
  For Each v In vAry
    ToMoji2 = ToMoji2 & Chr(v)
  Next
End Function

Public Sub Sample2()
  Dim i1 As Long
  Dim i2 As Long
  Dim i3 As Long
  Dim i4 As Long
  Dim i5 As Long
  Dim i6 As Long
  Dim IT As Long
  Dim IL As Long

  IT = Asc("A")
  IL = Asc("B")

  For i1 = IT To IL
    For i2 = IT To IL
      For i3 = IT To IL
        For i4 = IT To IL
          For i5 = IT To IL
            For i6 = IT To IL
              Debug.Print ToMoji2(i1, i2, i3, i4, i5, i6)
            Next
          Next
        Next
      Next
    Next
  Next
End Sub

 
「A」「B」の連続した文字を扱う時には、どちらでもよいような気がします。
その3 では、「A」「C」「E」の連続していないバラバラの文字を使いたい場合どう・・・・
For の値を「0」~「2」とし、「0」なら「A」、「1」なら「C」、「2」なら「E」・・・

1-3)For を必要分組み込む その3

Private Function ToMoji3(ParamArray vAry() As Variant) As String
  Dim v As Variant
  Dim sS As String
  
  ToMoji3 = ""
  For Each v In vAry
    sS = ""
    Select Case v
      Case 0: sS = "A"
      Case 1: sS = "C"
      Case 2: sS = "E"
    End Select
    ToMoji3 = ToMoji3 & sS
  Next
End Function

Public Sub Sample3()
  Dim i1 As Long
  Dim i2 As Long
  Dim i3 As Long
  Dim i4 As Long
  Dim i5 As Long
  Dim i6 As Long

  For i1 = 0 To 2
    For i2 = 0 To 2
      For i3 = 0 To 2
        For i4 = 0 To 2
          For i5 = 0 To 2
            For i6 = 0 To 2
              Debug.Print ToMoji3(i1, i2, i3, i4, i5, i6)
            Next
          Next
        Next
      Next
    Next
  Next
End Sub

 
ただ、上記の場合、6文字を8文字にしたい・・・・等、簡単に修正対応しにくいですね。


2)再帰処理で考える【標準モジュール:Pat1r】


2-1) その1

Private Sub ReToMoji(iNst As Long, sSrc As String)
  If (iNst <= 1) Then
    Debug.Print sSrc & "A"
    Debug.Print sSrc & "B"
  Else
    Call ReToMoji(iNst - 1, sSrc & "A")
    Call ReToMoji(iNst - 1, sSrc & "B")
  End If
End Sub

Public Sub Sample1()
  Call ReToMoji(6, "")
End Sub

 
ReToMoji 部分が再帰呼出しになります。
初期で 6文字作ってね。初期の文字は "" ですよ。
自分自身に文字数を1少なく、初期文字に自分の担当する文字を付加し、引数で渡します。
最後の1文字を担当する際、自分の担当する文字を付加して表示します。
この時、= 1 で判別はしません。(私は、そうしているという事で)
ここの判別がキモで、<= 1 とします。
これは一番最初に呼ばれる時、< 1 の値が指定された場合、処理を続けさせないためです。
-1 を指定した時でも「A」「B」は表示されますが、-1 を指定する方が悪いという事で・・・・

また、
Public Sub Sample1_1()
  Call ReToMoji(6, "パターン:")
End Sub
の様に初期文字列を設定しておくこともできます。
この時の結果は「パターン:AAAABA」の様になります。

6文字を8文字にしたい・・・・ 6 を 8 に変更して実行・・・・簡単ですね。

上記の場合、同じ処理(「A」と「B」)を書いていて、同じのは1つにまとめたい・・・・
という事で、その2

2-2) その2

Private Sub ReToMoji2(iNst As Long, sSrc As String)
  Dim sS As String
  Dim v As Variant

  For Each v In Array("A", "B")
    sS = sSrc & v
    If (iNst <= 1) Then
      Debug.Print sS
    Else
      Call ReToMoji2(iNst - 1, sS)
    End If
  Next
End Sub

Public Sub Sample2()
  Call ReToMoji2(6, "")
End Sub

 
For Each に「A」「B」を与えて・・・・
Array を使ってみましたが、その3 の様に Split でも同じです。

2-3) その3

Private Sub ReToMoji3(iNst As Long, sSrc As String)
  Dim sS As String
  Dim v As Variant

  For Each v In Split("A,B", ",")
    sS = sSrc & v
    If (iNst <= 1) Then
      Debug.Print sS
    Else
      Call ReToMoji3(iNst - 1, sS)
    End If
  Next
End Sub

Public Sub Sample3()
  Call ReToMoji3(6, "")
End Sub

 
これらの書き方になると、「A」「C」「E」で・・・となった場合、
Array("A", "B") を Array("A", "C", "E") とすれば良いですね。

2-4) その4

ここで、Dictionary を使ってみたいかな・・・・
キーには、何文字目(何桁目)の数値を使って、Item に「A」「B」の値を・・・・

Dim gDic As Object ' Sample4 で使用

Private Sub ReToMoji4(iNst As Long)
  Dim sS As String, vS As Variant
  Dim v As Variant

  For Each v In Array("A", "B")
    gDic.Item(iNst) = v
    If (iNst <= 1) Then
      sS = ""
      For Each vS In gDic.keys
        sS = sS & gDic.Item(vS)
      Next
      Debug.Print sS
    Else
      Call ReToMoji4(iNst - 1)
    End If
  Next
End Sub

Public Sub Sample4()
  Set gDic = CreateObject("Scripting.Dictionary")
  Call ReToMoji4(6)
  Set gDic = Nothing
End Sub

 
gDic.Item(iNst) = v により、キーがあったら Item が v になるし、なかったら追加されるし
For Each vS In gDic.keys で登録していった順で キーが得られていたので・・・
sS = sS & gDic.Item(vS) そのキーの値で文字列作成・・・・と

※ Web で Item 値を求める方法で、違う例があったのですが
For i = 0 To gDic.Count - 1
  sS = sS & gDic.Item(gDic.keys(i)) ← ???

私の環境でやると、
' 実行時エラー '451':
' Property Let プロシージャが定義されておらず、Property Get プロシージャからオブジェクトが返されませんでした。
となってしまいます。
以前と言うか・・・できていたのでしょうか・・・・・・・・・
仕様が変わった?・・・・・・参照の順?・・・・
気持ち的にはわかるような・・・・
 Split("A,B", ",")(0) で "A" が求まる。 この時の (0) の扱いなのかな・・・・

      For Each vS In gDic.keys
        sS = sS & gDic.Item(vS)
      Next

      For Each vS In gDic.items
        sS = sS & vS
      Next
でも良いようです

以下の その5 では、Dictionary 部分を再帰処理内に Static で確保しておきましょう。
最初に呼ばれたか・・・の引数を追加して、戻る時に Dictionary を Nothing

2-5) その5

Private Sub ReToMoji5(iNst As Long, bTop As Boolean)
  Static dic As Object
  Dim sS As String, vS As Variant
  Dim v As Variant

  If (dic Is Nothing) Then Set dic = CreateObject("Scripting.Dictionary")
  For Each v In Array("A", "B")
    dic.Item(iNst) = v
    If (iNst <= 1) Then
      sS = ""
      For Each vS In dic.keys
        sS = sS & dic.Item(vS)
      Next
      Debug.Print sS
    Else
      Call ReToMoji5(iNst - 1, False)
    End If
  Next
  If (bTop) Then Set dic = Nothing
End Sub

Public Sub Sample5()
  Call ReToMoji5(6, True)
End Sub

 

再帰処理の記述は難しくはないけど、何回呼ぶのか考えないと・・・・
スタック領域不足になったり、遅くなってくるし・・・・・


サンプルは以下
 バージョン 20002003 (2002)2007
 ファイル kEnt119_2000.zipkEnt119_2003.zipkEnt119_2007.zip
 サイズ 55,85856,31659,894
※ ファイルは zip 形式
※ 2007 以外は、2007 保存時に変換 & 各バージョンで動作確認 & 最適化

関連記事

2012/03/12

Category: やってみる

TB: 0  /  CM: 0

top △

この記事に対するコメント

top △

コメントの投稿

Secret

top △

トラックバック

トラックバックURL
→http://kikutips.blog13.fc2.com/tb.php/119-73d7a6ad
この記事にトラックバックする(FC2ブログユーザー)

top △


上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。