2011/10/23

いまさらVisual Basic(手続き指向篇)

今でこそ『MacBook がほしい』と口癖のように言っている僕ですが、実はビルゲイツとマイクロソフトに魂を売り渡しており、少年期は Visual Basic とともに過ごしておりました。

そんな事はどうでもよいのですが、このたび情報処理技術者試験も終わって気持ちに少し余裕が生まれた事ですし、原点回帰という事で、ふたたび Visual Basic を動かしてみようと思い立ったわけです。

折しも、僕の周りに『将来的に仕事で使うかも知れない』という方がいらっしゃるようなので、その方の為にもなればと簡単に VB の雰囲気をまとめてみる事にしました。




【はじめに】

このエントリでは、C 言語のソースと対比させる形でオブジェクト指向以前の基本的な文法を紹介していこうと思います。具体的には、変数の宣言や基本的な制御構文、そして関数の作り方に加え、デリゲートλ式をつまみ食いします。

あまり突っ込むと、このエントリのページがトイレットペーパーのように長くなってしまいますので本当にさわりだけ。あと、あくまで構文のおさらいが目的ですから、サンプルは思いっきりコンソールアプリケーションですよ。がっかりだね

コンソールアプリケーションを選んでみる



【Hello, World】

まずはお約束の Hello World から。

【C】
// ========================================
//                  C 言語
// ========================================

#include <stdio.h>

int main() {
    printf("Hello World\n");
    return 0;
}

【VB】
'  ========================================
'                Visual Basic
'  ========================================

Module Module1
    Sub Main()
        Console.WriteLine("Hello, World")
    End Sub
End Module

実行結果



【変数の宣言・初期化】

変数宣言の構文です。C 言語と比較すると冗長な印象を受けます。

【C】
// ========================================
//                  C 言語
// ========================================

// int 型の変数 var を定義して、値 100 で初期化
int var = 100;

【VB】
'  ========================================
'                Visual Basic
'  ========================================

Dim var As Integer = 100
'   ~~~    ~~~~~~~   ~~~
' 変数名     型    初期値



【配列の宣言・初期化】

配列の宣言の構文は、先ほどの変数の宣言に準ずる形になりますが、初期化の構文は C 言語と VB でかなり似通った形になっています。

【C】
// ========================================
//                  C 言語
// ========================================

int array1[5];             // 配列の宣言
//  ~~~~~~~~~
//
//           +-+-+-+-+-+
//  array1 : | | | | | |
//           +-+-+-+-+-+


int array2[] = {1, 2, 3};   // 初期化
//  ~~~~~~~~~   ~~~~~~~
//              初期化リスト
//
//
//           +-+-+-+
//  array2 : |1|2|3|
//           +-+-+-+
//              ↑初期値が入る


【VB】
'  ========================================
'                Visual Basic
'  ========================================

Dim array1(5) As Integer    ' 配列の宣言
'   ~~~~~  ~
'
'           +-+-+-+-+-+
'  array1 : | | | | | |
'           +-+-+-+-+-+


Dim array2() As Integer = {1, 2, 3} ' 初期化
'   ~~~~~~                 ~~~~~~~
'                        初期化リスト
'
'
'           +-+-+-+
'  array2 : |1|2|3|
'           +-+-+-+
'              ↑初期値が入る

C 言語において配列の要素にアクセスする際には [ ] (角括弧)の添字演算子を用いますが、VB の場合は ( ) (丸括弧)でアクセスします。

ちなみに、多次元配列の宣言は、
'  ========================================
'                Visual Basic
'  ========================================

Dim multiArray(100, 200) As Integer
のように行います。 (i, j) 要素にアクセスするには multiArray(i, j) と書きます。

好みの問題ではありますが、C 言語の multiArray[i][j] よりも見た目が直感的に解りやすい気がします。



【If 文】

VB は、{ } (中括弧)ではなく、 IfEnd If 間でひとつの If ブロックを作ります。

【C】
// ========================================
//                  C 言語
// ========================================

#include <stdio.h>

