fc2ブログ

マルチパラダイム時代におけるデータ構造 第7回 ー 宣言的プログラミングの力を感じよう

 この記事は、「マルチパラダイム時代におけるデータ構造 第6回 ー ユーザーストーリーを考えよう」の続きです。前回は、ユーザーストーリーとテストについて解説しました。今回は、宣言的プログラミングで、コンセンスセルオブジェクトを実装する方法について解説します。
 いよいよ、本格的なデータ構造であるコンセンスセルオブジェクトを実装します。実装する方法は様々ですが、C#はマルチパラダイム言語なので、宣言型プログラミングで実装することにします。マルチパラダイム言語では、始めに宣言型プログラミングをし、必要に応じて命令型プログラミングをする方法が最適です。その理由は、これらから判明します。
 宣言型プログラミングで、コンセンスセルオブジェクトの発生・更新・消滅のアルゴリズムを考えと、アルゴリズムがほとんど同じになります。何故ならば、指定された位置で処理を行うだけだからです。文章ではわかりにくいと思いますので、実際のプログラムを見てください。

//1つの要素とそれ以外の要素への参照を持つデータ構造。
public class ConsCell<T> : IDataFreeManager<T>
{
    //指定位置で任意の関数を実行する。
    private ConsCell<T> Common(
        ConsCell<T> old,
        T value,
        int index,
        Func<ConsCell<T>, T, ConsCell<T>> hitIndexFunc,
        int count )
    {
        if ( index == count ) {
            return hitIndexFunc( old, value );
        } else {
            if ( old == null ) return null;
            return new ConsCell<T>(
                old.Car,
                this.Common(
                    old.Cdr,
                    value,
                    index,
                    hitIndexFunc,
                    ++count ) );
        }
    }
}

ずいぶんシンプルで驚いた人もいると思います。宣言型プログラミングに慣れていない人は、アルゴリズムの意味が分かりにくいと思いますので解説します。
 この共通処理のアルゴリズムは再帰処理構造です。指定された位置がくるまで、共通処理を呼び出しつつ、新しいインスタンスを生成しています。何故ならば、このオブジェクトが不変だからです。オブジェクトが不変の場合、データライフサイクルは、全て新しいオブジェクトを生成することになります。具体的には、CarとCdrプロパティが書き換えできないため、新しく必要があります。不変オブジェクトは、一見制約に見えますが、こうした新しい表現方法を生み出します。
 共通処理があればあとは簡単です。その共通処理を呼び出すだけです。Insert、Update、Delteのアルゴリズムはすごく単純です。

public class ConsCel<T> : IDataFreeManager<T>
{
    //cdrを初期化する
    private ConsCell<T> InitCdr( ConsCell<T> old )
    {
        return old == null
            ? null
            : new ConsCell<T>(
                old.Car,
                this.InitCdr( old.Cdr ) );
    }

    //最後尾へデータを追加する。
    public void Insert( T value )
    {
        this.Insert( value, this.Count );
    }

    //指定位置へデータを発生させる。
    public void Insert( T value, int index )
    {
        this._cons = this.Common(
            this,
            value,
            index,
            ( ConsCell<T> obj, T element ) =>
                new ConsCell<T>(
                    element,
                    this.InitCdr( obj ) ),
            0 );
    }

    //指定位置のデータを更新する。
    public void Update( T newValue, int index )
    {
        this._cons = this.Common(
            this,
            newValue,
            index,
            ( ConsCell<T> obj, T element ) =>
                new ConsCell<T>(
                element,
                this.InitCdr( obj.Cdr ) ),
            0 );
    }

    //最後尾のデータを消滅する。
    public void Delete( )
    {
        this.Delete( this.Count - 1 );
    }

    //指定位置のデータを消滅する。
    public void Delete( int index )
    {
        this._cons = this.Common(
            this,
            default( T ),
            index,
            ( ConsCell<T> obj, T element ) =>
                obj.Cdr == null
                ? null
                : new ConsCell<T>(
                    obj.Cdr.Car,
                    this.InitCdr(
                        obj.Cdr.Cdr ) ),
            0 );
    }

    //暗黙的にコンスセルをタプルに変換する。
    //読みやすくするために実装
    public static implicit operator
        Tuple<T, ConsCell<T>>( ConsCell<T> obj )
    {
        return new Tuple<T, ConsCell<T>>( 
            obj.Car, obj.Cdr );
    }
}

見ての通り複雑な事を考えなくてもOKです。目的だけを考えて実装すればよくなるので、簡単に考えられます。命令型プログラミングで実装する場合は、本質とは関係がない余計な処理を考えなくてはなりませんが、宣言型プログラミングでは本質だけを考えられるようなるので簡潔明瞭になります。
 あとは要素の個数を数えるだけです。再帰処理で考えれば、カウント処理も簡単です。

//cdrの数を数える。
private int GetCdrCount( ConsCell<T> target, int index )
{
    if ( target.Cdr == null ) return index;
    else return GetCdrCount( target.Cdr, ++index );
}

//要素の数を返す。
public int Count
{
    get 
    {
        return this.GetCdrCount( this, 1 );
    }
}

おっと、忘れるところでした、選択も同様に実装します・・・

//最初のデータを選択する。
public T Select( )
{
    return this.Car;
}

