ExcelでIF関数を使うと条件によっていろいろと処理を変えることができる。
複雑な条件はIF関数を組み合わせることで実現できるが、やりすぎるとすごく見づらい。
たとえば、以下のような式を作ってみた。
=IF(条件,IF(条件,IF(条件,TRUE,FALSE),IF(条件,IF(条件,FALSE,TRUE),FALSE)),FALSE)
ネストしすぎて解析が困難になっている。
これを解析用にインデントしてみると、このような構造になっていることがわかる。
VBAと違ってElse文はないけれど、このようにインデントすれば真と偽の対応がわかりやすい。
【注意】解析用のインデントなので、最終的にはタブと改行を削除して元に戻さないと式として使えない。
今回はこの改行とインデントを自動で行うマクロを作ってみた。
技術的にはコンパイラの字句解析を応用したものだ。
これは説明するとすごく長くなるので割愛し、コードだけ貼り付けておく。
※末尾に参考文献を貼り付けておくので興味がある方はどうぞお買い求めください。
コード
準備するモジュールは4つある。
標準モジュール1つ
- Module1
クラスモジュール3つ
- Expression
- Token
- Stack
Module1のコード
プログラムのエントリーポイント(開始場所)はMainプロシージャである。
PublicEnum TokenType Target 'If BeginParen '( EndParen ') Comma ', Other EndEnumPublicEnum CharType Alphabet Number BeginParen EndParen Comma DoubleQuote Other EndEnumFunction GetCharType(c)As CharType Dim ret As CharType SelectCaseAsc(c)CaseAsc("a")ToAsc("z"),Asc("A")ToAsc("Z") ret = CharType.Alphabet CaseAsc("0")ToAsc("9") ret = CharType.Number CaseElseSelectCaseTrueCase c ="(": ret = CharType.BeginParen Case c =")": ret = CharType.EndParen Case c =",": ret = CharType.Comma Case c ="""": ret = CharType.DoubleQuote CaseElse ret = CharType.Other EndSelectEndSelect GetCharType = ret EndFunctionFunction IsIn(target_,ParamArray check())AsBooleanDim i AsLong, ret AsBoolean: ret =FalseFor i =LBound(check)ToUBound(check) ret = ret Or check(i)= target_ Next IsIn = ret EndFunctionSub Main()Dim targetExpression As Expression: Set targetExpression =New Expression targetExpression.ExpressionString =InputBox("数式を入力してください。")Dim tokens As Collection Set tokens = GetTokens(targetExpression)Dim t As Token Dim st As Stack: Set st =New Stack Dim tabCount AsLongFor i =1To tokens.Count Debug.Print tokens(i).tString; SelectCase tokens(i).tType Case TokenType.BeginParen If tokens(i -1).tType = TokenType.Target Then st.Push True tabCount = tabCount +1Else st.Push FalseEndIfCase TokenType.EndParen If st.Pop Then tabCount = tabCount -1Case TokenType.Comma If st.Top Then Debug.Print Debug.PrintString(tabCount,vbTab); EndIfCaseElseEndSelectNextEndSubFunction GetTokens(targetExpression As Expression)As Collection Dim ret As Collection: Set ret =New Collection Dim t As Token DoWhile targetExpression.hasNext Set t =New Token t.tString = targetExpression.getNext SelectCase GetCharType(t.tString)Case CharType.Alphabet DoWhile IsIn(GetCharType(targetExpression.checkNext), CharType.Alphabet, CharType.Number) t.AddChar targetExpression.getNext LoopCase CharType.Number DoWhile GetCharType(targetExpression.checkNext)= CharType.Number t.AddChar targetExpression.getNext LoopCase CharType.DoubleQuote DoWhile GetCharType(targetExpression.checkNext)<> CharType.DoubleQuote t.AddChar targetExpression.getNext Loop t.AddChar targetExpression.getNext Case CharType.BeginParen Case CharType.EndParen Case CharType.Comma Case CharType.Other EndSelect ret.Add t LoopSet GetTokens = ret EndFunction
Expressionクラスのコード
Public ExpressionString Private cursor PrivateSub Class_Initialize() cursor =1EndSubFunction hasNext()AsBoolean hasNext =Len(ExpressionString)> cursor -1EndFunctionFunction getNext()AsString getNext =Mid(ExpressionString, cursor,1) cursor = cursor +1EndFunctionFunction checkNext()AsStringIf hasNext Then checkNext =Mid(ExpressionString, cursor,1)ElseMsgBox"error"EndIfEndFunctionSubReset() cursor =1EndSub
Tokenクラスのコード
Public tString Sub AddChar(c) tString = tString & c EndSubPropertyGet tType()As TokenType Dim ret As TokenType SelectCaseUCase(tString)Case"IF": ret = TokenType.Target Case"(": ret = TokenType.BeginParen Case")": ret = TokenType.EndParen Case",": ret = TokenType.Comma CaseElse: ret = TokenType.Other EndSelect tType = ret EndProperty
Stackクラスのコード
Private Items()AsVariantPropertyGetCount()AsIntegerCount=UBound(Items)EndPropertyPropertyGet Top()AsVariant Top = Items(UBound(Items))EndPropertyPublicFunction Pop()AsVariantIfUBound(Items)>0Then Pop = Items(UBound(Items))ReDimPreserve Items(UBound(Items)-1)Else Pop =EmptyEndIfEndFunctionPublicSub Push(ByRef x AsVariant)ReDimPreserve Items(UBound(Items)+1) Items(UBound(Items))= x EndSubPrivateSub Class_Initialize()ReDim Items(0)EndSub
実行してみる。
Module1のMainプロシージャを実行すると、インプットボックスが表示されるので適当なネストしたIF関数を入れる。
OKをクリックすると、イミディエイトウインドウ内にインデントされた式が出てくる。
ネストしたIFを考えるのが面倒なので、以下のサイトからサンプルをいくつかいただいてきた。
Excel(エクセル)関数の技:IF関数のネスト(入れ子)の方法
例1)
=IF(C3="","",IF(C3>60,"○",IF(C3>30,"△","×"))
↓
=IF(C3="", "", IF(C3>60, "○", IF(C3>30, "△", "×"))
例2)
=IF(AND(C3>30,C3<=60),"△",IF(AND(C3>=0,C3<=30),"×",IF(AND(C3>60,C3<=100),"○","")))
↓
=IF(AND(C3>30,C3<=60), "△", IF(AND(C3>=0,C3<=30), "×", IF(AND(C3>60,C3<=100), "○", "")))
例3)
日本語まじりの抽象式でもこのとおり。
=IF(論理式1, [真の場合1], IF(論理式2, [真の場合2], IF(論理式3, [真の場合3], [偽の場合3])))
↓
=IF(論理式1, [真の場合1], IF(論理式2, [真の場合2], IF(論理式3, [真の場合3], [偽の場合3])))
参考文献

明快入門 コンパイラ・インタプリタ開発 C処理系を作りながら学ぶ 林晴比古実用マスターシリーズ
- 作者:林晴比古
- 出版社/メーカー:SBクリエイティブ
- 発売日: 2015/03/27
- メディア:Kindle版
- この商品を含むブログを見る
2017/08/26 修正
GetCharType関数でNumberの判定が1~9になってたのを0~9に修正しました。
※今回のIFの判定に影響はありません。