nomurabbitのブログ

nomurabbitのブログはITを中心にした技術ブログです。

nomurabbitのブログ

C#のDisposeメソッドとデストラクタが呼び出されるのはいつ?

<はじめに>
IDisposableインターフェースを実装したクラスを使用する場合、
using ステートメントを使ってリソースの解放を担保します。
そこで気になるのがDisposeメソッドとデストラクタの呼び出されるタイミング。

違うの?

一緒なの?

そこで、調べてみました。


<プログラム1>
■Dispose、デストラクタ呼び出しテスト

class Program
{
    static void Main(string[] args)
    {
        using (DisposeTest dTest = new DisposeTest())
        {
            Console.WriteLine("usingステートメントに入ったよ。");
        }
        Console.WriteLine("usingステートメントにから出たよ。");
    }
}

class DisposeTest : IDisposable
{
    ~DisposeTest()
    {
        Console.WriteLine("デストラクタが呼びだされたよ。");
    }

    public void Dispose()
    {
        Console.WriteLine("Disposeが呼びだされたよ。");
    }
}


<実行結果1>


usingステートメントを抜けたタイミングでDisposeメソッドが呼び出されるのは
想像通りだったのですが、デストラクタが呼び出されるのはもっと後なんですね。
ちょっと調べてみると下記のような記述が…


デストラクタ (C# プログラミング ガイド)
デストラクタがいつ呼び出されるかはガベージ コレクタによって決定されるため、プログラマは制御できません。


ん?ってことはGC呼び出せばそのタイミングでデストラクタ呼ばれるんじゃ…?
そう思ってプログラムを下記のように修正


<プログラム2>
■Dispose、デストラクタ呼び出しテスト_GC呼び出しver

class Program
{
    static void Main(string[] args)
    {
        using (DisposeTest dTest = new DisposeTest())
        {
            Console.WriteLine("usingステートメントに入ったよ。");
        }

        Console.WriteLine("usingステートメントにから出たよ。");

        // 下記2行を追加
        GC.Collect();
        Console.WriteLine("GCを実行したよ。デストラクタは呼び出されてるよね?");
    }
}

class DisposeTest : IDisposable
{
    ~DisposeTest()
    {
        Console.WriteLine("デストラクタが呼びだされたよ。");
    }

    public void Dispose()
    {
        Console.WriteLine("Disposeが呼びだされたよ。");
    }
}


<実行結果2>


<まとめ>
思ってたタイミングと違う!

やっぱりmsdnに書いてある通り、プログラムからは制御出来ませんでした。
正直デストラクタ使ってどうこうする機会はあまりないのですが、
興味本位で調べてみた結果は以上です。

MySQLからDBIx::Classで取ってきたデータをJavascriptで取得してみよう!

<はじめに>
3層構造かつmemcachedの練習です。
1)MySQLに格納されているデータをDBIx::Classで取得します。
2)無意味に一旦memcachedに格納した後に取り出してみます。
3)XML::SimpleやらJSONやらで加工してみます。
4)1)〜3)で作ったデータをjavascriptで取得します。


<プログラムについて>
[サーバー側]
DBIx::ClassMySQLにアクセス!

 10 #---------------------------------------------------------------------------- 
 11 # get Data from MySQL 
 12 #
 13 my $schema = MyApp::Schema->connect('dbi:mysql:ASUKA','user','pass');
 14 
 15 $schema->storage->dbh->do("SET names utf8");
 16 
 17 my @all_TODOFUKEN    = $schema->resultset('MDTDFKN')->all;
 18 my @tempHashArray    = ();
 19 
 20 foreach $todofuken (@all_TODOFUKEN)
 21 {
 22     my %tempHash = ('TODOFUKEN_ID'       => decode("utf8", $todofuken->TODOFUKEN_ID)       ,
 23                     'TODOFUKEN_MEI'      => decode("utf8", $todofuken->TODOFUKEN_MEI)      ,
 24                     'TODOFUKEN_MEI_KANA' => decode("utf8", $todofuken->TODOFUKEN_MEI_KANA));
 25     push(@tempHashArray, \%tempHash);
 26 }