//指定位置のデータを選択する。
private ConsCell<T> Select( 
    ConsCell<T> target, int index, int count )
{
    if ( index >= this.Count )
        throw new IndexOutOfRangeException( 
            "指定された位置" + index + 
            "にデータは存在しません。" );
    return index == count
        ? target
        : this.Select(
            target.Cdr, 
            index, 
            ++count );
}

内容は指定位置(インデックス)にヒットするまで再帰的に呼び出すだけです。こちらもずいぶん簡単ですね♪
 今回の記事で、宣言型プログラミングの強力さが伝わったと思います。マルチパラダイム言語を使うのですから、使わない手はありません。次回へ続く。
スポンサーサイト



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

計算機の基本原理を味わおう34 - カラスが鳴いたら帰りましょう♪

 この記事は、計算機の基本原理を味わおう33 - 帰るべきところはどこか?の続きです。前回は、サブルーチンの仕組みについて解説を始めました。今回も引き続き、サブルーチンの実現方法を解説します。
 戻る場所がわからないのが問題ならば、決めてしまえばいい。それが計算機でよくある仕様です。サブルーチンの戻る場所についても、計算機の使用として予め場所を決めておく方法が一番簡単です。その場所は2つ考えられます。メモリの特定領域とレジスタです。今回はメモリ領域を予約する方式で行うことにします。
 メモリのどこにするのかは「どこでもいい」のですが、最小計算機の使用は、データセグメント0にしておきます。それが決まれば後は簡単。アドレスを管理するオブジェクトに、ロード命令とセーブ命令を追加します。

using System;
using System.Collections.Generic;
using System.Linq;
using MiniBitMachine.Utility;

//アドレスを管理するオブジェクト。
internal class Addresser
{
    //関係のないプログラムは省略・・・

    //プログラムカウンタとコードセグメントの値を保存する。
    private void SaveAddress( )
    {
        ushort address = 0;
        var pc = BinaryUtility.ConvertBinaryWithZero(
            ( byte ) ( this._pc.Value + 1 ) );
        var cs = BinaryUtility.ConvertBinaryWithZero(
            ( byte ) this._cs.Value );
        List<bool> binary = new List<bool>();
        binary.AddRange( pc );
        binary.AddRange( cs );
        this._cpu.ReferenceMemory.Write(
            address, binary );
    }

    //保存したプログラムカウンタとコードセグメントの値を復元する。
    internal void LoadAddress( )
    {
        //値を読み出す
        ushort address = 0;
        List<bool> pcbinary = new List<bool>();
        Memory m = this._cpu.ReferenceMemory;
        pcbinary.AddRange( m.Read( address, 8 ) );
        byte pc = BitMaster.ConvertToByte( pcbinary );
        List<bool> csbinary = new List<bool>();
        csbinary.AddRange( m.Read(
            ( ushort ) ( address + 8 ), 8 ) );
        byte cs = BitMaster.ConvertToByte( csbinary );
        //イベントをいったん無効化して値を復元
        this._cs.ValueChangeedEvent 
            -= CsValueChangeedEvent;
        this._cs.Value = cs;
        this._pc.Value = pc;
        this._cs.ValueChangeedEvent 
            += CsValueChangeedEvent;
    }
}

後はいつこれらの命令を呼び出すか決めるだけです。しかも、自ずと決まってきます。サーブについてはコードセグメントを切り替える瞬間、ロードは任意の比較移動命令を実行するときがベストでしょう。という事は・・・

using System;
using System.Collections.Generic;
using System.Linq;
using MiniBitMachine.Utility;

//関係のないプログラムは省略・・・

//アドレスを管理するオブジェクト。
internal class Addresser
{
    //CS変更時に実行する処理。
    private void CsValueChangeedEvent( object sender, EventArgs e )
    {
        SaveAddress();
        this._pc.Value = 0;
    }
}

//実行に必要な情報を保持するオブジェクト。
public partial class ExecuteEnvironment
{
    //インスタンスを生成する。
    internal ExecuteEnvironment( Cpu cpu )
    {
        this._cpu = cpu;
        this.ZeroAddress = 0;
        this.OneAddress = 1;
        this.LastAddress = 255;
        this.CompareFlagAddress = 254;
        this.ProgramCounterAddress = 253;
        this.CodeSegmentAddress = 252;
        this.LoadAddress = 251;
        this.Init();
    }

    //オペランド取得関数を用意する。
    private void InitGetOperandTable( )
    {
        this._getOperandTable.Add(
            this.LoadAddress,
                ( CompareMove ope ) => {
                    var result = new GetOperandResult( ope );
                    result.Result =
                        new Immediate( this.LoadAddress );
                    result.IsLoad = true;
                    result.IsImmediate = true;
                    //比較移動命令にロード命令を埋め込む
                    result.Target.LoadAddressFunc =
                        ( ) => this._cpu.Addresser.LoadAddress();
                    return result;
                } );
    }
}

//比較移動命令。
public class CompareMove :
    Instruction,
    IEquatable<CompareMove>
{
    //命令を実行する。
    public virtual void Execute( )
    {
        if ( this._loadAddressFunc != null ) {
            this.LoadAddressFunc();
        } else if ( this._compareFlag ) {
            this.Destination.Value = this.Source.Value;
        }
    }
}

