ズンドコ節で遊ぶ

ズンドコ節で遊ぶ

ノンプロ研のSlackのチャンネルに「今週のお題」というやつがある。毎週一つ、ちょっとした「お題」(〈数字をローマ数字に変換する関数を作れ〉とかそういうやつ)がランダムに出される。

今回、

「ズン」と「ドコ」をランダムで出力し、「ズンズンズンズンドコ」という並びが完成したら「キ・ヨ・シ!」と出力するプログラムを書け

という「お題」が出された。

GASやPythonで回答している人はいたものの、VBAで回答した人はいなかったので、ちょっとやってみた。

「ズン」と「ドコ」をランダムで出力する

これは、単純にRnd関数を使えばよいと考えた。

Rnd関数は「0~1未満の数」をランダムに生成するらしいので、

Int(2 * Rnd)

01かをランダムに取得することができる。

リスト1
Private Function genLyric() As String
  Dim ret As String
  ret = "ズン"
  Randomize
  Dim tmp As Long
  tmp = Int(2 * Rnd)
  If tmp = 1 Then ret = "ドコ"
  genLyric = ret
End Function

このgenLyricメソッドを呼べば、「ズン」か「ドコ」か、いづれかの文字列を取得することができる。

ズンドコ節を歌うクラスを作る

リスト1を用いれば、「ズン」か「ドコ」かのどちらかを歌わせることができる。

後は、歌い続ける中で「ズンズンズンズンドコ」の並びが完成したら「キ・ヨ・シ!」と歌わせるようにすればよい。

これは、要素数5のQueueではないか!

ならば、クラスの内部に要素数5のQueueを持たせて、歌うたびにそれをチェックすればよいではないか!

スト2 クラスモジュール Zundoko
'### オブジェクト名は Zundoko ###'
Option Explicit

'Field Variables'    '……(1)'
Private zundoko1 As String
Private zundoko2 As String
Private zundoko3 As String
Private zundoko4 As String
Private zundoko5 As String

Private hasDone_ As Boolean

'Properties'    '……(2)'
Public Property Get HasDone() As Boolean
  HasDone = hasDone_
End Property

Public Property Get Lyrics() As String
  Dim ret As String
  ret = zundoko1
  ret = ret & zundoko2
  ret = ret & zundoko3
  ret = ret & zundoko4
  ret = ret & zundoko5
  Lyrics = ret
End Property

'Constructor'
Private Sub Class_Initialize()    '……(3)'
  hasDone_ = False
  Dim n As Long
  n = 0
  Do Until n = 5
    Call Me.sing
    n = n + 1
  Loop
End Sub

'Methods'
Public Sub sing()    '……(4)'
  Dim tmp As String    '……(5)'
  tmp = genLyric
  zundoko1 = zundoko2    '……(6)'
  zundoko2 = zundoko3
  zundoko3 = zundoko4
  zundoko4 = zundoko5
  zundoko5 = tmp
  Debug.Print tmp    '……(7)'
  If Me.Lyrics = "ズンズンズンズンドコ" Then  '……(8)'
    hasDone_ = True
    Debug.Print "キ・ヨ・シ!"
  End If
End Sub

Public Sub resetZundoko()    '……(9)'
  hasDone_ = False
End Sub

'Internal Methods'
'リリック(笑)生成用メソッド'
Private Function genLyric() As String
  Dim ret As String
  ret = "ズン"
  Randomize
  Dim tmp As Long
  tmp = Int(2 * Rnd)
  If tmp = 1 Then ret = "ドコ"
  genLyric = ret
End Function

相変わらずのタテ長。

(1)の

Private zundoko1 As String
Private zundoko2 As String
Private zundoko3 As String
Private zundoko4 As String
Private zundoko5 As String

Private hasDone_ As Boolean

はフィールド変数。

上の五つは直近五つのリリック(笑)を蓄えておく場所。

最後の一つ(hasDone_)は、ズンドコ節が完成したかどうかを表すフラグ。「ズン、ズン、ズン、ズンドコ キ・ヨ・シ!」が完成したらTrueになる。

(2)の

Public Property Get HasDone() As Boolean
  HasDone = hasDone_
End Property

