fc2ブログ

C#文法リファレンス - デバッグ関連のプリプロセスディレクティブ

概要
 エラーを出したり、警告を出したりと、デバッグで使用できるプリプロセスディレクティブがあります。

日常でたとえると
 今日は棚卸をして期末の締処理を行う。その際、途中計算をメモ書きする。

使用に適した状況
 デバッグ作業で、ソースコードをいじり倒したい時。試し書きの要素が強いです。

サンプル

/*----------------------------------------------------
 * 
 *  デバッグ関連のプリプロセスディレクティブ
 *  
 ----------------------------------------------------*/
#define PIYO 
//#define PIYOPIYO
using System;
class Sample
{
    static void Main( )
    {

#if PIYO
#line 6 "Main.cs" //PIYOが6行目にあることを記述
#warning ピヨモードをお客様に見せるのはまずいピヨ
        Console.WriteLine( "ピヨ♪" );
#endif

#if PIYOPIYO
        Console.WriteLine("ピヨピヨピヨピヨピヨピヨ");
#line 7 "Main.cs"
#error ピヨピヨモードは絶対に外部に出したら駄目ピヨ
#endif

        //終了
        Console.ReadLine();
    }
}


文法
 「#warning」ディレクティブで警告、「#error」ディレクティブでエラーを出せます。また、「#line」ディレクティブで、警告もしくはデバッグで出力されるソースコードの場所を指定できます。

解説
 古来のC言語プログラミングでは、ソースコードを直接いじって、デバッグ作業をしていました。そうする理由は、開発環境がそれほど発達していなかったからです。当時は今と違い、開発マシンのスペックが低く、テキストエディタだけでプログラミングをしていました。といっても、概念的には今とほとんど変わりがありませんでした。
 昔と違い、テキストエディタだけで開発することは少なくなりましたが、技法そのものはいまだに有用です。開発環境が対応していないきめ細かなデバッグや解析を、自分自身で行う際には、デバッグ関連のディレクティブと、画面出力命令を駆使するとよいでしょう。
 とはいえ、あくまでも「下書き」段階で行う事だと思います。私個人としては、デバッグ関連のディレクティブは、用が済めばすぐに消します。万が一、何らかの手違いで、デバッグバージョンの製品が納品されると目も当てられません。十分に注意しましょう。

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

C#文法リファレンス - #if関連のプリプロセスディレクティブ

概要
 各種プリプロセスディレクティブを使用すれば、条件によりコンパイルするプログラムを変更できます。

日常でたとえると
 状況や内容を判断して、報告書のレイアウトを変更する。

使用に適した状況
 プリプロセスディレクティブの使用自体、あまりお勧めできませんが、プロジェクトの作業体制により、やむ得ないときがあります。例えば、基韓系業務システムにおいて、個別に実装するのではなく、カスタマイズという形で提供したい場合、プリプロセッサディレクティブを使用する可能性があります。

サンプル

/*----------------------------------------------------
 * 
 *  #if関連のプリプロセスディレクティブ
 *  
 ----------------------------------------------------*/
#define FOO //定義
#define BAR
#undef FOO //定義の無効化
#define PIYO
using System;
class Sample
{
    static void Main( )
    {

#if FOO && PIYO //複数の条件を指定可能
        Console.WriteLine( "FOOが定義されているピヨ。" );
#elif FOO
        Console.WrteLine( "FOOが定義されています。" );
#elif PIYO
        Console.WriteLine( "PIYOが定義されているピヨ。" );
#else
        Console.WriteLine( "何も定義されていません。" );
#endif

        //終了
        Console.ReadLine();
    }
}


文法
 「#」記号を付けるのがポイントです。それ以外は通常のif文と変わりませんが、else if などが省略名になっているので注意してください。また、変数の定義を特別なキーワード(#define)で行う必要がある点に注意してください。

解説
 コンパイル方式は古来、コードをテキストとみなす考え方が基本でした。具体的には、コードをテキストとみなし、直接テキストを変更することにより、コンパイルで出力するバイナリを変更する方法があります。これをプリプロセッサ方式と呼びます。
 プリプロセッサ方式は、コンパイラはテキスト変換プログラムとみなせるので、コードを解釈する前に、テキストそのものを変えてしまえば、出力するバイナリを変更できるという理屈で作られたものです。
 C#言語にもC言語にあったプリプロセッサ方式があります。しかしながら、プリプロセッサ方式は原始的なものであり、乱暴な方法だといえます。いまどきのコンパイル方式は、コードをただのテキストとみなさず、メタ情報とみなしています。特にC#が採用している、型ベースのオブジェクト指向もその考え方に基づいています。
 従って、プリプロセスディレクティブは、他に手段がないときに限って使用するべきだといえます。プリプロセスディレクティブを使用しなくても、オブジェクト指向分析とオブジェクト指向設計をちゃんと行っていれば拡張性が高い実装にできます
 なお、C#にプリプロセッサはありません。擬似的に処理をしているだけです。この点からも、プリプロセスディレクティブの使用が例外的な方法だという事が伺えます。

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