となります。ただし、比較移動命令オブジェクトに特別な命令を埋め込むのは、美しいコードとは言えません。改善の余地があります。それについては、今後触れると思います。
 実装したら、もちろんテストでしょう。テストも実装します・・・

class Sample
{
    static void Main( )
    {
        CallNotTest();
        Console.ReadLine();
    }

        //Not命令呼び出し。
    static void CallNotTest( )
    {
        //下準備
        byte ds = 1;
        byte cs = 2;
        byte targetAddress;
        var mem = new Memory();
        InitNot( ref mem, ds, cs, out targetAddress );
        Cpu cpu = new Cpu( ds, ( byte ) ( cs + 1 ), mem );
        ExecuteEnvironment exeinfo = cpu.GetEnvironment();

        //次の命令を実行すための準備用命令
        var ex = cpu.ExecuteInfo;
        var flagSet = new CompareMove();
        flagSet.Destination =
            new Immediate( ex.CompareFlagAddress );
        flagSet.Source =
            new Immediate( ex.OneAddress );

        //not命令を呼び出す命令を用意
        var call = new CompareMove();
        call.Destination =
            new Immediate( ex.CodeSegmentAddress );
        call.Source = new Immediate( cs );

        //not命令の呼出し後にストップする命令を用意
        var stop = cpu.GetStopCpuInstruction( true );

        //命令書き込み
        var codes = new CompareMove[ ] { 
            flagSet, call, stop };
        exeinfo.CodeWrite( codes );

        //テスト実行
        cpu.AllRun();
        bool value = exeinfo.ReadMemoryData( targetAddress );
        if ( value )
            Console.WriteLine( "エラー" );
        else
            Console.WriteLine( "成功" );
    }
}

このテストに合格したらOKです。これでサブルーチンらしいものが実現できるようになりました。しかし、メモリ領域やレジスタを使用する方法には、致命的な弱点が存在します。続く・・・

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

ニュースを分析第6回 ー ニュースをもとに自分が住む世界を考察

 今回は、自分が面白いと感じたニュースを取り上げます。私が今日、面白いと感じたニュースは「資格があると、転職に不利になる!?人事部は、「資格取得」をこう見ていた! 」です。このニュースの記事を読んで、日本人の仕事観を垣間見たような気がしました。
 このニュースの内容は、資格を多く取得していれば転職に有利になると考えがちだが、人事担当者に不快感を与える可能性があるから実際は不利になるかもしれないという事でした。インタビューに答えた人事担当者の意見が興味深いです。
 人事担当者の意見は、業務と関係のない学習は時間の無駄になるし、転職準備をしている恐れが強いというものでした。この意見に対して私が感じたことは、転職の可能性は確かにありますが、他人の人生を管理する考えは不適切だという事です。
 私はシステム屋ですが、情報技術だけを学習していません。趣味で数学を学習し、それにめどがついたら、今後は物理学や化学などの学問にも手を出そうと考えています。そのほかにも、美術にも興味がありますし、文学にも興味があります。さらには、IT系以外の資格の内容にも興味を持っており、調理師免許の本も読んだことがあります。知的好奇心は止められません。そういった自分の経験から、資格取得=転職とか、一見異分野の学習が無駄だとか思いません。それに、全てが仕事に役立つから問題だとは思えません。
 システム屋という職業に必要な知識は、情報技術だけだと思われがちです。ですが実際のところ、全ての知識が技術力を底上げしますし、間接的に関係してきます。これはシステム屋に限った話ではなく、人間の脳を鍛えることにより、全ての仕事の能率が上がりますし、他分野の仕事を知ることにより、チームワーク力を鍛える効果があります。そういったことから、時間の無駄だと決めつけるのは不適切だと思います。
 もう一つ私が思うのは、勤務時間外の他人の行動を監視し、それに対して白黒つけるのは、人として間違っているという事です。勤務時間以外でも仕事を持ち出してくる人は、他人に対する支配欲が強すぎます。日本ではこういった管理者が求められていましたが、そういった思想は仕事に硬直性をもたらし、逆に仕事の効率を下げます。また、変化に弱い組織の原因ともなります。何もよいことはありません。それに、短絡的に人を決めつけすぎです。他人に対して勝手な決めつけをするのはよくありません。
 インタビューに答えた人事担当者はごく普通の人であり、特別な存在ではなく、日本でありがちな意見の例にしかすぎません。ですから私は、そこに日本の病巣が垣間見えたという気がしました。
 日本の業務効率の悪さは有名です。それには色々な要因がありますが、そのうちの一つが、こうした短絡的な思い込みにあるのではないかと思います。よく、日本は無宗教だといわれていますが、私は常日頃、仕事で合理的でない宗教的行動をとっている気がします。精神論を全く無駄だと思いませんが、仕事には精神論だけでは通用せず、論理と合理性も必要です。
 以上のように、ニュースをもとにして、自分の環境を考えるのも一興だと思います。

テーマ : 気になるニュース
ジャンル : ニュース

