C#でインタプリタを作成したら日本語に興味が出た

仕事でインタプリタを作らなくてはならなくなり、2016年中になんとか完成にこぎ着けました。
計算もC#コンパイラがやってくれるものを、あらためて自作することをしていたら、逆ポーランド記法が面白くて、自分でも驚いています。
学生時代に国語が苦手だった原因が、フレーズ分けの説明が先生も参考書のも納得いかない中途半端な理屈・説明だったのを思い出して・・・
逆ポーランド記法がシンプルに、3x5を「3に5をかける」という日本語の語順と(偶然にも)同じという説明が多く、実際にプログラミングしての動作もそう(x35 と記載すると、コンピューターで手順指示による処理と相性が良い)だったので、ここら辺に日本語の文法の何かがある・・・と思わせてくれました。
そしたら、水谷静夫さんという国語辞典の関係者の方がおり、国語学の方ということで『意味記述体系』『曲がり角の日本語』などを著されているのですが、数学、逆ポーランドを国語の説明といいますか、解析として、そして説明としているということで、大変興味部会のです。

日本語を理論的にフレーズ分解するというのは、自分なりに考えたとしても非常に意味不明としかいいようのない理由付けになり、なんとなく日本語を使っているだけであって、きれいな日本語を使いたいという「つまらない思い」となってしまった気持ちだけが漂っていたのですが、この著者のように数学・自然科学的に日本語・国語を理解しようとしていいんだ、という解放された気持ちにも似た不思議な体験をさせて頂きました。

コンピューターに数学の計算式を計算させる複数の手順の中の1ステップとして、日本語での計算手順説明の順序に、数学式を並び替える、という処理があるのは非常に不思議に感じます。

プロパティというオブジェクト指向的な「モノに(複数の)属性」という考えでいえば、最近ではダウンタウンMCの俳句番組で、日本語には接続詞としての「や」でいろんな説明が出てきて、それぞれ納得のいく理路整然とした感じでされており、それを「や」のプロパティについての説明、と考えると、そこから日本語のフレーズ処理を自然科学的に説明できるのでは?と思うところです。

自然科学というのは、例えば裁判では「証拠」というものがありますが、どの程度の証拠さ加減が必要か?という問題に最高裁が説明したものに、「理屈に他者が異を挟む余地が無い程度」ではないが「自然科学のような絶対の証明というのではなく」というようなものがあります。
英語はフレーズ分けが比較的簡単というのに対して、日本語は難しい。そして、英語は単語に語順の意味合いが無いので単語の出現順が必須であるのに対して、日本語は単語に順序を入れ替える変化があるので、フレーズ分けにはこの要素をもプロパティとして、一覧表のようなものが必要になってくることを考えると、数学でいう○次元、つまりxとyという2つの分からない数字があれば2次元、というのでいうと英語+1次元は確実なので、相当に難しいことをプロパティという概念も知らずに理解しようとしていたんだなと、今更ながらに改めて思うのです。

.Netのformでメモリリーク(解放できないという問題)

C# .Netでオリジナルのメッセージボックスを作り、usingで囲んでnew form()をして、showDialogするだけ、という単純なものを使っていたところ、メモリ使用量が増えていく現象が不思議で仕方ありませんでした。
結論からいうと、
>Control クラス(を継承するクラス)は特別で、特別なところから参照されています。
>null(VB なら Nothing)を代入しても解放されません。

ということがMSDNに記載されていました。
回避方法はformオブジェクトの再利用しかないということで、
>何度も表示/非表示を繰り返すなら、new - Show - Close ではなく、
>Visible プロパティを操作することを勧めます。
>また、ShowDialog を使う場合は、new - ShowDialog - Close - Dispose とするのではなく、
>ShowDialog - Close を繰り返します。

>new~Dispose を繰り返すと、リークはしませんが、メモリの確保量は
>(実験されているように)大きくなるので、パフォーマンスに影響が出ると思われます。
>再生ではなく、再利用することを勧めます。
ということで、厳密な名称についてメモリリークではないが、メモリが解放されないので正式な名称こそページ内に出てきませんが、メモリを解放する能力が無いという点でメモリリークと何が違うのかわかりませんが、利用者からは同じ現象に見えます。

https://social.msdn.microsoft.com/Forums/ja-JP/9a19f648-a849-4090-a05e-e6ffccdc2adc?forum=csharpgeneralja