C#文法リファレンス - 高階関数

概要
 振る舞いとデータとして扱うことにより、プログラミングの幅を広げます。

日常でたとえると
 人間がとる行動を分類活用する。

使用に適した状況
 振る舞いに一定のパターンが見受けられ、そのパターンを有効に活用できる場合に使用します。

サンプル

/*----------------------------------------------------
 * 
 *  高階関数
 *  
 ----------------------------------------------------*/
using System;
using System.Collections.Generic;

class HigherOrderFunction
{
    //数値リストに対して演算を適用する高階関数
    static void Apply( 
        List<int> list, 
        Func<int, int, int> ApplyFunc, //受け取る関数
        int value )
    {
        for ( int i = 0 ; i < list.Count ; i++ ) {
            list[ i ] = ApplyFunc( list[ i ], value );
        }
    }

    static void Main( )
    {
        //データを準備する
        Console.Write( "演算前の数値:" );
        List<int> list = new List<int>();
        for ( int i = 0 ; i < 10 ; i++ ) {
            list.Add( i );
            Console.Write( i + " " );
        };
        Console.WriteLine( "\n" );

        //加算
        int value = 5;
        Console.WriteLine( "{0}を加算", value );
        Apply( list, ( x, y ) => x + y, value );
        Console.Write( "加算後の値:" );
        foreach ( int val in list ) {
            Console.Write( val + " " );
        };
        Console.WriteLine( "\n" );
    }
}


文法
 メソッドのパラメーターもしくは戻り値に、デリゲートを指定します。

解説
 ここ数年の間で、関数型プログラミングが一般化してきました。関数型プログラミングは、関数をデータと同様に扱い、プログラムの表現力を上げる考え方の事です。多くのプログラマーは、命令型プログラミングに慣れています。命令型プログラミングでは、関数(振る舞い)はデータと厳密に区分され、データと振る舞いは別物として扱います。
 しかしながら、データだけではなく、振る舞いにも一定のパターンが存在します。そのパターンを扱えば、同様の処理を何度も書かなくても済みます。さらに、振る舞いとデータを厳密に区分すると実現できないことができるようになります。
 実のところ計算機の本質は、データと振る舞いの区分はあってないが如しです。何故ならば0と1だけがそこにあるだけだからです。データだとか、振る舞いだとか、そういったものを考えているのは人間の主観であり、全ては幻です。ならば、その幻想を打ち捨て、さらなる表現力を追い求め、先入観を捨て、高階関数を自由自在に使うのが、真のプログラマーの姿だといえると思います。

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

計算機の基本原理を味わおう28 - 冗長命令の最適化

 この記事は、計算機の基本原理を味わおう27 - セグメントアドレス指定方式の弱点とオーバーフローの危険な関係の続きです。前回は、計算機を扱ううえで避けられない、セグメントアドレス指定方式の弱点と、オーバーフローについて解説しました。今回は、冗長な命令をどのようにして扱うのかについて解説します。
 前回示したように、最小計算機は16個を超える比較移動命令を扱えません。では、どうすればいいのでしょうか?その答えは単純明快です。長ければ短くすればいいのです。短くするという観点から、もう一度ビット値比較命令の降雪について考察してみましょう。
 ビット値を真偽に見立てて表にしてみます。そうすると、次のようになります・・・

Y       X    結果 
0(偽) 1(真)  0(偽)
0(偽) 0(偽)  1(真)
1(真)  1(真)  1(真)
1(真)  0(偽)  0(偽)
※結果はXの領域に格納される仕様であることに注意

0を偽1を真としてとらえると、Yの値が真の時はXと結果は同であることが見てとれます。言い換えれば、Yの値が真の場合は、Xの値が変化しないので何もしなくてもよいのです。ここに答えがあります。何もしなければ、始めから命令を用意しなければ命令の長さを短くできます。

0 : cmov flag, y
1 : cmov pc, trueY
2 : cmov flag, 1  //falseY
3 : cmov flag, x
4 : cmov pc, trueX
5 : cmov flag, 1 //falseX
6 : cmov x, 1 
7 : cmov stop, 9 //終了地点へジャンプ
8 : cmov x, 0 //trueX
9 : cmov stop, 0

この命令を生成するには・・・

//eqlb命令
public class EqualBit1 : IfCode
{
    //比較対象が真の場合に実行するコードを取得する。
    protected override IEnumerable<CompareMove> GetTrueCodes( )
    {
        return new List<CompareMove>();
    }
}


//if文を表わすオブジェクト。
public class If : CompositeInstruction
{
    //偽コードブロックを生成する。
    private void CreateFalseCode( ExecuteEnvironment info )
    {
        this._falseCodes.Insert(
            0,
            new CompareMove(
                info.CompareFlagAddress,
                info.OneAddress,
                "比較フラグを真に設定。" ) );
        if ( this._trueCodes.Count() == 0 ) return; //※ここ重要!
        CompareMove jump = new CompareMove(
                info.ProgramCounterAddress,
                0,
                "偽のコードブロックの終了地点。" );
        jump.IsJump = true;
        jump.JumpPoint = 
            this._trueCodes.Count + 
            this._addTrueCodeCount
            + 1; //※自己を含めるので1加算
        this._falseCodes.Add( jump );
    }
}

