初心者のためのC#プログラミング本格入門53 - 外見から考えよう

 この記事は、初心者のためのC#プログラミング本格入門52の続きです。前回は、オブジェクトのコンポジション(合成)について解説しました。今回は復習を兼ねて、初心者用のオブジェクトの作り方を解説します。
 「 ( 1 + 2 ) / 3 」の様な複数の式を解析するには、新しいオブジェクトを創らねばなりません。ですが、初心者の方はまだ不慣れだと思いますので、新しいオブジェクトを作る過程を丁寧に説明します。
 単一の式ならば解析できますので、先ずは複数の式を区切る方法について考えましょう。式を区切る方法は、前回()記号を使用する事を決定しましたので、「(」記号から「)」記号までの文字列を切出せれば実現できます。早速その方法をプログラムで実現する事を考えましょう。
 初心者がいきなり答えを出そうとしても混乱するので、使用する側のプログラムを作るのがベストです。

static void Main()
{
    //結果をチェック
    System.Tuple<bool, string, string> result =
        CutExpression( " ( + 1 2 3 ) / 3" );
    System.Console.WriteLine( "元となる式:" + result.Item2 );
    System.Console.WriteLine( "式は正しい?:" + result.Item1 );
    System.Console.WriteLine( "取り出した式:" + result.Item3 );

    //エラーの場合もチェック
    System.Console.WriteLine( "" );
    System.Tuple<bool, string, string> error =
       CutExpression( " + 1 2 3 / 3" );
    System.Console.WriteLine( "元となる式:" + error.Item2 );
    System.Console.WriteLine( "式は正しい?:" + error.Item1 );
    System.Console.WriteLine( "取り出した式:" + error.Item3 );
}

static System.Tuple<bool, string, string> CutExpression(
        string expression )
{
    System.Tuple<bool, string, string> error =
            new System.Tuple<bool, string, string>(
                false, expression, "" );
    return error;
}

最低限必要な要素、「式の正しさ」、「元の式」、「取り出した式」を取得する事を考えます。なお、呼び出される側のCutExpression静的メソッドは、ひとまずエラーを表すインスタンスを返すようにしておきます。このプログラムを元にして、徐々にプログラムを完成させます。
 これで完成イメージが湧いたと思います。次はCutExpression静的メソッドの内容を考えましょう。文字列を部分的に取得するメソッドを解説した事がありますが覚えていますか?正解は「Substring」メソッドです。Substringは、開始位置と文字の個数を指定して呼び出します。ということは、開始位置と文字の個数を記憶する変数が必要です。

static System.Tuple<bool, string, string> CutExpression(
        string expression )
{
    int startIndex = -1;
    int count = -1;
    System.Tuple<bool, string, string> error =
            new System.Tuple<bool, string, string>(
                false, expression, "" );
    return error;
}

目的が分かったら後は簡単です。順番に考えていくだけです。開始位置を判定するプログラムを考えてみましょう。

for ( int i = 0; i < expression.Length; i++ )
{
    char tmp = expression[ i ];
    if ( tmp == '(' )
    {
        startIndex = i + 1;
    }
}

「(」記号そのものは必要ないので次の位置を記録しています。その点さえ注意すれば簡単です。次は取り出す文字の個数を計算するために、終了位置も一緒に割り出しましょう。

for ( int i = 0; i < expression.Length; i++ )
{
    char tmp = expression[ i ];
    if ( tmp == '(' )
    {
        startIndex = i + 1;
        continue;
    }
    if ( tmp == ')' )
    {
        endIndex = i;
        break;
    }
}
int count = endIndex - startIndex;

このプログラムは理解できると思います。後は、エラー処理とSubstringメソッドを呼び出しを付け加えると完成です。

static System.Tuple<bool, string, string> CutExpression(
        string expression )
{
    //()記号の範囲を決定
    int startIndex = -1;
    int endIndex = -1;
    for ( int i = 0; i < expression.Length; i++ )
    {
        char tmp = expression[ i ];
        if ( tmp == '(' )
        {
            startIndex = i + 1;
            continue;
        }
        if ( tmp == ')' )
        {
            endIndex = i;
            break;
        }
    }

    //エラー判定
    if ( startIndex == -1 | endIndex == -1 )
    {
        System.Tuple<bool, string, string> error =
            new System.Tuple<bool, string, string>(
                false, expression, "" );
        return error;
    }

    //()内の式を取り出す
    int count = endIndex - startIndex;
    string cutExpression = expression.Substring(
        startIndex, count );
    System.Tuple<bool, string, string> result =
        new System.Tuple<bool, string, string>(
            true, expression, cutExpression );
    return result;
}

エラー判定の「if ( startIndex == -1 | endIndex == -1 )」というプログラムは、OR論理演算子「|」(Shiftキーを押しながら円記号(¥)のキーを押せば入力できます)を使用しています。
 プログラムの意味は、「startIndex変数が-1もしくは、endIndex変数が-1ならば{}内のプログラムを実行する」です。AND論理演算子(&)と違い、どちらかの条件が満たされれば{}内のプログラムが実行されます。
 以上で括弧内の式を取り出す事が可能となりました。これは複数の式を解析するための重要な一歩です。

テーマ : プログラミング
ジャンル : コンピュータ

コメントの投稿

非公開コメント

No title

これだと入力: ") 1 + 2 (" で例外起こしませんか?

Re: No title

> これだと入力: ") 1 + 2 (" で例外起こしませんか?
)がマッチした時点でループ処理がbreak;され、startIndexが-1のままなので例外は発生しないと思います。