再利用ということで、staticでformオブジェクトを確保しておき、利用時の初期化用の関数を用意するしかなさそうでした。
実際、そのようにすると嘘のようにメモリの増加が無くなりました・・・

正規表現の肯定先読み、否定先読み、肯定戻り読み、否定戻り読みが簡便すぎた真実・・・ただのオプション設定だった・・・

2段階で指定できる正規表現、というのに気づくと、とても簡単で便利な機能でした。

正規表現でマッチする箇所、というのは使っていると簡単になってきます。
肯定先読みなどは、「マッチ箇所のオプション設定」でしかありません。

順番で書くとわかりやすいのですが、
STEP1.目的の文字列にマッチする正規表現を書く(基本)
STEP 2.実は1.でマッチした箇所、その直前の文字列が○○のときが本当の目的の文字列(肯定戻り読み)
以上。

という感じで、オプション設定のような、レイヤーといいますかフィルター方式でマッチする文字列を設定していける、といった記述になります。

簡単な例ですが、郵便番号にマッチするにしても "〒\d{3}-\d{5}" に対して
STEP1.マッチしたいものは "\d{3}-\d{5}"
STEP2.実は、直前の文字が "〒" のもの
これを "(?<=〒)\d{3}-\d{5}" と書けます。(肯定戻り読み)

なぜ正規表現にこのような機能があり、「〒\d{3}-\d{5}」でもよさそうなところ2段階のものの違いはというと、具体的にはC#ではMatch系で違いが出てきます。
これはマッチした場所の場所も得ることができるので大変便利です。このとき、「〒」の文字を含めるか含めないか、というのをマッチした後に処理するのか、正規表現で記述しておけるか、という違いがあります。
個人的には可能な限り正規表現に含めることができた方が、1年後にソースを見直すときに思い出しやすいのでお勧めです。
なので、〒マークを含めるなら"〒\d{3}-\d{5}"、含めないなら"(?<=〒)\d{3}-\d{5}"とすることで、STEP1.の「\d{3}-\d{5}」についてだけ開始位置などが得られる一方で、STEP2.の〒マークはオプション設定扱いなので含まれない、という結果になります。

また、"(?<=〒)\d{3}-\d{5}"という書き方の一方でグループ指定「(\d{3}-\d{5})」があります。
この場合先ほどのが"(?<=〒)(\d{3}-\d{5})"となってしまいます、あくまで「(?<=〒)」はオプション設定
なので最初のグループは「(\d{3}-\d{5})」になります。(.Net 4.5)



とくに便利なのが(否定戻り読み)になると思うのですが、電話番号ではない、と指定したいケースです。
郵便番号は〒が抜けていて234-5678だけのケースがあるが、電話番号123-4567、TEL123-4567 のようになっているとすると、"\d{3}-\d{5}"に追加のオプション設定:否定戻り読み (?<!電話番号|TEL) を前に付けて
"(?<!電話番号|TEL)\d{3}-\d{5}" とするだけです。

さらにマッチを厳格にしたいとして、次の文字が北海道または沖縄県または東京都に限定したい、というケースでは オプション設定:肯定先読み (?=北海道|沖縄県|東京都) を後ろに付けて
"(?<!電話番号|TEL)\d{3}-\d{5}(?=北海道|沖縄県|東京都)"とするだけです。

C#で、RichTextBoxのスクロール連携class

RichTextBoxなら縦スクロールの連携ができる方法がSendMessageを使わなくてはいけないにせよ、可能です。
連携させると、他のペアも、となっていきます。
そのとき、クラスにしておくとパパッとできるので便利です。
使い方は以下のようになります。


// RichTextBoxの縦スクロールを連動させる処理
private RowScroll_Renkei RSR_chA;
private RowScroll_Renkei RSR_chB;

関数の中で、以下の様にセットしてください。

RSR_chA = new RowScroll_Renkei(textBox2, textBox3);
RSR_chB = new RowScroll_Renkei(textBox1, textBox1_R);




以下が今回使用しているclassです。