■無意味にmemcachedに出し入れしてみる

 28 #----------------------------------------------------------------------------
 29 # use Memcached
 30 #
 31 my $key     = "hash";
 32 my $value   = \@tempHashArray;
 33 my $expires = 3600;
 34 my $memcached = Cache::Memcached->new(
 35                 {
 36                     servers => ["127.0.0.1:11211"],
 37                     compress_threshold => 10_000
 38                 });
 39
 40 $memcached->add($key, $value, $expires);
 41 
 42 my $tempData = $memcached->get($key); 
 43 my @tempData = @$tempData;

XMLっぽく加工(XMLについては著しく勉強不足…)

 50 #----------------------------------------------------------------------------
 51 # Make XML data
 52 #
 53 print "\n\nXML DATA---------------------------------------------------\n\n";
 54 
 55 my $xs       = XML::Simple->new();
 56 
 57 foreach $_ (@tempData)
 58 {
 59     my $item = $xs->XMLout($_);
 60 
 61     print encode("utf8", $item);
 62 }

■JSON形式に加工

 64 #----------------------------------------------------------------------------
 65 # Make JSON data
 66 #
 67 print "\n\nJSON DATA--------------------------------------------------\n\n";
 68 
 69 foreach $_ (@tempData)
 70 {
 71     my $item = encode_json($_);
 72 
 73     print $item, "\n";
 74 }

[クライアント側]
Javascriptでサーバー側からデータを取得

  7 httpRequest = false;
  8 httpRequest = new XMLHttpRequest();
  9 httpRequest.overrideMimeType('text/xml');
 10 
 11 function request()
 12 {
 13     httpRequest.abort();
 14     httpRequest.open("GET","http://hogehoge/fuga.cgi",true);
 15     httpRequest.onreadystatechange = function()
 16     {
 17         if(httpRequest.readyState == 4)
 18         {
 19             if(httpRequest.status == 200)
 20             {
 21                 window.alert("Success!!");
 22                 document.getElementById('pageText').value = httpRequest.responseText;
 23             }
 24         }
 25     }
 26     httpRequest.send(null);
 27 }


<まとめ>
DBIx::Class使ってみると、データ取得した後にDBのカラム名をキーに
hashを作りたくなるんだけど、べた書きだとあんまりかっこよくない。
カラム名のデータはどこからか取れないのかなー?なんて思ったり。
今回はそこまでやりませんでしたけど、javascriptでデータ取得した後に
XMLなりJSONなりをどうやって加工しようかな?なんて思ったり。
このネタでもう少し遊べそうです。

F# で熱伝導の数値解析を頑張ってみた

F# Advent Calendarの18日目です。

F# Advent Calendar 2012 : ATND

さて、F#で何かしてみよう!

ということで、

今回は熱伝導の数値解析(のようなも)をしてみました!


<はじめに>

数値解析をするにあたり、できるだけ簡素化して考えてみました。

ざっくりとした仮定はこんな感じ。

1)対象の範囲は20 * 20の二次元配列とする

2)対象の範囲の3辺は壁、残る1辺を熱源とする

3)壁と熱源以外の要素の値はその要素の上下左右の要素の平均とする


<プログラムについて>

文字にすると仮定がわかりにくいですが、プログラムの流れとしては下記の通り

1)初期値の配列を生成する

2)関数に配列を渡し、仮定3)に基づき計算の上新しい配列を返す

3)2)の繰り返し

実際のソースの一部を以下に示します。


■初期値の作成(※ 熱源は9、壁、その他の要素は0とします。)

//----------------------------------------------------------
// 定数の定義
//
let width  = 20
let height = 20

//----------------------------------------------------------
// 熱源・壁を定義するメソッド
//
let heatSource (tempHeatLevel : int) (tempWidth : int) = Array.create tempWidth tempHeatLevel

let heatSource_heatLevelIs_0 (tempWidth : int) = heatSource 0 tempWidth
let heatSource_heatLevelIs_9 (tempWidth : int) = heatSource 9 tempWidth