マルチパラダイム時代におけるデータ構造 第6回 ー ユーザーストーリーを考えよう

 この記事は、「マルチパラダイム時代におけるデータ構造 第5回 ー 例外を活用しよう」の続きです。前回はテストの準備について解説しました。今回は、実際のテストの作り方について解説します。
 さて、いよいよテストを実装します。テストに対してマイナスイメージを持つ人が多いですが、よいテストは楽しく、プログラミングをやりやする効果があります。テスト実装のやり方を解説しますので、一緒にテストを作りましょう。
 有名なテストは単体テストで、メソッドごとに行うイメージがあるかと思います。単一メンバーごとに行うテストも時にはよいものですが、機械的で退屈になりがちですし、変化に弱く、想像力を刺激しません。テスト=単体テスト=単一メンバーという固定概念を捨て、ストーリー単位でテストを作りましょう。そうすれば、変化に強く、想像力を刺激するよいテストを作れます。
 ユーザーストーリーというと、なんだか難しく聞こえますが、本当は単純なものです。「貴方はデータ構造をどう使いますか?」という質問を自分にしてみるといいと思います。そうすれば、自然と発想が湧きます。最初「データ構造はどうあるべきか」と堅苦しく考えると、テストが思い浮かばなくなるものですが、どう自分が使いたいのか考えると、自然とテストのアイデアが浮かぶと思います。
 私がデータ構造に求めることは、データのライフサイクル(発生・更新・消滅)の面倒を見てくれることです。データを管理するオブジェクトなのですから、データのライフサイクルに対応する必要があるかと思います。そこで、最初に作るユーザーストーリーは、「1つのデータを作り消す。この過程で、データの個数は増え、データ構造内に作ったデータを選択できる。」です。これはごく簡単なストーリーであり、最初に作るテストとして適切だと思います。
 私が考えたユーザーストーリーを実装すると、次のようになります・・・

using System;
using DataStructure;

 //IDataManagerTest<T>を実装するオブジェクトに対するテスト。
abstract class IDataManagerTest<T>
{
    //要素数が正しいかチェックする。
    protected virtual void CountCheck(
        string testName,
        IDataManager<T> target,
        string methodName,
        int before,
        int rightValue )
    {
        if ( target.Count != rightValue )
            throw new TestException(
                testName,
                methodName +
                "メソッド実行後のCountプロパティの値が正しくありません。"
                + Environment.NewLine +
                "予想値:" + rightValue +
                " 実際の値:" + target.Count
                + Environment.NewLine +
                methodName +
                "メソッドとCountプロパティを確認してください。" );
    }

    //指定したデータが選択可能か否か判定する。
    protected abstract bool CanSelect(
        IDataManager<T> target,
        int count,
        T value );

    //発生したデータが選択できないときに行うエラー処理。
    protected void InsertSelectError(
        string testName,
        IDataManager<T> target,
        string methodName, //オーバーロードがあるから
        bool success,
        T value )
    {
        if ( !success ) {
            throw new TestException(
                testName,
                methodName +
                "メソッド実行後に発生したデータを選択できませんでした。"
                + Environment.NewLine +
                "選択できない値:" + value
                + Environment.NewLine +
                methodName +
                "メソッドとSelectメソッドを確認してください。" );
        }
    }

    //発生したデータが選択できないときに行うエラー処理。
    protected void DeleteSelectError(
        string testName,
        IDataManager<T> target,
        string methodName, //オーバーロードがあるから
        bool success,
        T value )
    {
        if ( success ) {
            throw new TestException(
                testName,
                methodName +
                "メソッド実行後に消滅したデータを選択できてしまいました。" +
                "選択値:" + value
                + Environment.NewLine +
                methodName +
                "メソッドとSelectメソッドを確認してください。" );
        }
    }

    //1つのデータを生成・消滅できる事を確認するテスト。
    protected void OneDataLifeCycleTest(
        IDataManager<T> target,
        T value )
    {
        string testName = "OneDataLifeCycleTest";
        int count = target.Count;
        OneDataInsertSubTest( target, value, testName, count );
        OneDataDeleteSubTest( target, value, testName, count );
    }

    //1つのデータを発生させるサブテスト。
    private void OneDataInsertSubTest(
        IDataManager<T> target, T value,
        string testName, int count )
    {
        target.Insert( value );
        this.CountCheck(
            testName: testName,
            target: target,
            methodName: "Insert",
            before: count,
            rightValue: ( count + 1 ) );
        bool success = this.CanSelect(
            target: target,
            count: 1,
            value: value );
        if ( !success ) {
            this.InsertSelectError(
                testName: testName,
                target: target,
                methodName: "Insert",
                success: success,
                value: value );
        }
    }

    //1つのデーターを消滅させるサブテスト。
    private void OneDataDeleteSubTest(
        IDataManager<T> target, T value,
        string testName, int count )
    {
        target.Delete();
        this.CountCheck(
            testName: testName,
            target: target,
            methodName: "Delete",
            before: count,
            rightValue: count );
        bool error = this.CanSelect(
            target: target,
            count: 1,
            value: value );
        if ( error ) {
            this.DeleteSelectError(
                testName: testName,
                target: target,
                methodName: "Delete",
                success: error,
                value: value );
        }
    }
}

