昨日のコードちょっと修正

昨日書いたコード、ちょっと修正してみた。元の記事の「いかがだろう、ずいぶんとすっきり記述できる。」という所に少しでも近づきたいという気持ちがあって、いや、あのIfブロックはあれはあれで、ハッキリとした意思表示だと思うんだけど、もっとスパっとかけないもんかなーと。

で、Queueなら! と思いついて利用箇所をこう変えた。

        'こんな風に書けるよ!
        Dim hogeParam1 As String = "hogedefault1"   'set default
        Dim hogeParam2 As String = "hogedefault2"   'set default
        If opts.ContainsKey("hoge") Then
            With opts("hoge")
                hogeParam1 = If(.Count > 0, .Dequeue, "hogedefault1")
                hogeParam2 = If(.Count > 0, .Dequeue, "hogedefault2")
            End With
        End If

見た目はだいぶマシになった。先方のサンプルにあるportとrootを取る記述にしてみる。

        Dim myOptions() As String = {"root", "port"}
        Dim opts = (New OptionParser(myOptions, "-"c)).Parse(args)

        'こんな風に書けるよ!
        Dim root As String
        If opts.ContainsKey("root") Then
            root = If(opts("root").Count > 0, opts("root").Dequeue, ".")
        End If

        Dim port As Integer
        If opts.ContainsKey("port") Then
            If Not Integer.TryParse(If(opts("port").Count > 0, opts("port").Dequeue, "80"), port) Then
                port = 80
            End If
        End If

実際の利用場面を想定したコードだと、それほどのスッキリ感はないなあ。でも、If式でデフォルト値とセットで書けるのはだいぶコンパクトにまとめられるようになったと思う。横に長くなるけども。
あと整数値を取るときの型変換がダルイなー。一気にみてくれがわるくなった。でもまあ、こんなもんじゃないかな。過剰に美しさを求めると、他が犠牲になったりするし、ここでやめとこう。

そうそう、パーサクラスはこうなった。

Public Class OptionParser
    Private listOfOptionName As New List(Of String)
    Private swChar As Char
    Public Sub New(ByVal args() As String, ByVal swc As Char)
        swChar = swc
        listOfOptionName = args.Distinct().ToList()
    End Sub
    Public Function Parse(ByVal args() As String) As Dictionary(Of String, Queue(Of String))
        Parse = New Dictionary(Of String, Queue(Of String))
        For i = 0 To args.Length - 1
            Dim targetArg = args(i)
            If listOfOptionName.Exists(Function(n) String.Format("{0}{1}", swChar, n) = targetArg) Then
                Parse.Add(targetArg.Remove(0, 1), New Queue(Of String))
            Else
                If Parse.Count > 0 AndAlso targetArg(0) <> swChar Then
                    Parse.Last.Value.Enqueue(targetArg)
                Else
                    Debug.Print("{0} droped.index={1}", targetArg, i)
                End If
            End If
        Next
    End Function
End Class

DictionaryのValueがQueueになっただけ。ああ、あとSubstringをRemoveに変更。こんな便利関数があるなんて、今でもVB6現役のボクは知りませんでした!