//----------------------------------------------------------
// 配列の初期化メソッド
//
let rec initializeArray (tempWidth : int) (tempHeight : int) =
  if tempHeight = 1
    then
      heatSource_heatLevelIs_9 tempWidth
  else
      Array.append (initializeArray tempWidth (tempHeight - 1)) (heatSource_heatLevelIs_0 tempWidth)


■引数の配列から新しい配列を生成して返す(メインの処理)

//----------------------------------------------------------
// メインの演算メソッド(tempIndexには(array.Length - 1) - width * 2 を指定)
//
let rec getResultArray (tempWidth : int) (tempIndex : int) (tempArray : int[]) =
  if tempIndex = 0 
    then
      [|0|]
  elif ((tempIndex % tempWidth) = 0) || ((tempIndex % tempWidth) = (tempWidth - 1))
    then
      Array.append
        (getResultArray tempWidth (tempIndex - 1) tempArray)
        [|0|]
  else
    if ((double tempArray.[tempIndex + tempWidth + 1] +
         double tempArray.[tempIndex + tempWidth - 1] +
         double tempArray.[tempIndex + tempWidth * 2] +
         double tempArray.[tempIndex]                 +
         2.0) / 4.0 * rateOfHeatCondactor) < 9.0
      then
        Array.append
          (getResultArray tempWidth (tempIndex - 1) tempArray)
          [|int (Operators.round (double tempArray.[tempIndex + tempWidth + 1] +
                                  double tempArray.[tempIndex + tempWidth - 1] +
                                  double tempArray.[tempIndex + tempWidth * 2] +
                                  double tempArray.[tempIndex]                 +
                                  2.0) / 4.0 * rateOfHeatCondactor)|]
    else
        Array.append
          (getResultArray tempWidth (tempIndex - 1) tempArray)
          [|9|]

//----------------------------------------------------------
// メインの演算結果に境界条件を追加するメソッド
//
let getResultArrayWithBoundaryCondition (tempFiestRow : int[]) (tempLastRow : int[]) (tempArray : int[]) =
  Array.append
    tempFiestRow
    (Array.append
       tempArray
       tempLastRow)

let getResultArrayWithBoundaryCondition_firstRowIs_9_lastRowIs_0 (tempArray : int[]) =
  getResultArrayWithBoundaryCondition (heatSource_heatLevelIs_9 width) (heatSource_heatLevelIs_0 width) tempArray


■メインの処理の繰り返し

let rec getResult (tempLoopCount : int) (tempArray : int[]) =
  if tempLoopCount = 0 
    then
      getResultArrayWithBoundaryCondition_firstRowIs_9_lastRowIs_0 (getResultArray width (tempArray.Length - 1 - width * 2) tempArray)
  else
      getResultArrayWithBoundaryCondition_firstRowIs_9_lastRowIs_0 (getResultArray width (tempArray.Length - 1 - width * 2) (getResult (tempLoopCount - 1) tempArray))


■このほかにも熱伝導率的なパラメータなんかも付け加えています。


<結果>

処理の結果をまとめるとこんな感じになります。

■5回処理後

■10回処理後

■20回処理後

■30回処理後

処理を繰り返すにつれて、熱源(底辺)から熱が伝っていくのがなんとなくわかりますでしょうか?


<まとめ>

普段業務では一切F#は書かないので、

AdventCalenderはF#で遊ぶとてもいい機会になりました。

慣れていない道具を使うのは大変ですけど、

いろいろ調べながらプログラムを書く過程もまた楽しいですね。

ますますF#の魅力にとりつかれそうです。

私の記事は以上となります。

明日のカレンダーはigetaさんです!

楽しみですねー!

Perlでデータを自作クラスの配列として扱う話

これまでの記事でやったようなことをPerlでやってみた。

多分こんな感じかな?という手探り感満載のコードになりましたが、

やってることは以下

1)ファイルの読み込み各行を配列に格納

2)配列に格納された各行をtabで分割し、データ格納用クラスに格納

3)データ格納用クラスの配列を返し表示


<test.pl>

#!perl\bin\perl

use Encode;

################################################################
# DataClassの定義
#
package DataClass;