本当は更新もテストしたかったのですが、IDataManager<T>にはUpdateメソッドがないのでやっていません。CanSelectメソッドが抽象的である理由は、データ構造ごとに選択の内容が違うからです。例えば、キューというデータ構造は先入先出で、スタックは後入れ先出しです。それに加えて、一度取り出したデータは消えてしまいます。そういった個々のデータ構造の個性を考慮しつつ、共通した部分を実装してテストプログラムの増加を防いでいます。
 あとは、コンスセル特有の部分を実装するだけです。

using System;
using DataStructure;

//コンスセルオブジェクトのテストを行う。
class ConsCellTest<T> : IDataFreeManagerTest<T>
{
    //指定したデータが選択可能か否か判定する。
    protected override bool CanSelect(
        IDataManager<T> target,
        int count,
        T value )
    {
        bool result = false;
        var temp = ( ConsCell<T> ) target;
        try {
            T returnValue = temp.Select( count );
            result = returnValue.Equals( value );
        } catch ( IndexOutOfRangeException ) {
        }
        return result;
    }

    //全てのテストを実行する。
    public void AllTestExecute(
        Func<ConsCell<T>> initFunc,
        T one )
    {
        this.OneDataLifeCycleTest( initFunc(), one );
    }
}

これでひとまず1つのユーザーストーリーが完成しました。このシンプルなテストに合格するように、オブジェクトを実装していきます。

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

マルチパラダイム時代におけるデータ構造 第5回 ー 例外を活用しよう

 この記事は、マルチパラダイム時代におけるデータ構造 第4回 ー 何はともあれテストをしよう!の続きです。前回は、テストの準備を行いました。今回も引き続き、テストの準備を行います。
 テストといえば、失敗がつきものです。というのも、テストファーストでは、最初に失敗してテスト環境をチェックするからです。バグが発生した後、テスト環境そのもののエラーと、テスト対象のエラーが混合してはなりません。それ故に、テスト環境をこまめに確認するのです。
 具体的な手順を説明します。テスト専用の例外を用意し、例外をキャッチするようにします。そして、テスト対象のメンバーの内容に間違いを仕込み、エラーメッセージが正しく表示されることを確認します。

using System;

namespace Test
{
    class TestException : Exception
    {
        //テストの名前。
        private string _testName;
        public string TestName
        {
            get { return this._testName; }
        }

        //インスタンスを生成する。
        public TestException( string testName, string errorMessage )
            : base( errorMessage )
        {
            this._testName = testName;
        }

        //エラーの内容をわかりやすくまとめる。
        public override string ToString( )
        {
            return
                "テスト名:" + this.TestName + Environment.NewLine +
                "内容:" + this.Message + Environment.NewLine;
        }
    }
}

using System;
using DataStructure;

namespace Test
{
    class TestProgram
    {
        static void Main( string[ ] args )
        {
            ConsCellTest();
        }

        //ConsCellに対するテストを行う。
        private static void ConsCellTest( )
        {
            Console.WriteLine(
                "ConsCellオブジェクトのテストを行います。"
                 + Environment.NewLine +
                "エラーメッセージが何も表示されなければテスト成功です。" );
            try {
                new ConsCellTest<int>().AllTestExecute();
            } catch ( TestException e ) {
                Console.WriteLine();
                Console.WriteLine( e.ToString() );
            }
            Console.WriteLine( "テスト終了。" );
            Console.WriteLine();
            Console.WriteLine();
        }
    }
}

始めのうちはこんな感じでOKです。テストもまたリファクタリングしながら作成するので、最初はシンプルです。ただし、現場ではテスト設計を行い、コーディング時に清書(テストをフルで記述)をする場合が多々あるので注意してください。やはり、学習と仕事は行動や手順が違います。
 これでテスト準備は終了です。次回から、テストの実装を行います。

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

マルチパラダイム時代におけるデータ構造 第4回 ー 何はともあれテストをしよう!

 この記事は、マルチパラダイム時代におけるデータ構造 第3回 ー 不変で自己記述的なデータ構造が原点の続きです。前回は、タプルを活用した、配列の元となるデータ構造について解説しました。今回は、コンセンスセルおよび、他のデータ構造を実装するためのテストを準備します。
 コンスセル(コンセンスセル)を実装する前に、テストを準備しなくてはなりません。テストファーストです。テストファーストは、面倒だという印象を持っている方がいるかもしれませんが、テストは非常に実装をはかどらせます。特にデータ構造のような、共通したインタフェースを持つオブジェクトが、たくさんある場合に効果を発揮します。この連載を読み進めれば、それが実感できると思います。
 ひとまずテストオブジェクトを用意します。

using System;
using DataStructure;

namespace Test
{
    //IDataManagerを、実装するオブジェクトに対するテスト。
    abstract class IDataManagerTest
    {
    }

    //IDataFreeManagerインタフェースを、実装するオブジェクトに対するテスト。
    abstract class IDataFreeManagerTest<T> 
        : IDataManagerTest
    {
    }

    class ConsCellTest 
        : IDataFreeManagerTest
    {
        //全てのテストを実行する。
        public void AllTestExecute( )
        {
        }
    }

    class TestProgram
    {
        static void Main( string[ ] args )
        {
            ConsCellTest();
        }