int main() {
    
    int age = 99;

    if(age < 20) {
        printf("お前は未成年じゃー\n");
    } else {
        printf("成人おめでとう∩( ・ω・)∩\n");
    }

    return 0;
}

【VB】
'  ========================================
'                Visual Basic
'  ========================================

Module Module1

    Sub Main()

        Dim age As Integer = 99

        If (age < 20) Then
            Console.WriteLine("お前は未成年じゃー")
        Else
            Console.WriteLine("成人おめでとう∩( ・ω・)∩")
        End If

    End Sub

End Module

注意したいのは、条件判定式の比較演算子が C 言語とは大きく異なる点です。

たとえば、C 言語では変数 ab の等価性の判定を if(a == b) と書きますが、VB では If a = b Then になります(余談ですが、個人的に、これは VB における非常に悪しき仕様だと考えています。この仕様に慣れきった人が他言語でも VB の感覚のまま if 文の条件式を書いたらと考えると、軽く目眩がします)。

また、C 言語では if(a != b) と書く判定も、VB では If a <> b Then のように書く必要があります。

C 言語に慣れてしまっているならば、条件式や演算子に関しては、MSDN で確認した方がいいですね。



【Select Case 文(switch 文)】

より複雑な条件分岐は、Select Case 文で行います。これは、C 言語の switch 文とほぼ同じですが、予約語や構文が微妙に違うため注意が必要です。

【C】
// ========================================
//                  C 言語
// ========================================

#include <stdio.h>

int main() {
    
    int param = 1;

    switch (param) {
        case 0:
            printf("アタタタタタタ(以下略)\n");
            break;
        case 1:
            printf("お前はもう死んでいる\n");
            break;
        case 2:
            printf("ひでぶッ\n");
            break;
        default:
            printf("You は Shock!!\n");
            break;
    }

    return 0;
}

【VB】
'  ========================================
'                Visual Basic
'  ========================================

Module Module1

    Sub Main()

        Dim param As Integer = 1

        Select Case param
            Case 0
                Console.WriteLine("アタタタタタタ(以下略)")
            Case 1
                Console.WriteLine("お前はもう死んでいる")
            Case 2
                Console.WriteLine("ひでぶッ")
            Case Else
                Console.WriteLine("You は Shock!!")
        End Select

    End Sub

End Module




【For 文】

くりかえしの For 文です。こちらも C 言語と比較的似ており、For 変数 As 型 = 初期値 To 終了値 Step 増分 と機械的に変換できます。

【C99】
// ========================================
//                  C 言語
// ========================================

#include <stdio.h>

int main() {
    
    for(int i = 0; i <= 10; ++i) {
        printf("i = %d\n", i);
    }

    return 0;
}

【VB】
'  ========================================
'                Visual Basic
'  ========================================

Module Module1

    Sub main()

        For i As Integer = 0 To 10 Step 1
            Console.WriteLine("i = " & i)
        Next

    End Sub

End Module

C 言語には、for 文を制御するために continue 文や break 文がありました。VB にも同様に、Continue ステートメントや Exit ステートメントが存在します。



【サブプロシージャ・関数】

C 言語では一口に関数と呼んでいますが、VB では戻り値のあるものとないもので区別されます

戻り値を返すものを『関数』、返さないものを『サブプロシージャ』と呼び分けます。まずは簡単なサブプロシージャの方から紹介しましょう。

【C】
// ========================================
//                  C 言語
// ========================================

#include <stdio.h>

void test(int);

int main() {
    
    test(20);

    return 0;
}

// =====================================
// 引数の値を表示する関数
// -------------------------------------
// 引数: i  --   int
void test(int i) {
    printf("引数の値は %d です\n", i);
}

【VB】
'  ========================================
'                Visual Basic
'  ========================================

Module Module1

    Sub main()

        Test(20)    ' サブプロシージャ呼び出し

    End Sub

    ' =====================================
    ' 引数の値を表示するサブプロシージャ
    ' -------------------------------------
    ' 引数: i  --   Integer
    Sub Test(ByVal i As Integer)
        '    ~~~~~~~~~~~~~~~~~~
        '          引数

        Console.WriteLine("引数の値は" & i & "です")

    End Sub