#---------------------------------------------------------------
# コンストラクタ
#
sub new
{
	my $this = shift;
	my $args =
	{
		ISBN10 => shift,
		ISBN13 => shift,
		TITLE  => shift
	};
	bless $args, $this ;
}

#---------------------------------------------------------------
# アクセサメソッド
#
sub GetIsbn10
{
	my $this = shift;
	return $this->{ISBN10};
}

sub GetIsbn13
{
	my $this = shift;
	return $this->{ISBN13};
}

sub GetTitle
{
	my $this = shift;
	return $this->{TITLE};
}

################################################################
# MethodClassの定義
#
package MethodClass;

use Encode;

#---------------------------------------------------------------
# コンストラクタ
#
sub new
{
	my $this = shift;
	my $args =
	{
		PATH => shift
	};
	bless $args, $this;
}

#---------------------------------------------------------------
# アクセサメソッド
#
sub GetPath
{
	my $this = shift;
	return $this->{PATH};
}

#---------------------------------------------------------------
# クラスメソッド
#
sub GetDataList
{
	# 初期化、変数の定義
	my $this            = shift;
	my $filePath        = "<" . $this->{PATH};
	my @dataList;
	
	open FH, $filePath;
	foreach(<FH>)
	{
		my $str = decode("utf8", $_);
		push(@dataList, $str);
	}
	close FH;

	return @dataList;
}

sub GetCommonDataList
{
	# 初期化、変数の定義
	my $this     = shift;
	my @dataList = $this->GetDataList;
	my @commonDataList;
	
	foreach(@dataList)
	{
		my @tempCommonData = split(/\t/, $_);
		push(@commonDataList, new DataClass($tempCommonData[0], $tempCommonData[1], $tempCommonData[2]));
	}

	return @commonDataList;
}

################################################################
# MainClassの定義
#
package main;

#--------------------------------------------------------------
# 変数の定義
#
my $path = "C:\\test.txt";

#--------------------------------------------------------------
# インスタンスの生成
#
my $method         = new MethodClass($path);
my @commonDataList = $method->GetCommonDataList;

#--------------------------------------------------------------
# データ出力
#
foreach(@commonDataList)
{
	print encode("shiftjis", $_->GetTitle . "\t" . $_->GetIsbn10 . "\t" . $_->GetIsbn13 . "\n");
}

こんな感じで書けました!

書きやすさでいえば

C# > F# >> Perl

個人的な慣れもあると思いますけどね!

F#でListの形でデータを扱ってみた

最近マイブームのF#のデータの扱いについて、

自作クラスの配列に格納する方法について試してみました。

やってることは以下の通り。

1)ファイルからtsvのデータを読み込んで各行をListに格納する

2)Listに格納された各行をtabで区切り自作クラスのListに変換する

3)自作クラスのListの指定した要素を標準出力に出力する


<File1.fs>

module File1

open System
open System.IO

//----------------------------------------------------------
// 定数の定義
//
let path = @"C:\test.txt";

//----------------------------------------------------------
// データ格納クラスの定義
//
type BookData = class
  val ISBN10     : string
  val ISBN13     : string
  val TITLE      : string

  new(isbn10, isbn13, title) = 
    {
      ISBN10 = isbn10
      ISBN13 = isbn13
      TITLE  = title
    }

  end;;

//----------------------------------------------------------
// サブルーチンの定義
//
let println (x : BookData) = printfn "%s" x.TITLE

let readTextFile (x : string)    = File.ReadLines(x) |> Seq.toList
let splitTextRow (x : string)    = x.Split([|'\t'|])
let makeBookData (x : string []) = new BookData( x.GetValue(0).ToString() , x.GetValue(1).ToString(), x.GetValue(2).ToString())

//----------------------------------------------------------
// メインの処理
//
let result3 =
  List.map makeBookData
    (List.map splitTextRow
      (readTextFile path))    

List.iter println result3

F#はマルチパラダイム言語なので、基本的にはOOPの考え方でコードは書けます。

(そうでないと僕の場合困ります。)

ただ、データの処理においては関数型プログラミングの書き方ができるので、

ずいぶん読みやすく、スッキリと書くことができます。