こんな具合に冗長な命令を生成しないようにします。
 こういった冗長命令を削減する行為を、冗長命令の最適化と呼びます。これは、コンパイラで一般的に行われていることです。こういったことがコンパイル段階で行われていることを理解していれば、バイナリレベルでのデバッグができるようになりますし、情報技術に関する深い知識が得られえます。
 ちなみに私は、冗長命令の最適化という概念を、オブジェクト指向プログラミングでコンパイラを実装することにより得ました。この経験から、現代のプログラミングは、オブジェクト指向プログラミングが当たり前になっており、こういったバイナリレベルの知識が無関係だと軽視されますが、オブジェクト指向とバイナリは意外と深い関係があると考えています。続く...

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

計算機の基本原理を味わおう27 - セグメントアドレス指定方式の弱点とオーバーフローの危険な関係

 この記事は、計算機の基本原理を味わおう26 - オブジェクト指向アセンブラプログラミング?の続きです。前回はアセンブラコードを、オブジェクト指向で扱う方法について解説しました。今回は、セグメントアドレス指定方式と、オーバフローについて解説します。
 前回の答えわかりましたか?ヒントはサンプルコードのコメントに書いてあります。BitEqualTest( m ); //この命令は長すぎて正常に実行できない
 では、長い命令とは何を意味するのでしょうか?その答えを知るには、最小計算機の仕様について考えねばなりません。ビットマシンである最小計算機は何ビットまでの命令を扱えるでしょうか?答えは256ビットです。おや?おかしいですね。セグメント機能を使って、256 × 256 = 65536 にしたはずです。通常の人は、65536ビットまで対応しているはずだと考えるでしょう。
 しかしながら、セグメントアドレス指定方式は、セグメントアドレスと、ベースとなるプログラムカウンタが別に存在しています。従って、256個のセグメントは扱えますが、個々のセグメントは256ビットを超える命令を扱えません。これは実際に存在した問題に基づいています。
 インテル社製CPUはその昔、1Mバイトの壁と呼ばれるものと、64Kバイトの壁というものがありました。1Mバイトの壁により、搭載メモリ容量が1M以上あっても、リアルモードでは1Mまでのアドレスしか指定できませんでした。また、個々のセグメントは64Kバイトまでしか指定できませんでした。この問題は、当時の状況を考えると仕方がないものでした。
 同様に、最小計算機のプログラムカウンタは8ビット(byte型)なので、255を超えるアドレスを指定しようとすると、不都合なことが起こります。どうなるのかは、環境とコンパイラオプションに依存します。よくあるのは、255をインクリメントすると0になる現象です。従って最小計算機は、比較移動命令1個当たり16ビット必要なので、16個までの比較移動命令しか扱えないということです。
 それを確かめるために、例外処理をプログラムに仕掛けてみましょう。

//アドレスを管理するオブジェクト。
public class Addresser
{
    //次の命令が格納されているアドレスを取得する。
    public ushort GetNextProgramCounter()
    {
        if ( this.ProgramCounter.Value == 255 ) {
            throw new InvalidOperationException(
                "プログラムカウンタが限界値を超えてしまいました。" );
        }
        ushort result = ( ushort ) ( this._cs.Value << 8 );
        result += this.ProgramCounter.Value++;
        return result;
    }
}

このプログラム付け加えて実行すると、正しく実装していれば例外処理が発生します。
 以上にように、計算機は限られた資源で数値を表現しているので、扱える数値の限界を超えてしまうことがよくあります。これをオーバーフローと呼びます。
 計算機を扱う以上、オーバーフローの危険は常に存在します。オーバーフローを意識して、プログラミングしましょう。今回はこれで終わりです。続く。

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

計算機の基本原理を味わおう26 - オブジェクト指向アセンブラプログラミング?

 この記事は、計算機の基本原理を味わおう25 - アセンブリコード合体!の続きです。前回は、ちょっと長いアセンブリコードを書く方法について解説しました。今回は、入れ子構造アセンブリコードについて解説します。
 前回の最後に予告した通り、Equalオブジェクトを実装してみましょう。いくつもの方法がありますが、こんな具合の実装が長さ的に良いかと思います。

using System;
using System.Collections.Generic;

namespace MiniBitMachine.V3
{
    //eql(等価性判定)命令
    public sealed class Equal : IfCode
    {

        //対象アドレスとCPUの情報を元に、インスタンスを生成する。
        public Equal( byte targetAddress, Cpu cpu )
            : this( targetAddress, cpu, true )
        {
        }

        //対象アドレスとCPUの情報を元に、インスタンスを生成する。
        public Equal( byte targetAddress, Cpu cpu, bool addLast )
            : base( "eql", targetAddress, cpu )
        {
            this.CreateInstruction( cpu, addLast );
        }