End Module

次に、戻り値を返す関数の紹介です。

【C】
// ========================================
//                  C 言語
// ========================================

#include <stdio.h>

int test(int);

int main() {
    
    test(20);

    return 0;
}

// =====================================
// 引数の値を2乗して返す関数
// -------------------------------------
// 引数: i  --   int
int test(int i) {
    int ret = i * i;
    printf("引数の2乗は%dです\n", ret);
    return ret;
}

【VB】
'  ========================================
'                Visual Basic
'  ========================================

Module Module1

    Sub main()

        Test(20)    ' サブプロシージャ呼び出し

    End Sub

    ' =====================================
    ' 引数の値を2乗して返す関数
    ' -------------------------------------
    ' 引数: i  --   Integer
    Function Test(ByVal i As Integer) As Integer
        '         ~~~~~~~~~~~~~~~~~~     ~~~~~~~
        '               引数            戻り値の型

        Dim ret As Integer = i ^ 2
        Console.WriteLine("引数の2乗は" & ret & "です")
        Return ret
    End Function

End Module

サブプロシージャと関数の主な違いは、接頭辞が Sub から Function に変わっている事、関数は戻り値の型を明示的に記述しなければならない事、そして戻り値を Return している事などが挙げられます。



【参照渡し】

C 言語における関数への≪ポインタ渡し≫の有用性を示す最小限のサンプルとして、『引数に渡した 2 変数の中身を入れ替える swap 関数』は定番中の定番でしょう。

【C】
// ========================================
//
//                  C 言語
//
// ========================================
#include <stdio.h>

void swap(int *arg1, int *arg2);

int main() {
    int x = 10;
    int y = 20;
    
    printf("x = %d, y = %d\n", x, y);
    swap(&x, &y);
    printf("x = %d, y = %d\n", x, y);
    
    return 0;
}

// ===============================
// 変数の入れ替え
// ===============================
void swap(int *arg1, int *arg2) {
    int tmp = *arg1;
    *arg1   = *arg2;
    *arg2   = tmp;
}

幸か不幸か、VB では C 言語ほど素直にポインタを扱えません。ですが、サブプロシージャに変数を「参照渡し」すれば、上記と等価の処理を VB で実装する事ができます。

【VB】
'  ========================================
'
'                Visual Basic
'
'  ========================================
Module Module1
    Sub Main()
        Dim x As Integer = 10
        Dim y As Integer = 20

        Console.WriteLine("x = " & x & ", y = " & y)
        swap(x, y)
        Console.WriteLine("x = " & x & ", y = " & y)

    End Sub

    ' =====================================
    ' 変数の入れ替え
    ' =====================================
    Sub swap(ByRef arg1 As Integer, ByRef arg2 As Integer)
        '    ~~~~~                  ~~~~~
        '    ※ByRefを指定すると、参照渡しになる

        Dim tmp As Integer = arg1
        arg1 = arg2
        arg2 = tmp
    End Sub
End Module



【デリゲート(関数ポインタ)】

VB 紹介も佳境に入ってきました。デリゲートです。これは何かというと、C 言語の関数ポインタに近い機能です。

Windows API では、ウィンドウプロシージャを OS からコールバックするために関数ポインタが用いられていました(まぁ今もなんですけど)。これと同様の仕組みを VB でも実現するため(かどうかは分かりませんが)、デリゲートという仕組みが存在します。

【C】
// ========================================
//                  C 言語
// ========================================

#include <stdio.h>

void baka(int);
void aho(int);

int main() {
    
    void(* pFunc) (int);    // 関数ポインタの宣言

    pFunc = baka;
    pFunc(10);

    pFunc = aho;
    pFunc(2);
    
    return 0;
}

void baka(int arg) {
    for(int i = 0; i < arg; ++i) {
        printf("ばーか\n");
    }
}

void aho(int arg) {
    for(int i = 0; i < arg; ++i) {
        printf("アホー\n");
    }
}