/// <summary>
/// RichTextBoxの縦スクロールを連動させる処理
/// </summary>
class RowScroll_Renkei
{
// RichTextBoxの縦スクロールを連動させる処理
[DllImport("USER32.dll")]
private static extern IntPtr SendMessage(IntPtr hWnd, int msg, int wp, out Point lp);
private const int EM_GETSCROLLPOS = 0x04DD;
private const int EM_SETSCROLLPOS = 0x04DE;

// スクロール量を同じにする処理
private bool is_InSameScrollEventing = false;
private List<string> RichTextBox_NAME_list = new List<string>();
private List<RichTextBox> RichTextBox_OBJ_list = new List<RichTextBox>();

public RowScroll_Renkei()
{
}
public RowScroll_Renkei(params RichTextBox[] in_RichTBs)
{
for (int i = 0; i < in_RichTBs.Length; i++)
{
RichTextBox tmp_RichTB = (RichTextBox)in_RichTBs[i];
Add_RichTextBox(tmp_RichTB);
}
}
public void Add_RichTextBox(RichTextBox in_RichTBs)
{
RichTextBox_OBJ_list.Add(in_RichTBs);
RichTextBox_NAME_list.Add(in_RichTBs.Name);
in_RichTBs.VScroll += Plus_Event_VScroll;
}

/// <summary>デコンストラク
/// とりあえず、イベントを1つずつ削除してみる。</summary>
~RowScroll_Renkei()
{
foreach(RichTextBox tmp_RichTB in RichTextBox_OBJ_list)
{
tmp_RichTB.VScroll -= Plus_Event_VScroll;
}
}

void Plus_Event_VScroll(object sender, EventArgs e)
{
if (is_InSameScrollEventing)
return;
is_InSameScrollEventing = true;


Func<string, int> get_obj_number = (in_t1) =>
{
for (int i = 0; i < RichTextBox_NAME_list.Count; i++)
{
if (RichTextBox_NAME_list[i] == in_t1)
{
return i;
}
}
return -1;
};

Point pt_new;
string tmp_sender_name = *1
continue;
if (i == tmp_sender_obj_number) // スクロールされた元のRichTextBoxのときは(多重処理で無限ループになってしまうので)何もしない
continue;
Point pt_moto;
SendMessage(((RichTextBox)RichTextBox_OBJ_list[i]).Handle, EM_GETSCROLLPOS, 0, out pt_moto);
pt_new = new Point(pt_moto.X, pt_new.Y);
SendMessage(((RichTextBox)RichTextBox_OBJ_list[i]).Handle, EM_SETSCROLLPOS, 0, out pt_new);
}

is_InSameScrollEventing = false;
}

}

*1:RichTextBox)sender).Name;
int tmp_sender_obj_number = get_obj_number(tmp_sender_name);

SendMessage(((RichTextBox)sender).Handle, EM_GETSCROLLPOS, 0, out pt_new);