C#のListのLINQの話

昨日Windows Phone 7 ハッカソン in 名古屋に参加してきました。

ハッカソンで作成したアプリケーションは簡易的な書籍の検索システムです。

昨日の段階では、ひとまずタブ区切りのテキストデータをデータソースとしました。

アプリケーションでやってることは以下の通り。

1)データ格納クラス、処理クラスを作成

2)処理クラスのコンストラクタでデータソースからデータを読み込み、
  データ格納クラスインスタンスのリストを作成

3)処理クラスはデータ複数の検索メソッドを持ち、各検索メソッドはパラメータごとに
  データ格納クラスインスタンスのリストからLINQで対象データを抽出する

たいしたことは一切やっていないのですが、2)のようなデータの扱い方は

データソースが何であれ応用できるのでなかなか便利だと思います。

備忘録の意味も込めてソースコードの一部を紹介します。


<処理クラスのコンストラクタ

//-------------------------------------------------------------------
// ファイルを読み込んでCommonDataクラスの配列を作成するメソッド
//
private void SetCommonDataList()
{
    // データリストの初期化
    commonDataList = new List<CommonData>();

    // リソースの準備
    var uri                       = new Uri(PATH, UriKind.Relative);
    StreamResourceInfo streamInfo = Application.GetResourceStream(uri);
    Stream stream                 = streamInfo.Stream;
    StreamReader dataReader       = new StreamReader(stream);

    // ヘッダ読み込み
    string header = dataReader.ReadLine();

    // データ読み込み
    while(dataReader.Peek() >= 0)
    {
        string buff = dataReader.ReadLine();

        string[] buffs = buff.Split('\t');

        CommonData tempCommonData = new CommonData();

        tempCommonData.isbn10     = buffs[0];
        tempCommonData.isbn13     = buffs[1];
        tempCommonData.title      = buffs[2];
        tempCommonData.shuppansha = buffs[3];
        tempCommonData.chosya1    = buffs[4];
        tempCommonData.chosya2    = buffs[5];
        tempCommonData.mainTag    = buffs[6];
        tempCommonData.subTag     = buffs[7];
        tempCommonData.gaiyou     = buffs[8];
        tempCommonData.biko1      = buffs[9];
        tempCommonData.biko2      = buffs[10];

        commonDataList.Add(tempCommonData);
    }
}

<検索メソッド>

//-------------------------------------------------------------------
// CommonDataクラスの配列を返すメソッド(すべて)
//
public List<CommonData> GetCommonDataList()
{
    return this.commonDataList;
}

//-------------------------------------------------------------------
// CommonDataクラスの配列を返すメソッド(メインタグ)
//
public List<CommonData> GetKomokuList()
{
    List<CommonData> response = new List<CommonData>();

    var q = from x in commonDataList group x by x.mainTag;

    foreach (var item in q)
    {
        response.Add(new CommonData(item.Key));
    }

    return response;
}

//-------------------------------------------------------------------
// CommonDataクラスの配列を返すメソッド(タイトルの一覧)
//
public List<CommonData> GetShosaiList(string mainTag)
{
    List<CommonData> response = new List<CommonData>();

    var q = from x in commonDataList where x.mainTag == mainTag select x;

    foreach (CommonData item in q)
    {
        response.Add(item);
    }

    return response;
}

//-------------------------------------------------------------------
// CommonDataクラスの配列を返すメソッド(詳細)
//
public List<CommonData> GetShosaiData(string isbn)
{
    List<CommonData> response = new List<CommonData>();

    var q = from x in commonDataList where x.isbn10 == isbn select x;

    foreach (CommonData item in q)
    {
        response.Add(item);
    }

    return response;
}

と、いうことで何が言いたいかというと、

なんかしらのデータソースから取得してきたデータをhogehogeしたいときは

自作したデータ格納用のクラスのリストにつっこんでLINQで処理すると楽だよね!

ってことでした。

Windows Phone 7 ハッカソン in 名古屋も実に楽しゅうございました♪


Windows Phone 7 ハッカソン in 名古屋(10/13)
http://atnd.org/events/32639