No title

ああ、そうか。

いずれにせよ現段階では "(((( 1+2 ))" は正しい式と判定するわけですね。

Re: No title

> ああ、そうか。
>
> いずれにせよ現段階では "(((( 1+2 ))" は正しい式と判定するわけですね。
はい、そうです。
再帰の解説を狙って伏線を張っています。

No title

>Shiftキーを押しながらドルマークのキーを押せば入力できます
|は\マークのキーですよね。

>if ( startIndex == -1 | endIndex == -1 )
||(条件OR演算子)を使うとショートサーキットが行われるので処理が少しだけ速いですよ。左辺の式を評価した結果がtrueであれば、右辺の式を評価した結果がtrueであろうとfalseであろうと、OR演算の結果はtrueになります。左辺の式を評価した結果がtrueであれば、||では右辺の式の評価が省略されます。

左辺の式を評価した結果がtrueであっても、|では右辺の式が評価されます。条件分岐とは別に、条件式が評価可能であることを確認することができますので、使い分けはケースバイケースなのでしょうけれども、インドリさんは演算子の使い分けについてなにかこだわりがありますか?

Re: No title

> >Shiftキーを押しながらドルマークのキーを押せば入力できます
> |は\マークのキーですよね。
>
有難うございます。円記号を書き間違えました。

> >if ( startIndex == -1 | endIndex == -1 )
> ||(条件OR演算子)を使うとショートサーキットが行われるので処理が少しだけ速いですよ。左辺の式を評価した結果がtrueであれば、右辺の式を評価した結果がtrueであろうとfalseであろうと、OR演算の結果はtrueになります。左辺の式を評価した結果がtrueであれば、||では右辺の式の評価が省略されます。
>
> 左辺の式を評価した結果がtrueであっても、|では右辺の式が評価されます。条件分岐とは別に、条件式が評価可能であることを確認することができますので、使い分けはケースバイケースなのでしょうけれども、インドリさんは演算子の使い分けについてなにかこだわりがありますか?

演算子の使い分けの問題ではなくて、連載のやり方に関する拘りです。
ショートカット演算子を解説する前に、必ず非ショートカット演算子を解説しないと初心者に対して不親切過ぎると考えているからです。
ショートカット演算子を解説する予定がありそれまでは使用しません。
この連載は自分が主役ではなく初心者が主役であり、常に初心者目線で書いています。
私自身の書き方は全く違います。
文章は自分ではなく相手目線で書くのが私のポリシーです。

No title

× ショートカット
○ ショートサーキット

No title

>インドリさん
||は短絡評価(short cuircit evaluation)を行う演算子ですので、ショートカット(近道)演算子ではなくて、ショートサーキット(短絡)演算子と呼ぶほうが適切だと思います。インドリさんは順序立てて解説されていて、||はのちに解説されるご予定だということですね。私は勇み足でしたね。失礼しました。

No title

それと、ショートカット”演算子"は適切ではありません。
ショートサーキット"評価"です。初心者には正しい説明を。

Re: No title

> × ショートカット
> ○ ショートサーキット
短絡評価(short-circuit evaluation)/最小評価(minimal evaluation)ですよね。

Re: No title

