蟹の種類と数を数えよう

Excelで、こんなクイズがあったので挑戦した。

タラバガニ毛ガニカニカマ毛ガニ越前蟹タラバガニ花咲蟹越前蟹カニカマ越前蟹ズワイガニアフガニスタン越前蟹アフガニスタンカニカマ

  1. カニの種類は、何種類か?
  2. それぞれのカニはいくつあるか?

を求めてください。

f:id:Infoment:20200506220220j:plain

今回は、法則では求められない事実が二つあった。

  1. タラバガニおよび花咲蟹はヤドカリの仲間であって、蟹ではない。
  2. アフガニスタンは国名、カニカマは食品名であって、蟹ではない。

なお花咲蟹がヤドカリの仲間ということは、ご指摘をいただいて今回初めて知った(ご指摘、ありがとうございます)。

法則が無いものは、仕方がない。決め打ちで除外しよう。
また「〇〇ガニ」または「○○蟹」を探せばよいので、正規表現を用いてみた。

Sub 蟹を探せ()
    ' 元の文字列。
    Dim Source As String
        Source = "タラバガニ毛ガニカニカマ毛ガニ越前蟹タラバガニ花咲蟹越前蟹カニカマ越前蟹ズワイガニアフガニスタン越前蟹アフガニスタンカニカマ"
    
    ' 正規表現によるパターンマッチ。
    ' ※Microsoft VBScript Regular Expression5.5参照設定済み。
    Dim myReg As VBScript_RegExp_55.RegExp
    Set myReg = New VBScript_RegExp_55.RegExp
    
    ' パターン定義。
    ' ○○ガニ 若しくは ○○蟹(※〇〇は一文字以上の文字列)というパターン。
        myReg.Pattern = "(.+?)(ガニ|蟹)"
    
    ' 文字列全体を検索する(該当するパターンが見つからなくなるまで探す)。
        myReg.Global = True
    
    ' パターンに該当した文字列を格納するためのコレクション。
    Dim MC As VBScript_RegExp_55.MatchCollection
    
    ' パターンに該当したカニを、種類ごとに数えるための辞書。
    ' ※Microsoft Scripting Runtime参照設定済み。
    Dim Dict As Scripting.Dictionary
    
    ' 辞書の初期化。
    Set Dict = New Scripting.Dictionary
    
    ' 除外用配列。
    Dim ExclusionArray(1) As Variant
    
    ' 蟹によく似た生き物だが、実はヤドカリの仲間を除外するための配列。
        ExclusionArray(0) = Array("タラバガニ", "花咲蟹")
        
    ' 「カニ」「ガニ」という文字を含んでいるが、生き物の名前ではない
    ' 言葉を除外するための配列。
        ExclusionArray(1) = Array("アフガニスタン", "カニカマ")
        
    ' ループ変数。
    Dim arr As Variant
    Dim i As Long
    
    ' 蟹の名前以外を、vbNullStringに置き換える。
        For i = 0 To 1
            For Each arr In ExclusionArray(i)
                Source = Replace(Source, arr, vbNullString)
            Next
        Next
    
    ' パターンにマッチするものがあるか否かを確認(Test)。
    ' 存在する場合、Trueを返す。
        If myReg.Test(Source) = True Then
        
        ' パターンにマッチした文字列を、MC(MatchCollection)にセット。
            Set MC = myReg.Execute(Source)
            
            ' MC(0), MC(1), ・・・, MC(MC.count-1) をループ。
                For i = 0 To MC.Count - 1
                
                ' マッチした文字列を、変数の型をStringとして宣言した
                ' tempに一旦格納する。
                ' ※そのまま辞書に格納すると、同じ蟹でも別物として格納
                '  されたため、型が文字列の変数に一旦納めている。
                '  なぜそのようになるか、現時点で原因不明。
                    Dim temp As String
                        temp = MC(i)
                
                ' 辞書に格納済みか(=存在するか)確認。
                ' 存在するならば、蟹の名前(key)に対する匹数(item)を
                ' カウントアップする。
                ' ※引数ではなく匹数。
                    If Dict.Exists(temp) Then
                        Dict(temp) = Dict(temp) + 1
                
                ' 蟹の名前が辞書に存在しない場合、1匹目として登録。
                    Else
                        Dict(temp) = 1
                    End If
                Next
            
        ' 辞書に格納された蟹の種類ごとに、その匹数と共にイミディエイトに表示。
            For i = 0 To UBound(Dict.Keys)
                Debug.Print Dict.Keys(i) & ":" & Dict.Items(i) & " 匹"
            Next
        
        ' 辞書のkey情報(Dict.keys)は配列であるため、UBoundで蟹の種類数を取得。
        ' ※0始まりの配列なため、今回は+1している。
            Debug.Print "※蟹は全部で " & UBound(Dict.Keys) + 1 & " 種類。"
            
        ' 除外理由を注釈として表示。
            Debug.Print "※タラバガニおよび花咲蟹はヤドカリの仲間であって、蟹ではない。"
            Debug.Print "※アフガニスタンは国名、カニカマは食品名であって、蟹ではない。"
        End If
End Sub

実行した結果が ↓ こちら。
f:id:Infoment:20200506221053p:plain

実際に数えた結果と一致するので、どうやら問題なさそうだ。

ところで20年程前になりますが、石川県の民宿で食べたカニのハサミの刺身は、とてもとても美味しかったです。

参考まで。