for (int i = 0; i < RichTextBox_NAME_list.Count; i++)
{
if(sender.Equals(RichTextBox_OBJ_list[i]

整形外科で1ヶ月分の湿布を処方されると保険証が使えなくなる?

先日、健康保険組合に問い合わせをすることがありました。
担当者がいろいろと説明をしてくれる方で、最初は詳しく説明してくれていたところから、いろんな話に広がってきました・・・
私が聞き上手なのかわかりませんが・・・

そんな中、健康保険の保険証が労災では使えないという話から、整形外科で1ヶ月の湿布を処方されるようなことがあると、その期間は他の整形関係では保険証が使えなくなるそうで、自費で払う必要があるという実務的な話までしてくれました。
そんな規定があるとは知りませんでした。

普通の病院では長くても1週間~10日分の処方箋なので、なるほどと思ったところです。

湿布のCMもよく見ますし、整形外科が提携しているような表示がありますから目を付けられているのかもしれません。

気をつけましょう。

AutoHotkeyで*指定と例外の組み合わせ記述方法

例えば、Windowsで特定のソフトのときだけキオスクモードのようにしたいというときがあります。
ですが、TABとAltの組み合わせ、左右のWindowsキー単独やAltなど修飾キーとの組み合わせで、OSのタスク切り替え系の画面が出てしまいます。
これを止める方法の1つに、AutoHotkeyで TAB+Alt を無効にしたり、他のキー入力だとさせることで回避することができます。例えば TAB+Alt を F5+Shift にすることで、アプリ側で TAB+Alt が押された時の動作、というのを実現することもできます。

組み合わせが膨大(ShiftとAlt同時押しの組み合わせも有効なので)なので *TAB とホットキーを指定すればTABを含む組み合わせの全て、だと指定できます。
ここでのポイントは、しかしながら、TAB単独やTAB+Shiftのときはそのままにしておきたいという問題です。
なにしろ、A_ThisHotkey という変数にホットキー名が格納されているのですが、TABでもTAB+Shiftでも「*TAB」だけしか格納されないのです!

なので、以下に対処方法として、A_PriorHotkeyを使用するケースを記載します。

TAB:: ;次のTAB UPのときに、1つ前のホットキー名としてA_PriorHotkeyに「TAB」が格納されるのを利用します。
+TAB::
!TAB::return ;ホットキーとして来るが、何もしない。

*TAB UP:: ;「*TAB」だけだとホットキー名が重複してるというエラーが出てしまいます。
;TABキーが(押し下げられた状態から)上がったとき、という指定方法で回避しています。
if(A_PriorHotkey == "TAB") { ;TAB単独のとき
send, {TAB} ;他の内容でもいいのですが、TAB単独は同じ動作で。
} else if(A_PriorHotkey == "+TAB") {
send, +{TAB} ;ホットキー名は「+TAB」ですがsendのときは「+」{TAB}です。
} else if(A_PriorHotkey == "!TAB") {
send, +{F5} ;TAB+AltをShift+F5にしてみます。
}
;ここに記述の無い組み合わせは無効(何も動作しない)とします
return

*AppsKey:: ;これらのキーは単純に無効化にします。
*LWin::
*RWin::
return




これで、TAB+Alt+Shift等も無効にしつつ、TAB単独の場合の動作を記述できました。
これを特定のソフトのときだけ有効、とすればキオスクモード的になります。
もちろん、Ctrl+Alt+Deleteや、Ctrl+Alt+TABのような特殊なものはOSが最優先にフックしてしまいますので、AutoHotkeyでは対応できません。これらは他のドライバレベルで対応するソフトが必要です。

AutoHotkeyは記述方法がイラッとすること多々ありましたが、慣れて動くようになってくると手放せない便利さがあります。

C#の匿名関数でプチクラス。シンプルなプログラムの為に・・・

中型のソフトを作っている中で効率を格段に向上させる方法を見つけました。
関数を作っていると、別の関数にしいが関数の追加は・・・というケースが非常に多々あります。

例えば、1行だけど後で読むと意味不明なので、(コメントは付けるが)わかりやすい関数名で書いておきたい。
しかし、今の関数専用の関数を作ると関数が多すぎて微妙・・・
そんなこんなで、関数自体を小さなクラスとして、小さな関数群を従えて・・・と思うくらいです。

解決法を編み出しました(笑)
結果的には以下のようにします。

public bool Open_DataFile(FileInfo file_pointer, string file_path)
{
// この関数でしか使用しないような小さな関数を、匿名関数として以下のようにFuncで書きます。
  Func<string, bool>is_has_error = (in_filePath) =>
    // ファイルにエラーがあるかどうかを調べます。
  {
    if(File.exist(in_filePath) == false) // ファイルが存在するかどうか
      return true;
    // 他のエラーチェックをして、エラーならtrueを返す。

    //** エラーがないとき
    return false;
  }; // ※匿名関数は最後に ; (セミコロン)が必要です。
  Func<FileInfo, string, bool> DATA_file_open_proc = (in_file_Pointer, in_filePath) =>
  {
    // エラーが無いという前提での、ファイルオープン処理

    return true; // ※匿名関数は返値が必須なので、必要なくてもダミーで返します。
  };


  //***** DataFile はじまり *****
  
  // 1.エラーチェック
  if(is_has_error(file_path))
    return false;

  // 2.ファイルオープン
  DATA_file_open_proc(file_pointer, file_path);

  return true;
}



このように「頭に浮かぶ、やりたいこと」中心に記述することができます。
さらに、ほとんどが上から下へ順方向に考えるだけで済むので、時間が経ってから読み直すのも大変楽になります。とくに複数行の処理などでforやforeachの処理が入り込む処理のとき、チェックと実処理で2度手間のケースもありますが、手間はPCがすることになるだけなので、保守のしやすさから、今ではこの方法を基本としています。

try ~ catch文も便利ですが、根本的な読みやすさ、バグの少なさで匿名関数に勝るものは今のところ見当たりません。