【VB】
'  ========================================
'                Visual Basic
'  ========================================

Module Module1

    Delegate Sub Waruguchi(ByVal arg As Integer) ' デリゲート
    '                      ~~~~~~~~~~~~~~~~~~~~
    ' Integer型の引数を1つ取るサブプロシージャ

    Sub main()
        Dim pFunc As Waruguchi  ' デリゲートの宣言

        pFunc = New Waruguchi(AddressOf baka)
        pFunc(10)

        pFunc = New Waruguchi(AddressOf aho)
        pFunc(2)

    End Sub


    Sub baka(ByVal arg As Integer)
        For i As Integer = 0 To arg - 1 Step 1
            Console.WriteLine("ばーか")
        Next
    End Sub

    Sub aho(ByVal arg As Integer)
        For i As Integer = 0 To arg - 1 Step 1
            Console.WriteLine("アホー")
        Next
    End Sub

End Module
いずれも、pFunc 経由で関数(サブプロシージャ)を呼び出している事が確認できると思います。

デリゲートはイベント処理等で真価を発揮します。たとえば『ボタンが押されたときの挙動』などを独立に関数化し、それをボタンコンポーネントのデリゲートに登録する……などの使い方が典型例ですね。

また、少しオブジェクト指向の話になりますが、Observer パターン(のようなもの)も、デリゲートのおかげで実装が容易になると考えられます。



【おまけ: λ式】

ところで、先ほどのサンプルでは、bakaaho をサブプロシージャとして独立させています。

ですが、デリゲートに登録するような関数は、普通、プログラムの中で使い回される事はほとんどありません。

その場合、先ほどのサンプル以下のように書き換えてしまう事ができます(ただし2010以降)。

【VB 2010】
'  ========================================
'                Visual Basic
'  ========================================

Module Module1

    Delegate Sub Waruguchi(ByVal arg As Integer)

    Sub main()
        Dim pFunc As Waruguchi

        ' λ式(その1)
        pFunc = Sub(arg As Integer)
                    For i As Integer = 0 To arg - 1 Step 1
                        Console.WriteLine("ばーか")
                    Next
                End Sub
        pFunc(10)

        'λ式(その2)
        pFunc = Sub(arg As Integer)
                    For i As Integer = 0 To arg - 1 Step 1
                        Console.WriteLine("アホー")
                    Next
                End Sub
        pFunc(2)

    End Sub

End Module
この無名のサブプロシージャは(文形式の)λ式と呼ばれるものです。もともと関数型プログラミング言語(Lisp とか Haskell とか)で用いられていた概念のようですが、VB ではあまり深い意味はなく、ただの便利な構文という解釈で充分のようです(なんか MSDN を読んでもそれ以上の解釈が見当たりませんでした…)。

ちなみに、上記のサンプルは以下のようにもうちょっとだけ簡潔に書く事ができます。

【VB 2010】
'  ========================================
'                Visual Basic
'  ========================================

Module Module1

    Sub main()

        ' λ式(その1)
        Dim pFunc = Sub(arg As Integer)
                        For i As Integer = 0 To arg - 1 Step 1
                            Console.WriteLine("ばーか")
                        Next
                    End Sub
        pFunc(10)

        'λ式(その2)
        pFunc = Sub(arg As Integer)
                    For i As Integer = 0 To arg - 1 Step 1
                        Console.WriteLine("アホー")
                    Next
                End Sub
        pFunc(2)

    End Sub

End Module



ここまで駆け足でオブジェクト指向以前の VB を見て回りました。

習得が容易な初心者向け言語と言われていますが、後半はそれなりに高度なトピックも含んでおり、ちょっと目を離した隙にますますわけのわからん言語へと成長を遂げたものだと感心しました。

続篇(オブジェクト指向)は暇になったらまた書こうかなぁと思っています。今回のような構文の列挙より、デザインパターンの中からお気に入りをかいつまんで紹介できたらなぁ…と。

それでは今日はこの辺でー∩( ・ω・)∩

0 件のコメント:

コメントを投稿

ひとことどうぞφ(・ω・,,)