> それと、ショートカット”演算子"は適切ではありません。
> ショートサーキット"評価"です。初心者には正しい説明を。
誤解されているようですが、初心者ではなくて貴方になら分かると思って愛称で書いています。

No title

インドリさん、こんばんは。
> ( + 1 2 3 ) / 3
計算式が間違ってると思います。
正しくはこうじゃないですか?
(1 + 2) / 3

Re: No title

> インドリさん、こんばんは。
> > ( + 1 2 3 ) / 3
> 計算式が間違ってると思います。
> 正しくはこうじゃないですか?
> (1 + 2) / 3
連載を読んでいれば分かって頂けたと思いますが、現在は演算子の位置をチェックしていません。
今後のために意図的に前置方式をOKにしてます。

No title

> インドリさん
インドリさんは初心者が主役だといわれた。初心者は( + 1 2 3 ) / 3をみて、プログラムで処理する計算式が間違っているのではないのかな?と思うのではないでしょうか。文章では「『 ( 1 + 2 ) / 3 』の様な複数の式を解析するには」と、一般にみかける中置記法の計算式が述べられていますから、どうしてプログラムでは計算式が( + 1 2 3 ) / 3になるのだろうかな?と、普通の人は思うのではないでしょうか。( + 1 2 3 ) / 3を前置記法で記述するなら/ + + 1 2 3 3となって、中置記法で記述するなら( 1 + 2 + 3 ) / 3になると思います。演算子の位置をチェックしていないということは、()の最初の位置に+がある必要がなくて、演算子が1つ省略される必要もないわけですから、ああよくみる計算式だなと初心者がみて思えるような、初心者が自然に理解できる計算式を使用するべきではないでしょうか。

Re: No title

> > インドリさん
> インドリさんは初心者が主役だといわれた。初心者は( + 1 2 3 ) / 3をみて、プログラムで処理する計算式が間違っているのではないのかな?と思うのではないでしょうか。文章では「『 ( 1 + 2 ) / 3 』の様な複数の式を解析するには」と、一般にみかける中置記法の計算式が述べられていますから、どうしてプログラムでは計算式が( + 1 2 3 ) / 3になるのだろうかな?と、普通の人は思うのではないでしょうか。( + 1 2 3 ) / 3を前置記法で記述するなら/ + + 1 2 3 3となって、中置記法で記述するなら( 1 + 2 + 3 ) / 3になると思います。演算子の位置をチェックしていないということは、()の最初の位置に+がある必要がなくて、演算子が1つ省略される必要もないわけですから、ああよくみる計算式だなと初心者がみて思えるような、初心者が自然に理解できる計算式を使用するべきではないでしょうか。

連載をずっと読んでいれば分かります。
連載なのですから、たとえ初心者であって短文だけを読んでその様な判断をする事はありません。
初心者を馬鹿にしすぎていると思えてなりません。
それと、文章の意味や主題を読まなすぎます。
意図的にはっている伏線を間違っていると騒がないでください。
騒がれるとネタばれをしなくてはならなくなります。
短絡評価がそれに該当します。
今後ネタばれにつながるようなコメントは止めて下さい。
コメントをする前によく読んで下さい。

No title

わたしは||についてコメントしましたが、騒ぐというのはどういうことでしょうか。インドリさんは、わたしが||についてやかましくいいたてたと受け取っておられるのですか?

わたしは初心者を馬鹿にしたつもりはありません。( 1 + 2 ) / 3が( + 1 2 3 ) / 3になることを疑問に思うことは馬鹿なことなんですか?

インドリさんが書こうとしているネタを教えてもらったことがわたしにはありませんので、どの話がネタバレにつながるのかわかりません。

Re: No title

> わたしは||についてコメントしましたが、騒ぐというのはどういうことでしょうか。インドリさんは、わたしが||についてやかましくいいたてたと受け取っておられるのですか?
>
執拗に当たり前のことをコメントをし続けています。
そういうのを騒いでいると表現しています。

> わたしは初心者を馬鹿にしたつもりはありません。( 1 + 2 ) / 3が( + 1 2 3 ) / 3になることを疑問に思うことは馬鹿なことなんですか?
>
連載を読んでいれば分分かる事だからです。
「この連載を読んでいる初心者には読解能力がない」と言っているのと同じなので馬鹿にしていると思います。
貴方にとって初心者とは読解能力がない人の事なのかもしれませんが、私は相手を卑下するのが嫌いなのでそう考えておりません。