        //ConsCellに対するテストを行う。
        private static void ConsCellTest( )
        {
            Console.WriteLine(
                "ConsCellオブジェクトのテストを行います。" 
                + Environment.NewLine +
                "エラーメッセージが何も表示されなければテスト成功です。" );
            new ConsCellTest<int>().AllTestExecute();
            Console.WriteLine( "テスト終了。" );
            Console.WriteLine();
        }
    }
}

読みやすくするために一つにまとめましたが、別にテストプロジェクトを作ったうえで、各テストオブジェクトを別ファイルへ保存してください。
 テストプログラムが長いので次回へ続く...

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

マルチパラダイム時代におけるデータ構造 第3回 ー 不変で自己記述的なデータ構造が原点

 この記事は、「マルチパラダイム時代におけるデータ構造 第2回 ー 最も基本的なデータ構造は・・・」の続きです。前回は、最も基本的なデータ構造「タプル」について解説しました。今回は、タプルを活用した、配列の元となるデータ構造について解説します。
 タプルは非常に簡単ですが、簡単ゆえにどうやって配列などのデータ構造を構成するのか、すぐにはわからないと思います。その鍵は再帰構造にあります。データ構造というオブジェクトは、再帰構造が基本であり、再帰構造がわかるか否かが明暗を分けています。しかしながら、再帰構造がわかりにくいという人が多いも確かです。それで、タプルを使って、いきなり配列を実装できますが、それだと分かりにくいので、段階的に解説していきます。
 いくつものデータが並んでいる様子を思い浮かべてください。数値で例えると、こんな具合になっています。

1 2 3 4 5 ...

これを、タプルを使って表現すればどうなるでしょうか?ちょっと発想し難いと思いますが、「1とその他の数値」と考えればタプルで表現できます。この発想はLISP言語にもあります。そこで今回は、LISP言語から借りてきたものを実装します。

namespace DataStructure
{
    //2つの要素を管理するデータ構造。
    public class Tuple<T1, T2>
    {
        //最初の要素。
        private T1 _first;
        public T1 First
        {
            get { return this._first; }
        }

        //2番目の要素。
        private T2 _second;
        public T2 Second
        {
            get { return this._second; }
        }

        //2つの要素を指定し、インスタンスを生成する。
        public Tuple( T1 first, T2 second )
        {
            this._first = first;
            this._second = second;
        }
    }
}

namespace DataStructure
{
    //1つの要素とそれ以外の要素への参照を持つデータ構造。
    public class ConsCell<T>
    {
        //要素
        private Tuple<T, ConsCell<T>> _cons;

        // 最初の要素。
        //※プロパティ名はカーと読む。
        public T Car
        {
            get { return this._cons.First; }
        }

        //他の要素。
        //※プロパティ名はクダーと読む。
        public ConsCell<T> Cdr
        {
            get { return this._cons.Second; }
        }

        //格納する要素と次のセルを指定し、インスタンスを生成する。
        public ConsCell( T element, ConsCell<T> next )
        {
            this._cons = new Tuple<T, ConsCell<T>>( element, next );
        }

        //インスタンスの文字列表現を取得する。
        //※デバッグ時に役立つので実装しました。
        public override string ToString( )
        {
            System.Text.StringBuilder result =
                new System.Text.StringBuilder();
            ConsCell<T> cons = this;
            result.Append( "{ " );
            do {
                result.Append( cons.Car + " " );
                cons = cons.Cdr;
            } while ( cons != null );
            result.Append( "}" );
            return result.ToString();
        }
    }
}

タプルは前回実装しましたが、深い学習を行うために、いったん不変オブジェクトにします。ちなみに、carとcdrは古い計算機のレジスタの名前が由来だそうです。面白い省略名ですね。
 もしかしたら、再帰構造は邪道な印象を与えるかもしれませんが、データ構造とアルゴリズムの学習には、再帰は欠かせないのでこれを機会に習得しましょう。そうすれば、貴方のプログラミング力は飛躍的にアップすると思います。続く...

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