        //比較フラグ設定に関するコードを取得する。
        protected override IEnumerable<CompareMove> GetCompareCodes( )
        {
            CompareMove[ ] result = new CompareMove[ 1 ];
            result[ 0 ] = new CompareMove(
                this.Info.CompareFlagAddress,
                this.TargetAddress,
                "実行フラグの場所に対象データを移動。" );
            return result;
        }

        //比較対象が真の場合に実行するコードを取得する。
        protected override IEnumerable<CompareMove> GetTrueCodes( )
        {
            CompareMove[ ] result = new CompareMove[ 1 ];
            result[ 0 ] = new CompareMove(
                this.TargetAddress,
                this.Info.OneAddress,
                "対象を1に設定。" );
            return result;
        }

        //比較対象が偽の場合に実行するコードを取得する。
        protected override IEnumerable<CompareMove> GetFalseCodes( )
        {
            CompareMove[ ] result = new CompareMove[ 1 ];
            result[ 0 ] = new CompareMove(
                this.TargetAddress,
                this.Info.ZeroAddress,
                "対象を0に設定。" );
            return result;
        }

        //アドレスを表すインスタンスを取得する。
        protected override string GetAddressString( )
        {
            System.Text.StringBuilder result = 
                new System.Text.StringBuilder();
            result.Append( "対象アドレス:" );
            result.Append( this.TargetAddress );
            result.Append( Environment.NewLine );
            return result.ToString();
        }

        //値を表すインスタンスを取得する。
        protected override string GetValueString( )
        {
            System.Text.StringBuilder result = 
                new System.Text.StringBuilder();
            result.Append( "対象値:" );
            result.Append( this.TargetValue );
            result.Append( Environment.NewLine );
            return result.ToString();
        }

        //オブジェクトが持つ各種値を保存する。
        internal override void SaveData( )
        {
            this.SaveValue = this.TargetValue;
        }

        //※リファクタリングにより名前を改名した
        //オブジェクトが保持している値と、現在値を比べ、
        //妥当な値ならばtrueを返す。
        internal override bool ValidData( )
        {
            if ( this.SaveValue == this.TargetValue ) return true;
            return false;
        }
    }
}

オブジェクト指向プログラミングをすると、非常に簡単なのが味わえます。でも、オブジェクト指向プログラミングの神髄はここからです。BitEqualは入れ子構造になっており、ちょっと複雑ですが、オブジェクトにしたら超簡単です。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace MiniBitMachine.V3
{
    //eqlb命令
    public class EqualBit : IfCode
    {
        //比較対象のアドレス。
        private byte _leftAddress;
        public byte LeftAddress
        {
            get { return _leftAddress; }
        }

        //対象の値( 左の値:X )
        public bool LeftValue
        {
            get
            {
                return this.Cpu.ReadMemoryData( this.LeftAddress );
            }
        }
        private bool _saveLeftValue;

        //対象アドレスとCPUの情報を元に、インスタンスを生成する。
        public EqualBit( byte targetAddress, byte targetAddress1, Cpu cpu )
            : this( targetAddress, targetAddress1, cpu, true )
        {
        }

        //対象アドレスとCPUの情報を元に、インスタンスを生成する。
        public EqualBit( 
            byte leftAddress, byte rightAddress, Cpu cpu, bool addLast ) 
            : base( "eqlb", rightAddress, cpu )
        {
            this._leftAddress = leftAddress; 
            this.CreateInstruction( cpu, addLast );
            this._if.ReLocation();
        }

        //比較フラグ設定に関するコードを取得する。
        protected override IEnumerable<CompareMove> GetCompareCodes( )
        {
            CompareMove[ ] result = new CompareMove[ 1 ];
            result[ 0 ] = new CompareMove(
                this.Info.CompareFlagAddress,
                this.TargetAddress,
                "実行フラグの場所に対象データを移動。" );
            return result;
        }

        //比較対象が真の場合に実行するコードを取得する。
        protected override IEnumerable<CompareMove> GetTrueCodes( )
        {
            Equal result = new Equal( this.LeftAddress, this.Cpu, false );
            return result.Compares;
        }

        //比較対象が偽の場合に実行するコードを取得する。
        protected override IEnumerable<CompareMove> GetFalseCodes( )
        {
            Not result = new Not( this.LeftAddress, this.Cpu, true );
            return result.Compares;
        }

        //アドレスを表すインスタンスを取得する。
        protected override string GetAddressString( )
        {
            System.Text.StringBuilder result = 
                new System.Text.StringBuilder();
            result.Append( "対象アドレスX:" );
            result.Append( this.TargetAddress );
            result.Append( Environment.NewLine );
            result.Append( "対象アドレスY:" );
            result.Append( this.LeftAddress );
            result.Append( Environment.NewLine );
            return result.ToString();
        }

        //値を表すインスタンスを取得する。
        protected override string GetValueString( )
        {
            System.Text.StringBuilder result = 
                new System.Text.StringBuilder();
            result.Append( "X:" );
            result.Append( this.LeftValue );
            result.Append( Environment.NewLine );
            result.Append( "Y:" );
            result.Append( this.TargetValue );
            result.Append( Environment.NewLine );
            return result.ToString();
        }

        //オブジェクトが持つ各種値を保存する。
        internal override void SaveData( )
        {
            this._saveLeftValue = this.LeftValue;
            this.SaveValue = this.TargetValue;
        }

        //オブジェクトが保持している値と、現在値を比べ、
        //妥当な値ならばtrueを返す。
        internal override bool ValidData( )
        {
            if ( this.SaveValue )
                return this._saveLeftValue == this.LeftValue;
            else
                return this._saveLeftValue != this.LeftValue;
        }
    }
}