> インドリさんが書こうとしているネタを教えてもらったことがわたしにはありませんので、どの話がネタバレにつながるのかわかりません。
前のコメントで短絡評価だと言っています。
兎に角ちゃんと読もう。
それと、連載という意味をよく考えてからコメントして下さい。

最後に一言、警告を発します。
読者である初心者を馬鹿にする発言は許せません。
読者を馬鹿にする発言を続けるのであればもう許可しません。

承認待ちコメント

このコメントは管理者の承認待ちです
プロフィール

Author:インドリ
みなさん、はじめまして、
コンニチハ。

ボクは、無限の夢(infinity dream)を持つネタ好きな虹色の鳥インドリ(in dre)です。
色々な情報処理技術を啄ばむから楽しみにしてね。

http://twitter.com/indori
は別人による嫌がらせ行為です。
私とは関係ないので注意して下さい。
次はなりすましブログなどをするかもしれませんが、ここ以外でブログをするつもりがないので、ここ以外にインドリのブログがあったとしても無視してください。


何度言っても分からない人がいるので、ここにコメント欄へ書き込むときの注意事項を書きます。


一、社会人としてのマナーをわきまえましょう。
一、妄想に基づく書き込みを止めてください。
一、暴言の類は書かないで下さい。
一、某誹謗中傷サイトの書き込みは彼らの妄想に基づく書き込みですから無視して、ここへ書き込まないで下さい。
一、コメント書く前に他のコメントよく読んでから行って下さい。
一、その他常識的に考えて迷惑なコメントはしないで下さい。


以上のルールを守れない人のコメントは削除します。



利用上の注意
ここに紹介してある文章およびプログラムコードは正確であるように心がけておりますが、内容を保証するものではありません。当サイトの内容によって生じた損害については、一切の責任を負いませんので御了承ください。


執筆したCodeZineの記事


【VB.NETで仮想CPUを作ろう】

  1. VB.NETで仮想CPUを作ろう
  2. レジスタの実装
  3. 仮想CPUのGUI化
  4. テストドライバの改良
  5. CPUの基礎動作の実装
  6. MOV命令の実装
  7. ADD命令実装
  8. SUB命令実装
  9. INC命令&DEC命令の実装と命令長
  10. MLU命令の実装とModR/Mについて
  11. DIV命令の実装とイベント設計について
  12. 機械語駆動式 関数電卓を作ろう!
  13. 機械語駆動式 関数電卓を作ろう! 解答編(前半)
  14. 機械語駆動式 関数電卓を作ろう! 解答編(後半)


【仮想ネットワーク実装でTCP/IPを学ぼう】
  1. TCP/IPの基礎と勘所
  2. ネットワークアクセス層の勘所
  3. インターネット層の勘所
  4. トランスポート層の勘所
  5. アプリケーション層の勘所
  6. セキュリティの基礎と仮想ネットワークの仕様
  7. GDI+と独自プロトコルの定義



【並列化】
インテル Parallel Studioを使って並列化プログラミングを試してみた
並列プログラミングの効率的なデバッグを実現する「Parallel Inspector」


【TBBシリーズ】
  1. インテル スレッディング・ビルディング・ブロックの概要
  2. インテルTBBから学ぶループの並列化
  3. スレッドセーフとインテルTBBのコンテナ
  4. インテルTBBのスレッドクラス


【OpenMPシリーズ】
  1. OpenMPの基礎構文
  2. OpenMPの実行時ライブラリと並列ループ
  3. OpenMPのメモリモデルとfork- joinモデル

最近の記事
最近のコメント
月別アーカイブ
カテゴリ
Ada (9)
COBOL (5)
C (9)
C++ (11)
C# (98)
D (25)
Java (8)
Perl (1)
Ruby (14)
PHP (2)
Boo (2)
Cobra (2)
LISP (6)
F# (20)
HTML (0)
XHTML (0)
CSS (0)
XML (0)
XSLT (0)
Scala (4)
WPF (0)
WF (2)
WCF (0)
LINQ (4)
MONO (5)
Linux (0)
MySQL (0)
ブログ内検索
リンク
最近のトラックバック
RSSフィード
By FC2ブログ

今すぐブログを作ろう!

Powered By FC2ブログ

ブロとも申請フォーム

この人とブロともになる

QRコード
FC2カウンター