ニュースを分析第5回 ー 今最も熱い大阪都構想について考えよう

 今、関西で最も熱いのではないかと思う時事問題があります。それは、大阪都構想です。大阪に住んでいる以外の人には関係のない話題だと思われるでしょうが、実は国家システムの変換句を意味しており、日本全体の問題だといえます。それ故に、既得権益者達が必死で抵抗しており、一部報道機関がネガティブな報道や、誹謗中傷に近い報道を頻繁にしています。今回は、そんな大阪都構想について、論理的に分析していきましょう。
 肝心なのは、大阪都構想とは何かという事です。それは、公式ホームページを見ればわかります。以前このブログで、一部の専門家にしかわからない説明である点を指摘しました。ですが、現在はわかりやすくなっているので大丈夫だと思います。公式ホームページの説明はよくできているので、トップページの説明だけ見ればOKです。これで、概要がわかります。深く考えたいときは、経営学(主に組織論の部分)と管理会計の知識が必要となりますが、その点については私が補足説明します。
 大阪都構想の目標は、二重行政の解決だそうです。これについては、私は大賛成です。日本の国家システムは、資源を無駄に食う割にはセキュリティが0の酷いシステムです。その理由の一つとして、二重行政が挙げられます。それが解決できるのですから、既得権益者以外の国民が反対する理由がありません。システムとは、より効率的に動作するように、改善していくのが基本です。
 ここで問題となるのは、本当に大阪都構想が特効薬になるかという点です。結論を先に言うと、特効薬ではないもののやらなければならない事だと思います。国家システムのような大規模なシステムを改善する特効薬は存在しません。そんな魔法はそもそも存在せず、一歩々地道に改善していくしかありません。特効薬なんてあれば、最初から問題は発生していません。改善するためにするべきことがあります。そのうちで最初にするべきことは、大阪都構想で言及していることと一致しています。
 この事は専門的な知識を要するので軽く説明します。組織を効率化するためにすべきことは、権限のコントロールと、資源配分の見直しです。ここでいう資源とは、時間・お金・人・物です。日本国民ならば誰もが知っていると思いますが、日本政府は非常に遅いです。いまだに原発事故問題や年金問題などを解決できていません。それがいかに、時間・お金・人・物を浪費しているか同意してもらえると思います。その原因は、いつまでも会議をしている、日本の風習にあります。サラリーマンの皆様は実感していると思いますが、日本は何をするにしても、会議で物事が一向に進みません。おまけに、責任者が曖昧で、何か問題が起きても「想定外でした。すみません。」で終わってしまいます。これでは、何も出来ない現状も当然だといえるでしょう。
 論理的に考えるときは、反論も検討せねばなりません。大阪都構想は確かによくできていますが、鵜呑みは危険です。何事も自分の頭で考える姿勢が大切です。反論としては、「大阪府と大阪市を一本化しなくても、やる気があれば二重行政は解決できる。」と、「区割りは大阪を分裂させる」です。検討していきましょう。
 「大阪府と大阪市を一本化しなくても、やる気があれば二重行政は解決できる。」について論理的に考えてみます。確かに一理ありますが、それは性善説だといえるでしょう。何故ならば、それならば戦後70年近く、何故やる気にならなかったのか疑問に感じるからです。また、「やる気」云々で政治が左右されるのは、国民として不満であり、戦後70年近くに及ぶ責任はどうとるのか問いただしたくなります。この「やる気があればできる」理論が正しいのであれば、やる気がなかった歴代首長に責任をとってもらわなくてはなりません。その責任をとるシステムがない、ゆとり仕事の結果が現状の問題を生んでいるので、一理あるものの性善説を前提とした、現実にそぐわない意見だと思います。もし本気でいうのであれば、歴代やる気がなかった首長に責任をとってもらい、今後「やる気がなければ罰則」を設けるべきです。
 「区割りは大阪を分裂させる」については、非理論的すぎます。行政の仕組みが変わったからと言って、ベルリンの壁状態になりません。行政がどのような区割りをしても、大阪の市民は普通に他の区を通行できます。分裂を言うのであれば、大阪市と大阪府が分裂している現状を容認するのはおかしいです。ダブルスタンダードで、非常に非論理的だといえるでしょう。
 今のところ、説得力のある反論がなく、大阪都構想については、維新側が圧倒的に論理的に正しいといえます。こういうと、偏っていると誤解する人もいるでしょうが、反論が非理論的なので賛成以外の意見を言いようがないです。それは感情ではなく、論理学による結論です。といっても、維新の会全ての賛成というわけではありません。大阪都構想を前提としたうえで、いかに妥当であるか検証するべきだと思います。むろん、他に良い案があれば、そちらを支持しますが、無限に時間があるわけではないので、他に妙案がない以上、大阪都構想をやるしかありません。
 日本人はどうも「完璧ではない」と新しいことを反対する体質があります。しかしながら、そもそも完璧な術は存在しません。いつまでも考えていれば、日本が崩壊するだけです。状況は常に変化するので、「今やるべきことをやる」のが基本であり、「全国民が賛成する案を考える」なんて御伽噺のような意見を鵜呑みにしてはなりません。現実を直視し、常に改善していく。それが人類の歴史であり、「新しいことは何でも反対する」今の風潮がおかしいのです。これは精神的な引きこもりであって、大人がすることではありません。現実を直視できる大人になれか否か、それもまた大阪都構想の隠れたテーマだといえるでしょう。
 今回は大阪都構想について書きました。正直にいえば、「まだその程度しか話が進んでいないか」と思いました。日本は既得権益者の引きこもりが酷すぎて、物事が前に進みません。また、既得権益者の言葉を鵜呑みにして、もしくは利権がらみで報道する傾向が強すぎます。戦後70年近く、何もしてこなかったといえるでしょう。日本人として非常に情けないと感じてなりません。もっと、論理的に考えられる進行の速さ(内容の深さ)を私は望みます。おそらく、日本人の大半は、「何もやらない」と認識していると思います。ですが、「何も変わらない」と何も考えない日本国民もまた「何もしない人」です。ですから、私たち国民がこういったニュースを真面目に考えないとならないと私は思います。

テーマ : 政治・時事問題
ジャンル : 政治・経済