オブジェクトを再利用すれば、ほとんどないのと同じぐらい簡単です。入れ子になったアセンブリコードはちょっと書きにくいですが、オブジェクトだとそんな細部に煩わせられることはありません。最後にこのオブジェクトを使うだけです。

using System;
using System.Diagnostics;
using System.Linq;
using MiniBitMachine;
using MiniBitMachine.V3;
using MiniBitMachine.V3.Utility;

class Sample
{
    //命令をテストする。
    private static ExecutionResult Execute(
        IfCode code,
        Cpu cpu, 
        byte targetAddress, 
        bool targetValue )
    {
        cpu.Reset();
        cpu.WriteMemoryData( targetAddress, targetValue );
        code.SaveData();
        Console.WriteLine( code.Name + "命令を実行します・・・" );
        Console.WriteLine( "---------- 実行前 ---------- " );
        Console.WriteLine( code.ToString( "v" ) );
        ExecutionResult result = cpu.AllRun();
        Console.WriteLine( "----------  実行後 ---------- " );
        Console.WriteLine( code.ToString( "v" ) );
        Console.WriteLine( "----------  状態 ---------- " );
        Console.WriteLine( result );
        Debug.Assert( code.ValidData() );
        return result;
    }

    //テストプログラムの開始部分
    private static void TestMain(
        Cpu cpu, 
        IfCode code, 
        byte targetAddress )
    {
        code.SetBinary();
        Console.WriteLine(
            "********** " + code.Name + "命令の内容 **********" );
        Console.WriteLine( Cpu.GetEnvironment() );
        Console.Write( cpu );
        Console.Write( code.ToString( "a" ) );
        Console.WriteLine( );
        Console.Write( code.ToString( "d" ) );
        Console.WriteLine( );
        ExecutionResult result;
        result = Execute( code, cpu, targetAddress, false );
        result += Execute( code, cpu, targetAddress, true );
        Console.WriteLine( "★★★★★ 総計 ★★★★★" );
        Console.WriteLine( result );
    }

    //eqlb命令のテストを行う。
    private static void BitEqualTest( Memory m )
    {
        byte ds = 0;
        byte cs = 1;
        Cpu cpu = new Cpu( ds, cs, m );
        byte targetAddress = 3;
        byte targetAddress1 = 4;
        EqualBit eql = new EqualBit(
            targetAddress,
            targetAddress1,
            cpu );
        cpu.WriteMemoryData( targetAddress1, false );
        TestMain( cpu, eql, targetAddress );
        cpu.WriteMemoryData( targetAddress1, true );
        TestMain( cpu, eql, targetAddress );
        Console.WriteLine();
    }

    static void Main()
    {
        Memory m = new Memory();
        BitEqualTest( m ); //この命令は長すぎて正常に実行できない
        Console.ReadLine();
    }
}

今回は楽勝ですね・・・と、言いたいところですが、最小計算機の仕様により無限ループします。いったいなぜなんでしょうか?!謎をはらみつつ次回へ続く。

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