Public Property Get Lyrics() As String
  Dim ret As String
  ret = zundoko1
  ret = ret & zundoko2
  ret = ret & zundoko3
  ret = ret & zundoko4
  ret = ret & zundoko5
  Lyrics = ret
End Property

はプロパティ。プロパティは二つ。

HasDone

はズンドコ節が完成したかどうかをこのクラスの利用者に知らせるもの。

もう一つのLyricsプロパティは、直近五つのリリック(笑)をつなぎ合わせた文字列を返す。

(3)の

Private Sub Class_Initialize()
  hasDone_ = False
  Dim n As Long
  n = 0
  Do Until n = 5
    Call Me.sing
    n = n + 1
  Loop
End Sub

はコンストラクタ。

hasDone_を明示的にFalseにする他、この段階で五つのリリック(笑)を満たしておく。

リリック(笑)を取得するためのsingメソッドについては後述。

(4)の

Public Sub sing()
  Dim tmp As String    '……(5)'
  tmp = genLyric
  zundoko1 = zundoko2    '……(6)'
  zundoko2 = zundoko3
  zundoko3 = zundoko4
  zundoko4 = zundoko5
  zundoko5 = tmp
  Debug.Print tmp    '……(7)'
  If Me.Lyrics = "ズンズンズンズンドコ" Then  '……(8)'
    hasDone_ = True
    Debug.Print "キ・ヨ・シ!"
  End If
End Sub

がリリック(笑)を取得して歌うsingメソッド。

まず(5)の

Dim tmp As String
tmp = genLyric

で変数tmpを用意。

リスト1のgenLyricで「ズン」か「ドコ」かを取得して変数tmpに突っ込む。

次に(6)の

zundoko1 = zundoko2
zundoko2 = zundoko3
zundoko3 = zundoko4
zundoko4 = zundoko5
zundoko5 = tmp

で、変数zundoko2~同5に入っている文字列をそれぞれzundoko1~同4にずらして入れる。

最後にzundoko5に先ほど取得したtmpを突っ込む。

これで、あたかも要素数五つのQueueに新たに一つの要素を追加(して先頭の要素を消去)したかのような動作になる。

ここで(7)の

Debug.Print tmp

で新たに取得したリリック(笑)をイミディエイトに出力。

そうして今度は(8)の

If Me.Lyrics = "ズンズンズンズンドコ" Then
  hasDone_ = True
  Debug.Print "キ・ヨ・シ!"
End If

で直近五つのリリック(笑)を調べる。

直近五つのリリック(笑)、すなわちLyricsプロパティの値がズンズンズンズンドコになっていたら条件成立なので、

hasDone_ = True
Debug.Print "キ・ヨ・シ!"

を実行。

hasDone_Trueにし、イミディエイトに「キ・ヨ・シ!」を出力する。

「ズン」とか「ドコ」は1行づつイミディエイトに表示されるので、条件が成立した場合は、イミディエイトに

ズン
ズン
ズン
ズン
ドコ
キ・ヨ・シ!

と表示されることになる。

最後の「ズン」と「ドコ」はⅠ行で表示した方が雰囲気は出るんですけどね……。

最後、(9)の

Public Sub resetZundoko()    '……(9)'
  hasDone_ = False
End Sub

は、HasDoneプロパティをFalseにするためのもの。ただそれだけ。

使ってみる

次のコードで実験。

リスト3
Private Sub testZundoko()
  Dim zd As Zundoko
  Set zd = New Zundoko
  Dim n As Long
  n = 1
  Do Until zd.HasDone
    Call zd.sing
    n = n + 1
    If n > 100 Then
      Debug.Print "タイムオーバー! 残念!"
      Exit Sub
    End If
    DoEvents
  Loop
  Debug.Print n & " 回で達成!"
End Sub

見ての通り、条件が成立するまでsingメソッドを実行し続ける。

最後に何回目で「ズンズンズンズンドコキ・ヨ・シ!」が完成したかを表示するようにした。なお、「ズンズンズンズンドコ」が完成しない限り、延々singメソッドを実行し続けるので、100回で一旦やめるようにした。

実行すると、

f:id:akashi_keirin:20200103150930g:plain

こんな感じ。

おわりに

頭の体操です。

もっとスマートなやり方がありそうですね。