マルチパラダイム時代におけるデータ構造 第2回 ー 最も基本的なデータ構造は・・・

 この記事は、「マルチパラダイム時代におけるデータ構造 第1回 ー データ構造はインタフェースの定義から入ろう」の続きです。前回は、データ構造を実装するうえで、1番最初にするべきことについて解説しました。今回は、最も基本的なデータ構造について解説します。
 では、張り切ってデータ構造を実装していきましょう♪最も基本的で、最初に実装するべきデータ構造とは何でしょうか?それは・・・タプル(組)です。
 色々なデータ構造があります。それらのデータ構造を実装するときに必要なのは配列です。しかしながら、配列もデータ構造であり、.NETライブラリにある配列(System.Array)を使用するわけにはいきません。それを認めてしまうと、データ構造はライブラリを使えの一言で終わってしまいます。データ構造を実装することにより、学習したいのですから、配列の使用は禁止とし、文法と基本型しか使用できないというルールにします。
 そうすると、配列はどのようにして実装するのかという問題が浮上してきます。アンセーフモードを使用すれば、C言語風の配列は作れるのですが、それだとライブラリの配列の使用を認めたのと同じなのでアンセーフモードも禁止します。そうすると、配列を作るためのデータ構造が必要となります。それがタプルです。
 タプルがデータ構造だと知らない人もいますが、タプルは立派なデータ構造です。タプルがなければ、データ構造を実装できません。何故ならば、1つのオブジェクトだけしか使用できないことになるからです。わかっていただけたと思いますので、タプルの実装をしましょう。
 念のために書きます。タプルというのは、2つのデータを管理するデータ構造です。実装は非常に簡単で、説明が要らないほどです。

namespace DataStructure
{
    //タプル(組)。
    public class Tuple<T1,T2>
    {
        //最初の要素。
        private T1 _first;
        public T1 First
        {
            get { return this._first; }
            set { this._first = value; }
        }

        //2番目の要素。
        private T2 _second;
        public T2 Second
        {
            get { return this._second; }
            set { this._second = value; }
        }

        //インスタンスを生成する。
        public Tuple( )
        {
        }
    }
}

これ以上ないぐらい簡単です。2つのデータを管理したいので、2つのプロパティを用意する。ただそれだけのデータ構造です。しかしながら、タプルを馬鹿にしてはなりません。非常に強力な力を持っています。その強力さは、次回以降明らかになります。

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

マルチパラダイム時代におけるデータ構造 第1回 ー データ構造はインタフェースの定義から入ろう

 この記事は、「マルチパラダイム時代におけるデータ構造 第0回 ー この連載の目的」の続きです。前回はこの連載の目的を書きました。今回から、データ構造の解説を始めます。
 マルチパラダイム言語でデータ構造を実装する際に、先ずやるべきことは、インタフェースを定義する事です。C#は前時代の技術であるCOM(Component Object Model)の流れも汲んでいるので、インタフェースにも対応しています。それに、巷ではあまり言われていませんが、インタフェース指向も重要な考えであって、何でもオブジェクトにすればよいというものではないのです。データ構造は、実装上関係のないオブジェクトの集まりです。無暗に継承してよいものではありません。データ構造のインタフェースから考えましょう。
 データ構造に、どんなインタフェースが必要でしょうか?それは、データ構造とは何かを考えると見えてきます。データ構造には、スタック、キュー、リストなどがあります。それらデータ構造に共通するものは、データを管理する事です。データ構造は、データを管理し取扱いしやすいようにするものだといえると思います。
 では、データを管理するオブジェクトには、どういったインタフェースが必要でしょうか?それは、データ指向で考えると分かります。データは、発生・更新・消滅します。また、データは選択(取得)できなければ意味がありません。加えて、本質的ではないものの、プログラミング上では管理しているデータの個数が必要となります。これらの要件を満たす最小限のインタフェースを考えれば、データ構造が持つべきインタフェースが判明します。
 インタフェースの定義が決まったので、実際にプログラミングしてみましょう。

using System;

namespace DataStructure
{
    //データを管理するオブジェクト(データ構造)が持つべきインタフェースの定義。
    public interface IDataManager<T>
    {
        //データの個数。
        int Count { get; }

        //データを選択(取得)する。
        //※選択するのは最初のデータであることに注意。
        T Select( );

        //データを発生させる。データを追加する。
        void Insert( T value );

        //データを消滅する。データ1つ削除する。
        void Delete( );
    }
}

おや?先ほど言った、データのライフサイクルである、更新がありません。それは、「最小限のインタフェース」ではないからです。何故ならば、任意の位置のデータを操作する必要があるからです。先で判明しますが、任意の位置のデータを操作できないデータ構造があるので、位置指定が必要なインタフェースは定義できません。任意の位置のデータを操作するインタフェースは、基本であるIDataManagerを継承することにより行います。

using System;

namespace DataStructure
{
    //任意の位置のデータを操作できるオブジェクトが、持つべきインタフェースの定義。
    public interface IDataFreeManager<T> : IDataManager<T>
    {
        //任意の位置のデータを選択(取得)する。
        T Select( int index );

        //任意の位置へデータを発生させる。
        void Insert( T value, int index );

        //任意の位置のデータを更新する。
        void Update( T newValue, int index );

        //任意の位置のデータを1つ消滅する。
        void Delete( int index );
    }
}

実は他にも持つべきインタフェースのメンバーがあるのですが、いきなりすべてを記述すると混乱を生むので、適切なタイミングで解説します。なお、ジェネリックインタフェースである理由は、任意のオブジェクトを格納する必要があるからです。int型だけしか管理できない何て嫌ですよね。

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

プロフィール

インドリ

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カウンター