ネタつつき200 - IT業界の構造から見える日本の封建社会性

 日本社会はいまだに封建社会の発想で物事を考えています。例えば、IT業界にはそれが如実に表れています。IT業界は多重請負が常習化する、階級社会になっています。しかも、それは本当の意味でレッテルであり、実力は関係なく、お客様の事なんて一切考えていません。さらに、会社内部でも役割が位と混同されています。
 情報技術にも色々な役割があります。プログラマー、システムエンジニア、データベースエンジニア、ネットワークエンジニア、セキュリティーエンジニア、オペレーションエンジニア、テストエンジニア、システムアーキテクチャ、プロジェクトマネージャなどです。むろん普通の会社にもいる、営業マンや経理マン達もいます。それが何故か、その役割が位と混同されており、そのレッテルだけで報酬などが決まってしまいます。
 プログラマー35歳限界説などいろいろ屁理屈をこねる人がいますが、全ての役割(情報技術者兼経営者)をしている私としては、ナンセンス以外の何物でもないと思います。これらの役割は、ただの役割分担であり、プロジェクトの成果に直結していません。プロジェクトの成果として現れるのは、あくまでも個人の実力であり、レッテルではありません。どの役割の人も必要であり、蔑視していい役割の人なんていません。しかしながら、プログラマーを奴隷扱いもしくは最下層扱いする会社は多いです。最近は状況が変わって、テストエンジニアがそうなっている節があります。
 いずれにせよ、身分的な発想で上下関係を考え、それで報酬や出世が決まるなんてことは、商売をしているのではなく、性質が悪い似非宗教をしているとしか言いようがありません。似非宗教をするために会社を経営しているのか、商売をするために経営をしているのか、よく考えるべきだと思います。
 他の業界の人も読んでいると思いますので、具体的な例を挙げます。優秀なプログラマーの人の報酬を上げたい(地位を上げたい)から、プログラミングをせずに、プロジェクトマネージャになれと命令する会社が存在します。これは商売上、非常にナンセンスです。その人はプログラマーとして優秀なのであって、プロジェクトマネージャとして優秀なのかは未知数です。また、その人が最大の戦力である状態なのにも関わらず、位を上げたいから絶対にプログラミングをするなと命令をする会社は、儲けるために従業員の役割を定めるのではなく、そういう封建社会的な似非宗教的行為のためにそういうふうな行為をしています。そういった会社が、エースを失い、サービスレベルが下がって衰退するのは目に見えています。
 商売として考えるのであれば、最大の利益を上げる人員配置を考えるべきです。従って、このケースの場合、エースプログラマーの役割をそのままにして、報酬と待遇だけを上げればよいと思います。報酬を上げたいから、会社の利益を損なうことをするなんてことは、非常に馬鹿げているというのはわかっていただけると思います。
 この位で職業の役割を考え、厳密に区分したがる日本企業の性質は、秒進日歩のIT業界では通用しません。日本企業は、プログラマーを卒業するという発想ですが、情報技術の進歩は速く、無理やりマネージャにしても、現在の情報技術を知らない、迷惑なマネージャになってしまうだけです。優秀な人を駄目な人にして、何が商売なのか、不思議でなりません。
 このような事を言うと、「個々の役割が高度化して、役割分担をせざるを得ない、それ故に、組織構造がそのようになってしまう。」という人がよくいます。しかしそれは、情報技術の無知さを露呈する発言であり、経営論としても遅れています。プロジェクトの役割設定は必要ですが、情報技術は全てが繋がっており、不可分であって、それ故に私は全ての領域を常に鍛錬しています。加えて、その手の役割分担化した組織構造が、経営上よくないことは、十年以上前に判明しています。いまだにこの常識レベルの経営論を知らないなんて信じられません。情報技術と経営を知らずに、IT会社をしている人がいるのが怖すぎます。この辺が成り立つのも、日本は実利で動くのではなく、封建社会的構造で動いているのがわかります。
 普通に情報技術によるビジネスを考えるのであれば、全ての人が情報技術を学び、どの分野が得意なのかで、プロジェクトごとに役割分担を変えることになります。プロジェクト毎と表現しているのは、状況が刻一刻と変化するからです。それに加えて、プロジェクトごとに特色があり、全てに適用できる理想的方法が存在しないからです。従って、アジャイルにプロジェクトを完遂する事だけを考えて、商売をするしかありません。目の前のプロジェクトを完成できずに、この商売は成り立ちません。商品をまともに作れない会社がどうやって商売をするのでしょうか?この当たり前の事実を無理するから、多重請負というお客様を無視した非効率的産業構造になっているのです。ただ、お客様の方も、サービスの質ではなく、会社のレッテルで購入するので、非常に根深い問題です。
 よくシステム開発は建築にたとえられますが、あくまでもたとえです。建築の場合は、理論でわかっていても肉体的にできないことがあるから、設計者と肉体労働者に分かれています。ですが、PCを使うのに身体能力は関係ありません。性別も年齢もなにも関係なく、ただ技術力があるかないかだけです。役割を細分化して、それを位として設定して、似非宗教をするのは馬鹿げています。商売をしているのですから、「如何にしてお客様を満足させるか」しかありません。
 そういえば、日本は学問も細分化して、本当の学問ではなく、お受験という儀式の道具として悪用しています。こういうところにも、封建社会的発想が見えますが、民主主義国家になったのですから、本当の意味の位は「天皇」と「国民」の2つだけでいいです。国会議員を位と混同している人がいますが、民主義国家では役割にすぎません。
 日本はいまだに封建社会の発想で動いています。それ故に、不景気になり、社会問題が発生しているといえます。いい加減に幻想に逃げ込んで鎖国するのではなく、現実を直視して、物事を正しく考えなければならないと私は思えてなりません。私は再び文明開化を望みます。もしかしたら私達は「ええじゃないか」と踊る必要があるのかもしれません。

テーマ : 文明・文化&思想
ジャンル : 学問・文化・芸術

