VBAユーザがPython・Rを使ってみた：条件分岐

# はじめに

機械学習の勉強を始めたVBAユーザです。
備忘録としてPython・Rの文法をVBAと比較しながらまとめていきたいと思います。

**目次**

- [条件分岐](#条件分岐)
  - [if文](#if文)
  - [case文の実現](#case文の実現)
- [まとめ](#まとめ)
  - [一覧](#一覧)
  - [プログラム全体](#プログラム全体)


# 条件分岐
今回は、条件分岐についてです。
[コメント](https://qiita.com/swathci/items/2dba6ebf683a11403f12#comment-92927d908fe6bfd8c226)をいただいて、[追記](#case文の実現)しました。

## if文
条件分岐（主に、if文）についてです。

**Python**

```Python:Python3
x = 0
if x == 0:
    print(0)
# 0

if x == 0:
    print(0)
else:
    print(1)
# 0

if x == 0:
    print(0)
elif x == 1:
    print(1)
elif x == 2:
    print(2)
else:
    print(3)
# 0

if x in (0,1,2):
    print(x)
else:
    print(3)
# 0
```
Pythonにはswitch文、case文はなさそうです。
なお、Pythonでは、If文のまとまり（ブロック）を（次のRのように`{``}`で囲んだり、VBAのように`If`ではじまって`End If`で閉じたり、というような方法ではなく）インデント（字下げ）で表現します（If文に限らず、他の制御構文（繰り返しのFor文）でも同様です。）。


**R**

```R:R
x <- 0
if (x == 0) {
  print(0)
}
# 0

if (x == 0) {
  print(0)
} else {
  print(1)
}
# 0

if (x == 0) {
  print(0)
} else if (x == 1) {
  print(1)
} else if (x == 2) {
  print(2)
} else {
  print(3)
}
# 0

if (x %in% c(0,1,2)) {
  print(x)
} else {
  print(3)
}
```
Rには、`switch`関数がありますが、使いづらそうです（参考：[R-Tips](http://cse.naro.affrc.go.jp/takezawa/r-tips/r/29.html#02)）。


**VBA**

```VBA:VBA
x = 0
If x = 0 Then
    Debug.Print 0
End If
' 0

If x = 0 Then
    Debug.Print 0
Else
    Debug.Print 1
End If
' 0

If x = 0 Then
    Debug.Print 0
ElseIf x = 1 Then
    Debug.Print 1
ElseIf x = 2 Then
    Debug.Print 2
Else
    Debug.Print 3
End If
' 0

Select Case x
    Case 0
        Debug.Print 0
    Case 1
        Debug.Print 1
    Case 2
        Debug.Print 2
    Case Else
        Debug.Print 3
End Select
' 0

Select Case x
    Case 0, 1, 2
        Debug.Print x
    Case Else
        Debug.Print 3
End Select
' 0

Select Case x
    Case 0 To 2
        Debug.Print x
    Case Else
        Debug.Print 3
End Select
'0

Select Case x
    Case Is <= 2
        Debug.Print x
    Case Else
        Debug.Print 3
End Select
' 0

Debug.Print Switch(x = 0, 0)
' 0
Debug.Print Switch(x = 0, 0, 1, 1)
' 0
Debug.Print Switch(x = 0, 0, x = 1, 1, x = 2, 2, True, 3)
' 0
Debug.Print Switch(x <= 2, x, True, 3)
' 0
```
VBAにはSelect Case文があります。Switch関数もあります。


## case文の実現
（[コメント](https://qiita.com/swathci/items/2dba6ebf683a11403f12#comment-92927d908fe6bfd8c226)をいただいて追記しました。）
Python、Rにはcase文がありませんが、標準機能を使って実現できます。

例として、日本語の曜日（aaa）を英語の曜日（ddd）に変換して出力することを考えます[^1]。

|aaa|aaaa|ddd|dddd|
|:-:|:-:|:-:|:-:|
|日|日曜日|Sun|Sunday
|月|月曜日|Mon|Monday
|火|火曜日|Tue|Tuesday
|水|水曜日|Wed|Wednesday
|木|木曜日|Thu|Thursday
|金|金曜日|Fri|Friday
|土|土曜日|Sat|Saturday

[^1]: "aaa","aaaa","ddd","dddd" はExcelの書式の表示形式の文字列です。

**VBA**
VBAでは、`Select Case`文や`Switch`関数を使って書くことができます。

```VBA:VBA
s = "金"

Select Case s
    Case "日": Debug.Print "Sun"
    Case "月": Debug.Print "Mon"
    Case "火": Debug.Print "Tue"
    Case "水": Debug.Print "Wed"
    Case "木": Debug.Print "Thu"
    Case "金": Debug.Print "Fri"
    Case "土": Debug.Print "Sat"
    Case Else: Debug.Print "not found"
End Select
' Fri

Debug.Print Switch(s = "日", "Sun", _
                   s = "月", "Mon", _
                   s = "火", "Tue", _
                   s = "水", "Wed", _
                   s = "木", "Thu", _
                   s = "金", "Fri", _
                   s = "土", "Sat", _
                   True, "not found")
' Fri
```

これと同じことを、Python、Rの標準機能で実現してみます。

**Python**
Pythonでは、[リスト（list）](https://docs.python.org/ja/3/library/stdtypes.html#lists)を使って次のように実現できます。

```Python:Python3
aaa = ['日', '月', '火', '水', '木', '金', '土']
ddd = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat']

s = '金'
t = '銀'
print(s in aaa)          # True
print(t in aaa)          # False
print(aaa.index(s))      # 5
# print(aaa.index(t))    # エラー
print(ddd[aaa.index(s)]) # Fri

def aaa_to_ddd(value):
    if value in aaa:
        return ddd[aaa.index(value)]
    else:
        return 'not found'
print(aaa_to_ddd(s))   # Fri
print(aaa_to_ddd(t))   # not found

def print_ddd(value):
    if value in aaa:
        print(ddd[aaa.index(value)])
    else:
        print('not found')
print_ddd(s)   # Fri
print_ddd(t)   # not found
```

Pythonでは、[辞書（dictionary）](https://docs.python.org/ja/3/library/stdtypes.html#dict)を使っても実現できます。こちらの方が簡単に書けます。

```Python:Python3
aaa = ['日', '月', '火', '水', '木', '金', '土']
ddd = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat']
aaa_ddd = dict(zip(aaa, ddd))   # 辞書
print(aaa_ddd)
# {'日': 'Sun', '月': 'Mon', '火': 'Tue', '水': 'Wed', '木': 'Thu', '金': 'Fri', '土': 'Sat'}

s = '金'
t = '銀'
print(aaa_ddd[s])     # Fri
# print(aaa_ddd[t])   # エラー
print(aaa_ddd.get(s)) # Fri
print(aaa_ddd.get(t)) # None

def aaa_to_ddd(value):
    if value in aaa:
        return aaa_ddd[value]
    else:
        return 'not found'
print(aaa_to_ddd(s))   # Fri
print(aaa_to_ddd(t))   # not found

def aaa_to_ddd(value):
    if value in aaa:
        return aaa_ddd.get(value)
    else:
        return 'not found'
print(aaa_to_ddd(s))   # Fri
print(aaa_to_ddd(t))   # not found

def aaa_to_ddd(value):
    return aaa_ddd.get(value, 'not found')
print(aaa_to_ddd(s))   # Fri
print(aaa_to_ddd(t))   # not found

def print_ddd(value):
    print(aaa_ddd.get(value, 'not found'))
print_ddd(s)   # Fri
print_ddd(t)   # not found
```

**R**
Rでは文字列ベクトルを使って実現できます。

```R:R
aaa = c("日", "月", "火", "水", "木", "金", "土")
ddd = c("Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat")

s <- "金"
t <- "銀"
s %in% aaa   # TRUE
t %in% aaa   # FALSE
aaa == s     # FALSE FALSE FALSE FALSE FALSE  TRUE FALSE
aaa == t     # FALSE FALSE FALSE FALSE FALSE FALSE FALSE
which(aaa == s)      # 6
which(aaa == t)      # integer(0)
aaa[which(aaa == s)] # "金"
aaa[which(aaa == t)] # character(0)
aaa[aaa == s]        # "金"
aaa[aaa == t]        # character(0)

aaa_to_ddd <- function(value) {
  if (value %in% aaa) {
    return(ddd[aaa == value])   # ddd[aaa == value][1]
  } else {
    return("not found")
  }
}
aaa_to_ddd(s)   # "Fri"
aaa_to_ddd(t)   # "not found"

print_ddd <- function(value) {
  if (value %in% aaa) {
    print(ddd[aaa == value])
  } else {
    print("not found")
  }
}
print_ddd(s)   # "Fri"
print_ddd(t)   # "not found"
```

**VBA**

```VBA:VBA
Function aaa_to_ddd_1(value As String) As String
' Case文を使って
Select Case value
    Case "日": aaa_to_ddd_1 = "Sun"
    Case "月": aaa_to_ddd_1 = "Mon"
    Case "火": aaa_to_ddd_1 = "Tue"
    Case "水": aaa_to_ddd_1 = "Wed"
    Case "木": aaa_to_ddd_1 = "Thu"
    Case "金": aaa_to_ddd_1 = "Fri"
    Case "土": aaa_to_ddd_1 = "Sat"
    Case Else: aaa_to_ddd_1 = "not found"
End Select
End Function

Function aaa_to_ddd_2(value As String) As String
' Switch関数を使って
Dim v As Variant
v = Switch(value = "日", "Sun", _
           value = "月", "Mon", _
           value = "火", "Tue", _
           value = "水", "Wed", _
           value = "木", "Thu", _
           value = "金", "Fri", _
           value = "土", "Sat")
If IsNull(v) Then
    v = "not found"
End If
aaa_to_ddd_2 = v
End Function

Function aaa_to_ddd_3(value As String) As String
' 配列を使って
Dim aaa As Variant
Dim ddd As Variant
Dim i As Integer
Dim s As String
aaa = Array("日", "月", "火", "水", "木", "金", "土")
ddd = Array("Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat")
s = "not found"
For i = LBound(aaa) To UBound(aaa)
    If value = aaa(i) Then
        s = ddd(i)
    End If
Next i
aaa_to_ddd_3 = s
End Function

Sub print_ddd(value As String)
' Case文を使って
Select Case value
    Case "日": Debug.Print "Sun"
    Case "月": Debug.Print "Mon"
    Case "火": Debug.Print "Tue"
    Case "水": Debug.Print "Wed"
    Case "木": Debug.Print "Thu"
    Case "金": Debug.Print "Fri"
    Case "土": Debug.Print "Sat"
    Case Else: Debug.Print "not found"
End Select
End Sub

Sub test_case()

Dim s As String
Dim t As String

s = "金"
Select Case s
    Case "日": Debug.Print "Sun"
    Case "月": Debug.Print "Mon"
    Case "火": Debug.Print "Tue"
    Case "水": Debug.Print "Wed"
    Case "木": Debug.Print "Thu"
    Case "金": Debug.Print "Fri"
    Case "土": Debug.Print "Sat"
    Case Else: Debug.Print "not found"
End Select
' Fri

Debug.Print Switch(s = "日", "Sun", _
                   s = "月", "Mon", _
                   s = "火", "Tue", _
                   s = "水", "Wed", _
                   s = "木", "Thu", _
                   s = "金", "Fri", _
                   s = "土", "Sat", _
                   True, "not found")
' Fri

s = "金"
t = "銀"

Debug.Print aaa_to_ddd_1(s)   ' Fri
Debug.Print aaa_to_ddd_2(s)   ' Fri
Debug.Print aaa_to_ddd_3(s)   ' Fri
Debug.Print aaa_to_ddd_1(t)   ' not found
Debug.Print aaa_to_ddd_2(t)   ' not found
Debug.Print aaa_to_ddd_3(t)   ' not found

Call print_ddd(s)   ' Fri
Call print_ddd(t)   ' not found

End Sub
```



# まとめ
### 一覧
各言語で使用するステートメント等を一覧にまとめます。比較のために、EXCELでの計算も示しました。

***条件分岐***

||Python|R|VBA|EXCEL|
|:-:|:--|:--|:--|:--|
|if文|`if P:`<br>　`...`<br>`elif Q:`<br>　`...`<br>`elif R:`<br>　`...`<br>`else:`<br>　`...`|`if (P) {`<br>　`...`<br>`} else if (Q) {`<br>　`...`<br>`} else if (R) {`<br>　`...`<br>`} else {`<br>　`...`<br>`}`|`If P Then`<br>　`...`<br>`ElseIf Q Then`<br>　`...`<br>`ElseIf R Then`<br>　`...`<br>`Else`<br>　`...`<br>`End If`|`=IF(P,...,`<br>`IF(Q,...,`<br>`IF(R,...,`<br>`...)))`|
|switch文|||`Select Case X`<br>`Case p`<br>　`...`<br>`Case q`<br>　`...`<br>`Case r`<br>　`...`<br>`Case Else`<br>　`...`<br>`End Select`|`=SWITCH(X,`<br>`p,...,`<br>`q,...,`<br>`r,...`,<br>`...)`|


### プログラム全体
参考までに使ったプログラムの全体を示します。

**Python**

```Python:Python3
# 条件分岐
x = 0
if x == 0:
    print(0)
# 0

if x == 0:
    print(0)
else:
    print(1)
# 0

if x == 0:
    print(0)
elif x == 1:
    print(1)
elif x == 2:
    print(2)
else:
    print(3)
# 0

if x in (0,1,2):
    print(x)
else:
    print(3)
# 0
```

**R**

```R:R
# 条件分岐（If）
x <- 0
if (x == 0) {
  print(0)
}
# 0

if (x == 0) {
  print(0)
} else {
  print(1)
}
# 0

if (x == 0) {
  print(0)
} else if (x == 1) {
  print(1)
} else if (x == 2) {
  print(2)
} else {
  print(3)
}
# 0

if (x %in% c(0,1,2)) {
  print(x)
} else {
  print(3)
}
# 0
```

**VBA**

```VBA:VBA
Sub test_conditional_branch()
Dim x As Integer

' 条件分岐
x = 0
If x = 0 Then
    Debug.Print 0
End If
' 0

If x = 0 Then
    Debug.Print 0
Else
    Debug.Print 1
End If
' 0

If x = 0 Then
    Debug.Print 0
ElseIf x = 1 Then
    Debug.Print 1
ElseIf x = 2 Then
    Debug.Print 2
Else
    Debug.Print 3
End If
' 0

Select Case x
    Case 0
        Debug.Print 0
    Case 1
        Debug.Print 1
    Case 2
        Debug.Print 2
    Case Else
        Debug.Print 3
End Select
' 0

Select Case x
    Case 0, 1, 2
        Debug.Print x
    Case Else
        Debug.Print 3
End Select
' 0

Select Case x
    Case 0 To 2
        Debug.Print x
    Case Else
        Debug.Print 3
End Select
'0

Select Case x
    Case Is <= 2
        Debug.Print x
    Case Else
        Debug.Print 3
End Select
' 0

Debug.Print Switch(x = 0, 0)
' 0
Debug.Print Switch(x = 0, 0, 1, 1)
' 0
Debug.Print Switch(x = 0, 0, x = 1, 1, x = 2, 2, True, 3)
' 0
Debug.Print Switch(x <= 2, x, True, 3)
' 0

End Sub
```


# 参考
- Python
   - [Python 標準ライブラリ](https://docs.python.org/ja/3/library/index.html)
      - [シーケンス型 --- list, tuple, range](https://docs.python.org/ja/3/library/stdtypes.html#sequence-types-list-tuple-range)
      - [リスト型 (list)](https://docs.python.org/ja/3/library/stdtypes.html#lists)
      - [マッピング型 --- dict](https://docs.python.org/ja/3/library/stdtypes.html#mapping-types-dict)
      - [dict](https://docs.python.org/ja/3/library/stdtypes.html#dict)
   - [Python チュートリアル](https://docs.python.org/ja/3/tutorial/index.html)
      - [5.1. リスト型についてもう少し](https://docs.python.org/ja/3/tutorial/datastructures.html#more-on-lists)
      - [5.5. 辞書型 (dictionary)](https://docs.python.org/ja/3/tutorial/datastructures.html#dictionaries)
- R
   - [RjpWiki](http://www.okadajp.org/RWiki/)
      - [ベクトルTips大全](http://www.okadajp.org/RWiki/?ベクトルTips大全)
   - [R-Tips](http://cse.naro.affrc.go.jp/takezawa/r-tips/r.html)
      - [29. 条件分岐](http://cse.naro.affrc.go.jp/takezawa/r-tips/r/29.html)
      - [13. ベクトル要素へのアクセス](http://cse.naro.affrc.go.jp/takezawa/r-tips/r/13.html)：`which`
      - [14. ベクトル計算](http://cse.naro.affrc.go.jp/takezawa/r-tips/r/14.html) `%in%`
- VBA
   - Office VBA リファレンス
      - [Select Case ステートメント](https://docs.microsoft.com/ja-jp/office/vba/language/reference/user-interface-help/select-case-statement)
      - [Select Case ステートメントの使用](https://docs.microsoft.com/ja-jp/office/vba/language/concepts/getting-started/using-select-case-statements)
      - [Switch 関数](https://docs.microsoft.com/ja-jp/office/vba/language/reference/user-interface-help/switch-function)