実数を表す文字列を受理するオートマトン①
前回のつづき。「正規表現とNFA・DFA」を参考に、実数の正規表現を元に状態遷移図を描いて、C#で実装してみたい。
RとSを正規表現とすると、RSとR|SとR*を受理するオートマトン(ただし、ε遷移あり)は次のように表現できるらしい。
- RS
- R|S
- R*
「整数を表す文字列を受理するオートマトン - チキン煮込みチーズミックス4辛」で、実数を表す文字列の正規表現は
^[-+]?([0-9]+(\.[0-9]*)?|\.[0-9]+)([eE][-+]?[0-9]+)?$
って書いたけど、上記3つを使って考えるために、次のように書き換えよう。
^[-+]?([0-9][0-9]*(\.[0-9]*)?|\.[0-9][0-9]*)([eE][-+]?[0-9][0-9]*)?$
さらに、簡単化のために[-+]と[0-9]と[eE]をそれぞれX、Y、Zとおいて、
^X?(YY*(\.Y*)?|\.YY*)(ZX?YY*)?$
としてみる。とりあえず、状態遷移図はε遷移を含んだ形で作っていく。上記の正規表現を小分けにして考えて、最後に合体させてから、ε遷移を除去していくことを考える。
続きを読むこんにちははてなブログ
はてなダイアリーから引っ越ししてきたよ~!
引っ越し
はてなダイアリーが終わるらしい。
2019年春「はてなダイアリー」終了のお知らせと「はてなブログ」への移行のお願い - はてなダイアリー日記
引越しせんとな。
整数を表す文字列を受理するオートマトン
正規表現は
^[-+]?[1-9][0-9]*$
でいいんじゃないかな。これに相当する状態遷移図は
でいいんじゃないかな。S2が受理状態。S3になったら直ちにエラーとして停止していい。これを元に作ってみたC#のクラスは
interface Checker { /// <summary>このオブジェクトの状態を初期状態に戻す</summary> void Reset(); /// <summary>1文字読む</summary> /// <param name="ch">読込む文字</param> void Next(char ch); /// <summary>受理状態かどうか調べる</summary> /// <returns>true:受理状態, false:受理状態ではない</returns> bool IsAcceptable(); /// <summary>エラー状態かどうか調べる</summary> /// <remarks>エラー状態になった場合、それ以降の文字を読む必要が無い</remarks> /// <returns>true:エラー状態, false:エラー状態ではない</returns> bool IsError(); }
class IntChecker : Checker { enum IntParsingState { /// <summary>状態0</summary> State0, /// <summary>状態1</summary> State1, /// <summary>状態2</summary> State2, /// <summary>状態3</summary> State3, } IntParsingState _state; public IntChecker() { Reset(); } public void Reset() { _state = IntParsingState.State0; } public void Next(char ch) { switch (_state) { case IntParsingState.State0: if (ch == '-' || ch == '+') { _state = IntParsingState.State1; } else if (ch == '1' || ch == '2' || ch == '3' || ch == '4' || ch == '5' || ch == '6' || ch == '7' || ch == '8' || ch == '9') { _state = IntParsingState.State2; } else { _state = IntParsingState.State3; } break; case IntParsingState.State1: if (ch == '1' || ch == '2' || ch == '3' || ch == '4' || ch == '5' || ch == '6' || ch == '7' || ch == '8' || ch == '9') { _state = IntParsingState.State2; } else { _state = IntParsingState.State3; } break; case IntParsingState.State2: if (ch == '0' || ch == '1' || ch == '2' || ch == '3' || ch == '4' || ch == '5' || ch == '6' || ch == '7' || ch == '8' || ch == '9') { _state = IntParsingState.State2; } else { _state = IntParsingState.State3; } break; case IntParsingState.State3: _state = IntParsingState.State3; break; } } /// <summary>受理状態かどうかを判断する</summary> /// <returns></returns> public bool IsAcceptable() { return _state == IntParsingState.State2; } public bool IsError() { return _state == IntParsingState.State3; } }
でいいんじゃないかな。これを
Checker checker = new IntChecker(); string str = "+123"; foreach (char ch in str) { checker.Next(ch); if (checker.IsError()) { break; } } if (checker.IsAcceptable()) { Console.WriteLine("整数だよ"); } else { Console.WriteLine("整数じゃないよ"); }
こんな感じで使ってみたりなんかしちゃって。
実数でも同じことしたい。実数を表す文字列の正規表現は
^[-+]?([0-9]+(\.[0-9]*)?|\.[0-9]+)([eE][-+]?[0-9]+)?$
でよさげだけど、今のところ状態遷移図を描けてない。ちょっと考える。
数式の結果が更新されない問題の回避方法
セルの値が正しく表示されない問題「数式の結果が更新されない - チキン煮込みチーズミックス4辛」を書いたが、回避の方法を見つけた。
数式内で、次のようにシート名まで書くと、問題は起こらないようだ。理由は不明。
=concat(Sheet1!A2:A21)
やっぱり無理だった
一回うまくいった気がしたけど。勘違いだった。もしくは、本当に上手くいったけど、再現性がない?
とにかく、まだ解決しない。
Excelで列番号を英文字に変換する方法
「Excelで列番号を英文字に変換する方法 - チキン煮込みチーズミックス4辛」は間違っていた。Excelの列を表すアルファベットは、A〜Zを使った26進数表現ではない。具体的には、次のようになっている。
桁数 | 列番号(0ベース*1) | アルファベット*2 | 場合の数 |
---|---|---|---|
1 | 0〜25 | A〜Z | 通り |
2 | 26〜701 | AA〜ZZ | 通り |
3 | 702〜18278 | AAA〜ZZZ | 通り |
また、上記のURLで紹介したアルゴリズムは、アルファベットが2桁までならば正しく計算できるようだ。アルファベットが3桁になるような列番号を入力すると、間違った値が得られる。ConvertToLetter(702)は"ZZ"で、ConvertToLetter(703)は"[A"となってしまう。
ということで、列番号を英文字に変換するVBA関数ConvertToLetter2を書いてみた。
Option Explicit Function ConvertToLetter2(iCol As Integer) As String 'Excelの列番号は1始まりなので、-1して0始まりにする ConvertToLetter2 = ConvertToLetter2_detail(iCol - 1) End Function Function ConvertToLetter2_detail(iCol As Integer) As String Dim nCases As Long Dim nCasesPrev As Long Dim i As Integer '何桁になるか調べる i = 1 nCases = 0 nCasesPrev = 0 Do While True nCasesPrev = nCases nCases = SumOfPower(1, i, 26) If iCol < nCases Then Exit Do End If i = i + 1 Loop 'i桁のアルファベットであることが確定 'i-1桁の最大値以下ではnCasesPrev通りの場合の数を表現できる Dim rest As Long rest = iCol - nCasesPrev Dim result As String result = ConvertRadix(rest) Dim act_length As Integer act_length = Len(result) 'i桁にする Dim shortage As Integer shortage = i - act_length For i = 0 To shortage - 1 result = "A" & result Next ConvertToLetter2_detail = result End Function Function SumOfPower(lowerLim As Integer, upperLim As Integer, base As Integer) As Long Dim result As Long result = 0 Dim i As Integer For i = lowerLim To upperLim Dim tmp As Long tmp = base ^ i result = result + tmp Next SumOfPower = result End Function Function ConvertRadix(n10 As Long) As String Dim result As String Dim codeA As Integer Dim radix As Integer Dim quotient As Long codeA = Asc("A") radix = 26 Dim r, q As Long quotient = n10 Do q = quotient \ radix r = quotient - (q * radix) quotient = q If q > 0 Then result = Chr(codeA + r) & result End If Loop While q > 0 result = Chr(codeA + r) & result ConvertRadix = result End Function