中の人の徒然草480 人間ほど難しい物はない

 たまには中の人らしいことを書こうと思います。以前のC#初心者用記事の書き方でも言及しましたが、私は10分ぐらいで答えを考えてから、それを崩しつつ、記事を書いています。
 今回連載している、「計算機の基本原理を味わおう」でも、予め解説したい事柄を全て考え、全てを実装してから、解説用に崩しつつ書いています。例えば、セグメントの解説をしいたとき、セグメントが必要になる状況(主にそうしないと駄目な状況)を考え、そこから書いています。この時意識している事は、聞き手の心に合わせるということです。
 解説したい事柄を初心者が知っていることはありません。何故ならば、知らない人を対象にしているからです。知っている人に知っている内容を伝えたいとは思いません。それほど無駄で虚しい行為はありません。従って、自分も知らなければどのように考えるのかを想像しつつ、初心者になりきって、初心者目線で書いているのです。この方法は実務で覚えました。
 システム屋の私は、お客様から相談を受けた時点で、答えがほぼわかっている状態になります。私も一応それで飯を食っている身ですので、お客様と会話し現状把握ができた時点で、頭の中で情報システムが完成します。もっと正確にいうと、情報システムの候補がいくつか頭の中で出来上がっている状態です。後はお客様の要望から候補を選ぶだけです。
 情報システムが頭の中で完成しているのは、プロとして当然の事なので、ここまでは普通のことです。しかし、ここからが難しいです。真の実務の恐ろしさはここからです。
 お客様は情報技術に関しては素人なので、こちらが持っている完成イメージを理解できません。また、私が分析の結果知っている状況を、お客様自身が把握していないのが普通の状態です。情報量に格差がある状態で、コミュニケーションしなければなりません。
 情報量もしくは情報の範囲が違う人同士のコミュニケーションは非常に困難です。こちらが当たり前だと思っていたことが、相手にとって当たり前でないことが多々あります。また、私がいくら分析しても、その道のプロであるお客様しか知りえないことがあります。このお客様だけが知っている情報で、頭の中の完成イメージは大きく変わることなんてざらにあります。
 この状況を例えると、通信プロトコルが違うコンピューター同士が、情報喪失が多発する低品質インターネットで通信をしている状態です。並大抵の方法では通じ合えません。いかにして、この状態を打破する方法をこの十年模索し続けました。
 その結果、効果が高いと判断したのが、相手のプロトコルを分析し、自分のプロトコルを相手のプロトコルのデータに変換して通信する方法です。つまり、相手の立場で物事を考えて、自分の意見を伝えつつ作品を完成させるのです。
 思えば私は始めから、人間の部分で苦労してきました。元々研究者だったので、技術的に困った経験がほとんどありません。大半の既存技術は専門書などを読めばすぐに理解できますし、いざとなったら自分で新しい技術を作ればいいだけの話です。しかし、人間相手だとそうはいきません。人間ほど難しいものはありません。
 これはどの仕事でも同じだと思います。その道のプロである専門家は専門分野においてはそれほど困りません。ただ鍛錬あるのみです。しかし、人間の部分は理論と根性でどうにかできる問題ではありません。常に不完全な状態で挑まないとなりません。答えなんて初めから存在しませんし、理不尽と矛盾がデフォルトの状態です。
 しかしながら、現実の不条理さを克服してこそ真のプロといえると私は考えています。真のプロを目指して、私は人との関わりあい方を模索し続けます。

テーマ : 文明・文化&思想
ジャンル : 学問・文化・芸術

計算機の基本原理を味わおう25 - アセンブリコード合体!

 この記事は、計算機の基本原理を味わおう24 - ビット値の等価性について深く考えようの続きです。前回は、ビット値の等価性について解説しました。今回は、ちょっと長いアセンブリコードを作成する方法について解説します。
 前回、ビット値等価性判定のアルゴリズムを値により分類し、細かい単位で考えました。次は細かなアセンブリコードを合体します。ひとまず、Yの値を基準にして合体してみましょう。

「Yが0」
0 : cmov flag, y
1 : cmov pc, trueY
2 : cmov flag, 1  //falseY
3 : cmov flag, x
4 : cmov pc, trueX
5 : cmov flag, 1 //falseX
6 : cmov x, 1 
7 : cmov pc, 9 //終了地点へジャンプ
8 : cmov x, 0 //trueX
9 : cmov stop, 0

「Yが1」
0 : cmov flag, y
1 : cmov pc, trueY 
2 : cmov flag, x //trueY
3 : cmov pc, trueX
4 : cmov flag, 1 //falseX
5 : cmov x, 0
6 : cmov pc, 8 //終了地点へジャンプ
7 : cmov x, 1 //trueX
8 : cmov stop, 0

ここまではOKだと思います。not命令とinc命令を考えた時と同じ状態です。では、この2つのコードブロックを合体してみましょう。

0 : cmov flag, y
1 : cmov pc, trueY
2 : cmov flag, 1  //falseY
3 : cmov flag, x
4 : cmov pc, trueX
5 : cmov flag, 1 //falseX
6 : cmov x, 1 
7 : cmov pc, 9 //終了地点へジャンプ
8 : cmov x, 0 //trueX
9 : stop, 0 //終了地点
10 : cmov pc, 16 //終了地点へジャンプ
11 : cmov flag, x //trueY
12 : cmov pc, trueX
13 : cmov flag, 1 //falseX
14 : cmov x, 0
15 : cmov pc, 16 //終了地点へジャンプ
16 : cmov x, 1 //trueX
17 : cmov stop, 0

単純に合体すると18行になってしまいました。この冗長なアセンブリコードをC#で実装するのは面倒です。今まで作ってきたコードを再利用できないのか考えるのがオブジェクト指向の考え方です。Yの値を基準にして条件分岐されているので、その中のコードをオブジェクトして捉えてみましょう。すると、次のことがわかると思います。

Yの値が0の場合はnot命令(Xの値が反転している)
Yの値が1の場合はnotの逆(Xの値がそのまま)

ということは、普通に考えると、Notオブジェクトと、Notオブジェクトの逆の動作をするEqualオブジェクトを実装し、その2つのオブジェクトを再利用するEqualBit(eqlb)を実装すればよいことになります。長くなったので次回へ続く・・・

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

計算機の基本原理を味わおう24 - ビット値の等価性について深く考えよう

 この記事は、計算機の基本原理を味わおう23 - 名前に注目して論理エラーを直そうの続きです。前回は、論理エラーと対処法について解説しました。今回は、ビット値における等価性について解説します。
 最少計算機を通じて、より深く計算機を理解するために、もう少し量が多いプログラミングをするのがよいと思います。そこで今回は、ビット値の等価性を判定するアセンブラプログラミングを題材にすることにしました。
 ビット値の等価性について、多くの人が「そんなの簡単だ。考える価値もない。」と思う事でしょう。しかしながら、アセンブラレベルで考えると、意外と奥が深いです。特に最少計算機には比較移動命令しかないので、実装がちょっと難しいです。いきなり最少計算機のアセンブラプログラミングをするのはハードルが高いので、ビット値の等価性判定についてのアルゴリズムを書くところから始めましょう。
 あるビット値Xとビット値Yの等価性は、次のアルゴリズムで判断します。

「ビット値等価性判定アルゴリズム」
1.Yが0の場合
 1.1 Xが0ならば真
 1.2 Xが1ならば偽
2.Yが1の場合
 2.1 Xが0ならば偽
 2.2 Xが1ならば真

ここまではOKだと思います。だけど、ここから少し難しいです。ビット値等価性判定アルゴリズムを最少計算機のアセンブリコードに翻訳するのはちょっと骨が折れます。纏めると読みにくいので、ケースごとに分けて書いてみましょう。


「Yが0、Xが0のコード」
0 : cmov flag, y
1 : cmov pc, trueY
2 : cmov flag, 1  //falseY
3 : cmov flag, x
4 : cmov pc, trueX
5 : cmov flag, 1 //falseX
6 : cmov x, 1 
7 : cmov stop, 0

「Yが0、Xが1のコード」
0 : cmov flag, y
1 : cmov pc, trueY
2 : cmov flag, 1 //falseY
3 : cmov flag, x
4 : cmov pc, trueX
5 : cmov x, 0 //trueX
6 : cmov stop, 0

「Yが1、Xが0のコード」
0 : cmov flag, y
1 : cmov pc, trueY 
2 : cmov flag, x //trueY
3 : cmov pc, trueX
4 : cmov flag, 1 //falseX
5 : cmov x, 0
6 : cmov stop, 0

「Yが1、Xが1のコード」
0 : cmov flag, y
1 : cmov pc, trueY
2 : cmov flag, x //trueY
3 : cmov pc, trueX
4 : cmov x, 1 //trueX
5 : cmov stop, 0

本来比較移動命令cmovに指定できるのは数値だけなのですが、このコードは、わかりやすいようにYが0の時falseYなどと表示しています。コメントで「flaseY」と書かれていれば、それはYが0のときに実行するコードブロックの先頭を意味しています。また、宛先のレジスタやフラグ設定などもわかりやすいようにしています。
 アセンブラレベルで考える習慣がない、大多数のプログラマーはちょっと難しいと感じると思います。それは、普通の感覚なので気にしないでください。いまどき、アセンブラレベルで考える人の方が少数派です。では、じっとコードを見つめてください。一定のパターンがあることがわかると思います。
 プログラミングで重要なのは、パターンを読み取る事です。パターン読み取り能力が高いほど優秀なプログラマーだといえると思います。パターン認識ができない人は、細かいことを言えても実務で活躍できません。
 では、このコードに何のパターンがあるでしょうか?いくつかの事が言えます。大まかにいうと、定型的な処理があり、Xの値が反転する場合と、そのままの場合があります。具体的にいうと、フラグ設定部分とジャンプ部分が定型的処理です。これは、最少計算機の仕様によるものです。
 いよいよここからが本題です。今までわかったことを総合して考え、一つの命令をアセンブラ(cmovだけ)で実装しましょう。さらに、C#で最少計算機の実装もしましょう。それができて初めて、ビット値の等価性について理解したといえると思います。きりがいいので、今回はこれで終わります。続く・・・

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

プロフィール

インドリ

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# (370)
D (25)
Java (8)
Perl (1)
Ruby (14)
PHP (2)
Boo (2)
Cobra (2)
LISP (6)
F# (33)
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フィード
ブロとも申請フォーム

この人とブロともになる

QRコード
FC2カウンター