MySQL で LINQ を使う

このブログの MySQL の記事をまとめていると「あれ?意外に少ない」と気が付いて、ADO.NET 絡みでMySQLを扱うTipsでも追加しようかと探していたところなのですが、なんと、LINQ で MySQL を使えることを発見。と言いますか、「至高の技」の執筆中に確認しなかったのが痛恨の一撃。

MySQL :: MySQL 5.1 Reference Manual :: 20.2.4.5 Tutorial: Using an Entity Framework Entity as a Windows Forms Data Source
http://dev.mysql.com/doc/refman/5.6/en/connector-net-tutorials-entity-framework-winform-data-source.html

どうも、日本語の情報が少ないなぁと思っていたら、該当するマニュアルは英語版しかないみたいですね。mysql linq で検索するあまり日本語のページは引っ掛かりません。

ざっと、英語のマニュアルを読めば分かるのですが、手順は非常に簡単です。SQL Server を LINQ で使うのと同じように、MySQL も DataAdapter 経由で LINQ を使うことができます。.NET Framework v4.0 からは、ここの ADO.NET 絡みが大きく変わっているので、かなり楽になっています。

という訳で、簡単に手順など。C# でサンプルコードを書いていますが、VB でも同じことができます。

■ADO.NET Data Entity Model を作成

0. 最初に MySQL :: Download Connector/Net から、Connector/Net 6.4.4 をダウンロードして、インストールしておきます。

1.「新しい項目の追加」で「ADO.NET Entity Data Model」を追加します。

2.「データベースから生成」で「次へ」

3.「新しい接続」ボタンをクリック

4.「変更」をクリック

5.「MySQL Database」をクリックして「.NET Framework Data Prodvider for MySQL」を選択。

6.「接続のプロパティ」で、サーバー名などを設定

7.「詳細設定」ボタンをクリックしたあと、「Allow Zero Datetime」を「True」、「Convert Zero Datetime」を「Ture」」に変更しておく。
これは、MySQL の Datetime は「0000-00-00 00:00:00」が許されるのですが、.NET の Datetime は「1900-01-01」あたりが最小値なためにエラーがでるため、これを自動でコンバートします。
「Unable to convert MySQL date/time value to System.DateTime」という例外が発生したら、ここを設定してみてください。

8.後は、テーブルを選択してエンティティクラスを自動生成します。ここでは、wordpress のテーブルから作成しています。


ここまで出来れば普通にSQL Sever で使う時のように LINQ が使えます。どうやら、sqlite も同じパターンなので、後で試して見る予定です。

■LINQ で検索する

DataGridView を使って、単純に一覧を表示した例です。

private void button1_Click(object sender, EventArgs e)
{
	// 単純に post テーブルの内容を表示
	wordpressEntities ent = new wordpressEntities();
	var posts = from t in ent.wp_posts select t;
	dataGridView1.DataSource = posts;
}

 

公開されている記事だけを検索する例です。

private void button2_Click(object sender, EventArgs e)
{
	// 公開している記事一覧を表示
	wordpressEntities ent = new wordpressEntities();
	/*
		select id, post_date, post_title, post_status, post_type from wp_posts
		where post_status = 'publish' and post_type = 'post'
		order by post_date desc
		*/
	var posts = from t in ent.wp_posts
				where t.post_status == "publish"
				&& t.post_type == "post"
				orderby t.post_date descending
				select new { t.ID, t.post_title, t.post_date };
	// バインド
	dataGridView1.DataSource = posts;
}

 

そんな訳で、LINQ to MySQL(という名前ではないけれど)は簡単に利用できます。wordpress などの oss の場合には mysql が使われていることが多いので、解析したり修正したりするときには勢い php になってしまうんですよね。自分が python あたりが使えるようになると、また別なんでしょうが、どうせならば .NET 関係のツールで揃えられれば windows から扱うのも手軽になるかなと。特に gui は作り易いですからね。

簡単なデータ更新の話とか、統計用の SlimStat のテーブル調査なんかは後ほど時間を取ってやっていこうと思います。SlimStat は、CakePHP から SlimStat へアクセスする(準備) | Moonmile Solutions Blog で準備だけして、頓挫しているので .NET のチャートが使えると結構便利なので。

カテゴリー: C#, MySQL | MySQL で LINQ を使う はコメントを受け付けていません

ひと目でわかるAzureにゃんの準備メモ

久し振りに、.NETラボ 勉強会 2012年01月 で発表をします。で、前々回(行ってないのだけど)も、大和屋氏による発表があったので、.NETラボ 勉強会 2011年11月 それとダブらないように…と思ったけど、先方はインフラとしての Windows Azure の話なのでダブらないですね。私のほうは、開発として Windows Azure の話をします。

主な内容としては、手元の「ひと目でわかる Azure アプリケーション入門」の原稿からピックアップするのと、実際執筆中に起こった落とし穴(まだ校正中で、落とし穴がちらほら)あるので、それをまとめていく予定です。

落とし穴の話は、ぼちぼちこちらのブログにも並べていくと思うのですが、少しメモ書きとして。

  • Windows Azure SDK の日本語版が、非常に簡単に見つけらる件(以前は、探し回らないと駄目だったのですが、Web Platform Installer からインストールできるようになっている)。
  • Tools v1.6 では、web.config の session の項目が直っている(v1.4 の時は、ビルド→デプロイが動かないという)
  • Web ロールを作るときに、ログインを含むマスターページが作られる(ログイン機能が実装されていないので、これは不要…というか、難しいところ)。
  • セッション情報、ログイン情報絡みは、SQL Azure を使わずにテーブルストレージを使いたいところ(所見だけど、Windows Azure だけで閉じておきたい)。
  • ローカルでエミュレーターを動かしたときに、テーブルストレージをリセットする方法(エミュレーターのサービスをリセットすればOK)。
  • Windows Azure 上で、テーブルストレージのリセット(これは、ストレージを再作成するほうが早い)
  • SSMS で、SQL Azure 用のスクリプトを吐き出せる件(SQL Server Management Studio をアップデートする必要あり)。
  • SQL Azure 上では、日本語は nvarchar, nchar を使う(ローカルで作るときは、nvarcharあまり使わない?のだが、SQL Azure上では必須。業務アプリのテーブル設計/移行時に重要)。

のような形で、開発者視点でピックアップしていきます。基本、アプリ開発自体は ASP.NET アプリケーションを作る時と変わらないので、修得は楽なんでしょうが、デバッグやらストレージの設計(特に、テーブルストレージを使う場合)をするときにはまりそうな感じがしてます。

カテゴリー: Azure | ひと目でわかるAzureにゃんの準備メモ はコメントを受け付けていません

キャッシュを作ってDataTableのアクセスを高速にする(2)

データベースアクセスのパフォーマンスチューニング例 | Moonmile Solutions Blog
http://www.moonmile.net/blog/archives/2423
キャッシュを作ってDataTableのアクセスを高速にする | Moonmile Solutions Blog
http://www.moonmile.net/blog/archives/2780

の続きで、複数キー対応のDictonaryを作成してキャッシュを有効に使う例をば。
つまりは、「Dictonary」のような形で、2つの int型のキーを持っていて、それから ProductRow オブジェクト(Productテーブルの列オブジェクト)を取ってくる。

■利用方法

まずは、使い方を。

/// <summary>
/// Product のキャッシュ
/// </summary>
private ProductCache pcache = null;
/// <summary>
/// 複数キーのキャッシュ実行
/// </summary>
/// <param name=&quot;sender&quot;></param>
/// <param name=&quot;e&quot;></param>
private void button2_Click(object sender, EventArgs e)
{
	if (cache == null)
	{
		SqlConnection cn = new SqlConnection(CNSTR);
		SqlDataAdapter da = new SqlDataAdapter(&quot;SELECT * FROM PRODUCT&quot;, cn);
		DataTable dt = new DataTable();
		da.Fill(dt);

		// キャッシュに設定
		Product product = new Product();
		DataBind.Conv(dt, product);
		this.pcache.Fill(product.Rows);
	}
	int id = int.Parse(textBox1.Text);
	int subid = int.Parse(textBox2.Text);
	// 2つのキーで検索
	ProductRow row = this.pcache[id, subid];
	textBox2.Text = row.name;

}

初回だけデータベースに接続して、キャッシュを作成します。
DataRow から独自の Product テーブルクラスにコピーした後に、キャッシュ pcache に保存します。
キャッシュへのアクセスは、pcache[ id, subid ] のように配列でアクセスできて、戻り値は ProductRow オブジェクトという具合。DataRow よりも型付の ProductRow のほうが、row.id とか row.name とかのプロパティアクセスができるので、コードの品質はあがるはずです。インテリセンスが使ええるし、コンパイル時に列名が間違っているとエラーになるし。

■内部実装

キャッシュクラスはこんな感じ。

/// <summary>
/// 複数キーに対応したDictonary
/// キーが2つの場合
/// </summary>
/// <typeparam name=&quot;K1&quot;></typeparam>
/// <typeparam name=&quot;K2&quot;></typeparam>
/// <typeparam name=&quot;V&quot;></typeparam>
public abstract class Dictonary<K1, K2, V> : Dictionary<string, V> where V : new()
{
	public V Empty { get; set; }

	/// <summary>
	/// コンストラクタ
	/// </summary>
	public Dictonary()
	{
		this.Empty = new V();
	}

	// protected override
	protected void Add(string key) { }
	protected new  V this[string key] { get { return Empty; } set { } }
	protected new bool ContainsKey(string key) { return false; }

	/// <summary>
	/// キーと値を追加
	/// </summary>
	/// <param name=&quot;key1&quot;></param>
	/// <param name=&quot;key2&quot;></param>
	/// <param name=&quot;value&quot;></param>
	public void Add(K1 key1, K2 key2, V value)
	{
		this.Add(MakeKey(key1, key2), value);
	}
	/// <summary>
	/// キーを作成
	/// </summary>
	/// <param name=&quot;key1&quot;></param>
	/// <param name=&quot;key2&quot;></param>
	/// <returns></returns>
	public string MakeKey(K1 key1, K2 key2)
	{
		return key1.ToString() + &quot;_&quot; + key2.ToString();
	}
	public abstract string MakeKey(V value);

	/// <summary>
	/// []演算子でアクセス
	/// </summary>
	/// <param name=&quot;key1&quot;></param>
	/// <param name=&quot;key2&quot;></param>
	/// <returns></returns>
	public V this[K1 key1, K2 key2]
	{
		get
		{
			string key = MakeKey(key1, key2);
			return this.ContainsKey(key) ? this[key] : Empty;
		}
		set
		{
			string key = MakeKey(key1, key2);
			if (this.ContainsKey(key))
			{
				this[key] = value;
			}
			else
			{
				this.Add(key, value);
			}
		}
	}
	/// <summary>
	/// 指定したキーを含むか
	/// </summary>
	/// <param name=&quot;key1&quot;></param>
	/// <param name=&quot;key2&quot;></param>
	/// <returns></returns>
	public bool ContainsKey(K1 key1, K2 key2)
	{
		string key = MakeKey(key1, key2);
		return base.ContainsKey(key);
	}
	/// <summary>
	/// データを設定
	/// </summary>
	/// <param name=&quot;table&quot;></param>
	public void Fill(IList<V> items)
	{
		foreach (V it in items)
		{
			this[MakeKey(it)] = it;
		}
	}
}

抽象クラスになっているのは、DataRow にあたる V から主キーは直接取り出せないので、一度継承するという方式です。データエンティティに「主キーの属性」みたいなのをつければ、ここは解決するかも。

もうひとつは、DataRow から型付のDataRowクラスにコンバートするためのクラスが必要と思います。毎回コンバートだけコードを書くのは面倒なので、テンプレートとリフレクションを使って自動化します。

/// <summary>
/// リフレクションを簡単にするための準備
/// </summary>
public interface IDataTable
{
	Type GetRowType();
	object CreateRow();
}
/// <summary>
/// 型付DataTable用のテンプレート
/// </summary>
/// <typeparam name=&quot;DRType&quot;></typeparam>
public class DTTemplate<DRType> : IDataTable, IListSource
{
	/// <summary>
	/// DataRow の型を返す
	/// </summary>
	/// <returns></returns>
	public Type GetRowType()
	{
		return typeof(DRType);
	}
	/// <summary>
	/// DataRow のリスト
	/// </summary>
	protected List<DRType> _rows = new List<DRType>();
	public List<DRType> Rows
	{
		get { return _rows; }
	}

	/// <summary>
	/// 新しい DataRow を作成
	/// </summary>
	/// <returns></returns>
	public DRType NewRow()
	{
		return (DRType)Activator.CreateInstance(typeof(DRType));
	}
	/// <summary>
	/// 新しい DataRow を作成(object型)
	/// </summary>
	/// <returns></returns>
	public object CreateRow()
	{
		return NewRow();
	}

	// インターフェースの実装
	/// <summary>
	/// 内部リストから IList を使う
	/// </summary>
	public bool ContainsListCollection
	{
		get { return true; }
	}
	/// <summary>
	/// IListのコレクションを返す
	/// </summary>
	/// <returns></returns>
	public System.Collections.IList GetList()
	{
		return this.Rows;
	}
}

/// <summary>
/// <summary>
/// 形無しDataSetを型付DataSetにコンバートするクラス
/// </summary>
public class DataBind
{
	public static void Conv(DataTable src, IDataTable dest)
	{
		Type rowType = dest.GetRowType();
		System.Collections.IList rows = ((IListSource)dest).GetList();
		foreach (DataRow row in src.Rows)
		{
			object item = dest.CreateRow();
			// リフレクションを使って、列ごとにコピーする
			foreach (DataColumn column in src.Columns)
			{
				// この部分はキャッシュすると高速化する
				string key = column.ColumnName;
				PropertyInfo pi = rowType.GetProperty(key);
				if (pi != null)
				{
					// object型 -> 元の型のキャストでエラーになるため、
					// 一度元の型に ChangeType してから代入する。
					pi.SetValue(item,
						Convert.ChangeType(row[key], pi.PropertyType),
						null);
				}
			}
			rows.Add(item);
		}
	}
}

これを作成しておいて、自前のエンティティのクラスやキャッシュクラスを作成。

/// <summary>
/// 複数キーを持つ Product エンティティクラス
/// </summary>
public class ProductRow
{
	public int id { get; set; }		// PK
	public int subid { get; set; }	// PK
	public string name { get; set; }
	public int parent_id { get; set; }
	public string desc { get; set; }
	public DateTime updatedate { get; set; }
}
/// <summary>
/// 型付DataTable
/// </summary>
public class Product : DTTemplate<ProductRow> { }

/// <summary>
/// Product キャッシュクラスを作成
/// </summary>
public class ProductCache : Dictonary<int, int, ProductRow>
{
	/// <summary>
	/// ProductRow からキー文字列を取得
	/// </summary>
	/// <param name=&quot;value&quot;></param>
	/// <returns></returns>
	public override string MakeKey(ProductRow value)
	{
		return MakeKey(value.id, value.subid);
	}
}

テンプレートのまま使ってもいいけど、もう一度のエンティティクラスの名前を付け直すとコーディングが楽です。テーブル名と揃えておくと、データベース仕様書とのマッチングが楽とか。

ちなみに、キーが3つある場合は、先の Dictonary のコードをコピーして、

public abstract class Dictonary<K1, K2, K3, V> : Dictionary<string, V> where V : new()

のように倍増させておきます。あらかじめ、6個ぐらい作っておけば大抵のテーブルは大丈夫かと。デリゲートの <Action> と同じ仕組みですね。

カテゴリー: C# | キャッシュを作ってDataTableのアクセスを高速にする(2) はコメントを受け付けていません

キャッシュを作ってDataTableのアクセスを高速にする

データベースアクセスのパフォーマンスチューニング例 | Moonmile Solutions Blog
http://www.moonmile.net/blog/archives/2423

の続きを作ろうと思って「3分間クッキング」ならぬ「30分間プログラミング」になってしまいました。テンプレートを使ったものと、DataRow をそのまま使ったものとを 2 つ作ってしまったかですね。DataTable や List<> の場合には、Select メソッドを使えるのではないか?というのもありますが、Dictionary を使っているので速度的には断然高速なはずです。少なくとも、20万件ぐらいでも、Dictionary の場合は、一瞬(0.001秒以下)で返ってきます。

■利用の仕方

private string CNSTR = "";
private AnkenCache cache = null;

private void button1_Click(object sender, EventArgs e)
{
	if (cache == null)
	{
		SqlConnection cn = new SqlConnection(CNSTR);
		SqlDataAdapter da = new SqlDataAdapter("SELECT * FROM ANKEN", cn);
		DataTable dt = new DataTable();
		da.Fill(dt);
		// キャッシュに設定
		this.cache.SetData(dt);
	}
	int id = int.Parse(textBox1.Text);
	DataRow row = cache.Get(id.ToString());
	textBox2.Text = (string)row["name"];
}

こんな風にキャッシュ利用します。1度目はデータベースから検索するけど、2度目以降はキャッシュを使うってことで。
キー情報は、string 型なので、適当な MakeKey なる関数を作っておくと便利です。

■内部コード

DataTable を渡して DataRow の中身を Dictionary にキャッシュしているだけです。某案件では、これに似たことをやりましたが、こっちのほうが洗練されている…と思う。

public abstract class SelectCacheTableRow
{
	protected Dictionary<string, DataRow> _dic;
	public DataRow Empty { get; set; }

	public SelectCacheTableRow()
	{
		_dic = new Dictionary<string, DataRow>();
		this.Empty = new DataTable().NewRow(); // 空を設定
	}
	public void Set(string key, DataRow value)
	{
		if (_dic.ContainsKey(key) == true)
		{
			_dic[key] = value;
		}
		else
		{
			_dic.Add(key, value);
		}
	}
	public DataRow Get(string key)
	{
		if (_dic.ContainsKey(key) == true)
		{
			return _dic[key];
		}
		else
		{
			return this.Empty;
		}
	}
	/// <summary>
	/// キー情報を作成
	/// </summary>
	/// <param name=&quot;t&quot;></param>
	/// <returns></returns>
	public abstract string MakeKey(DataRow it);
	/// <summary>
	/// データを設定
	/// </summary>
	/// <param name=&quot;table&quot;></param>
	public void SetData(DataTable table)
	{
		foreach (DataRow it in table.Rows)
		{
			this.Set(MakeKey(it), it);
		}
	}
}

public class AnkenCache : SelectCacheTableRow
{
	/// <summary>
	/// キー情報を作成
	/// </summary>
	/// <param name=&quot;t&quot;></param>
	/// <returns></returns>
	public override string MakeKey(DataRow row)
	{
		return row[&quot;id&quot;].ToString();
	}
}

■テンプレートを使う

上記の例だと、DataRow しか扱えないので、単純なデータクラスを使いたい場合(こっちのほうがお勧めなんだが)、テンプレート(正確にはジェネリック)版を使います。「Project」というプロパティだけのクラスを作っておいて、プロパティ名でアクセスできるようにします。DataRow からデータクラスへのコンバートは、リフレクションを使ったバージョンを用意しておくと(型なしDataTableから型付きDataTableにコピーする方法 | Moonmile Solutions Blog 参照)いいでしょう。

public abstract class SelectCache<T> where T: new()
{
	protected Dictionary<string, T> _dic;
	public T Empty { get; set; }

	public SelectCache()
	{
		_dic = new Dictionary<string, T>();
		this.Empty = new T(); // 空を設定
	}
	public void Set(string key, T value)
	{
		if (_dic.ContainsKey(key) == true)
		{
			_dic[key] = value;
		}
		else
		{
			_dic.Add(key, value);
		}
	}
	public T Get(string key)
	{
		if (_dic.ContainsKey(key) == true)
		{
			return _dic[key];
		}
		else
		{
			return this.Empty;
		}
	}
	/// <summary>
	/// キー情報を作成
	/// </summary>
	/// <param name=&quot;t&quot;></param>
	/// <returns></returns>
	public abstract string MakeKey(T it);
	/// <summary>
	/// データを設定
	/// </summary>
	/// <param name=&quot;table&quot;></param>
	public void SetData(List<T> items)
	{
		foreach (T it in items)
		{
			this.Set(MakeKey(it) , it);
		}
	}
}
public class Project
{
	public int id { get; set; }
	public int projnum { get; set; }
	public string name { get; set; }
	public int parent_id { get; set; }
	public string desc { get; set; }
	public DateTime updatedate { get; set; }
}
public class ProjectCache : SelectCache<Project>
{
	// キー情報を作成
	public override string MakeKey(Project it)
	{
		return it.id.ToString() + &quot;_&quot; + it.projnum.ToString();
	}
}

Empty プロパティを null にしないのは、以下のような書き方でもエラーにならないようにするためです。DataRow の場合は名前がマッチングしないと例外を発生しますが、こちらの場合は1行で書けるから便利かと。if文でいちいちチェックしなくてよいので。

string name = cache.Get( key ).name ;

敢えて空を調べたいときは、以下のように書きます。

string name = "";
if ( cache.Get(key) != cache.Empty ) {
  name = cache.Get( key ).name ;
}
カテゴリー: C# | キャッシュを作ってDataTableのアクセスを高速にする はコメントを受け付けていません

visual c++ 2010 では BOM 無しの UTF-8 がコンパイルできない

mac とのソースコードの共通化について
http://social.msdn.microsoft.com/Forums/ja/vsgeneralja/thread/62164982-9795-4f5a-bd7c-85ec121f9126

こんなのを見つけて「え?」となったので試してみると…確かにできませんでした。

以下のようなファイルを visual c++ 上で作成

#include "stdafx.h"

int _tmain(int argc, _TCHAR* argv[])
{
	puts("masuda tomoaki");
	puts("増田 智明");

	return 0;
}

エディタで開いて「BOM 無し」で保存する。

先のファイルをコンパイルすると、

D:\work\blog\src\SampleUTFCode\SampleUTFCode>cl /c SampleUTFCode.cpp
Microsoft(R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86
Copyright (C) Microsoft Corporation.  All rights reserved.

SampleUTFCode.cpp

D:\work\blog\src\SampleUTFCode\SampleUTFCode>cl /c SampleUTFCode.cpp
Microsoft(R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86
Copyright (C) Microsoft Corporation.  All rights reserved.

SampleUTFCode.cpp
SampleUTFCode.cpp : warning C4819: ファイルは、現在のコード ページ (932) で表示
できない文字を含んでいます。データの損失を防ぐために、ファイルを Unicode 形式で
保存してください。
SampleUTFCode.cpp(10) : error C2001: 定数が 2 行目に続いています。
SampleUTFCode.cpp(12) : error C2143: 構文エラー : ')' が 'return' の前にありませ

のようなエラーが出ます。どうやら、BOM 無しなので os の標準コード(sjis 932)を使って読み込むために、puts(“増田 智明”); あたりで読み込みエラーになっているようです。

じゃあ、コマンドプロンプトで「chcp 65001」でコードページを変えたらどうなる?と思ったけど、変わりませんね。

コンタクトは、フォーラムの貼ってあるURLは間違っているようでページエラーになります。後でURLが変わっている?タイトルで検索してみると出てきましたので、リンクし直し

UTF-8シグネチャ(BOM)なしのソースコードを VC++ でコンパイルできる様にして欲しい | Microsoft Connect
http://connect.microsoft.com/site550/feedback/details/688472/utf-8-bom-vc

終了しているみたいですが、記録として要望をしておきました。

カテゴリー: C++ | visual c++ 2010 では BOM 無しの UTF-8 がコンパイルできない はコメントを受け付けていません

windows 環境で NSString を試してみるよ

危ない文章をトップに晒しておく(笑)のもアレなので、objective-c の話を少し。
mac mini 上で確認してもいいのですが、エディタは QX を使いたい、ということで windows 環境で文法のテストをしています。
最終的には、xcode 上で確認をするので、GUI のところも同時に記述したいなぁと思って、次のように書いています。

まず、NSString クラスのお試しコード

#include "base1.m"

- (IBAction)clickButton
{
	// 固定文字列を表示
	textField.text = @"fixed string";

	// 文字列の長さを取得する
	NSString *text = @"masuda tomoaki";
	int len = [text length];
	textField.text = [NSString stringWithFormat: @"length is %d", len ]; 

	{
	// 後半の部分文字列を取得する
	NSString *text = @"masuda tomoaki";
	NSString *lname = [text substringFromIndex: 7];
	textField.text = [NSString stringWithFormat: @"[%@]", lname ]; 
	}
	{
	// 前半の部分文字列を取得する
	NSString *text = @"masuda tomoaki";
	NSString *fname = [text substringToIndex: 6];
	textField.text = [NSString stringWithFormat: @"[%@]", fname ]; 
	}
	{
	// 文字列をトリムする
	NSString *text = @" masuda tomoaki ";
	NSString *name = [text stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
	textField.text = [NSString stringWithFormat: @"[%@]", name ]; 
	}
	{
	// 文字列を大文字にする
	NSString *text = @"MASUDA tomoaki";
	NSString *name = [text uppercaseString];
	textField.text = [NSString stringWithFormat: @"[%@]", name ]; 
	}
	{
	// 文字列を小文字にする
	NSString *text = @"MASUDA tomoaki";
	NSString *name = [text lowercaseString];
	textField.text = [NSString stringWithFormat: @"[%@]", name ]; 
	}
	{
	// 最初だけを大文字にする
	NSString *text = @"MASUDA tomoaki";
	NSString *name = [text capitalizedString];
	textField.text = [NSString stringWithFormat: @"[%@]", name ]; 
	}
}

#include "base2.m"

前後に、base1.m と base2.m がインクルードされているのミソです。この中で UI 部分をエミュレートする textField や、clickButton メソッドの実態があります。

■base1.m

#import <stdio.h>
#import <Foundation/Foundation.h>
#import <Foundation/NSObject.h>
// #import <UIKit/UIKit.h>
#define IBAction void

@interface UITextField: NSObject
{
	@private
	NSString *_text;
}
@property (nonatomic,retain) NSString *text;
@end
@implementation UITextField
@synthesize text = _text ;
- (id)init {
	[super init];
	return self;
}
- (void)print {
	NSString *log = [NSString stringWithFormat: @&quot;text is %@&quot;, _text];
	// NSLog(@&quot;%@&quot;, log );
	printf(&quot;UITextField.text: %s\n&quot;, [_text cString] );
}
@end

@interface View : NSObject 
{
	UITextField *textField;
	UITextField *textField2;
}
- (IBAction)clickButton;
@end
@implementation View
- (id)init
{
	textField = [[UITextField alloc] init];
	textField2 = [[UITextField alloc] init];
	return self;
}
- (void)print
{
	[textField print];
	[textField2 print];
}

■base2.m

@end

void Main()
{
	View *view = [[View alloc] init];
	[view clickButton];
	[view print];
}

int main(int argc, char *argv[])
{
	NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
	Main();
	[pool release];
	return 0;
}

本当は、makefile を作ればいいのでしょうがソースコードレベルで書きたいので #inlucde を使っています。
テキストフィールドである UITextField クラスのダミーと、View クラスのダミーを書きます。
ボタンのクリックは、「clickButton」固定です。

base1.m と base2.m で分断しているのは、clickButton の実装が記述されているファイルが basic001.m, basic002.m のように、通番になっているからですね。ええ、原稿書きのために使っているためなのです。

これを以下のようなバッチを作ってコンパイルします。

■cc.bat

@echo off 
set _path=%path%
set path=%path%;c:\llvm\bin;C:\GNUstep\bin;C:\GNUstep\mingw\bin;C:\GNUstep\GNUstep\System\Tools
@echo on
clang -o %~n1.exe %~n1.m -I c:/llvm/include -I c:/GNUstep/GNUstep/System/Library/Headers -L c:/GNUstep/GNUstep/System/Library/Libraries -lobjc -lgnustep-base -fconstant-string-class=NSConstantString 
@echo off
set path=%_path%

環境変数 path のほうは、gnuestep, clang が通るように設定します。コマンドラインでビルドをします。
一時的なので、_path に保存して戻すという…変な作り。私のところの path が長すぎてマイコンピュータの環境設定では収まりきらないので、仕方なくこうしています。整理すればいいんですけどね。

cc basic083.m

のようにコンパイルすることができます。

実行するとこんな感じ

■実行結果

D:\work\逆引きiOS\llvm>basic083
UITextField.text: fixed string
UITextField.text: change string

D:\work\逆引きiOS\llvm>

ええ、これを xcode project を作るスクリプトを組まないとねぇ、今月中には。

カテゴリー: Objective-C | windows 環境で NSString を試してみるよ はコメントを受け付けていません

原発と安全神話と誤謬

年末(というか年始)の真夜中に再放送していたETV特集を観ての感想をば。一応、原子力工学科出身(落ちこぼれだけどね orz)なので、3月以降「原発」については、ぼちぼちと考える時間を取っています。
大阪の原子力学科に入ったのは、かれこれチェルノブイリが爆発した次の年です。なので、それ以前はあまり原子力自体には興味がなくて、漠然と漫画か物理学かなんかかかんかと悩んでおりました…つーか、ほどよく頭は良くなかった。そんな訳で、伊方原発訴訟 のあたりは、良く知りませんでした。オイルショックからくる冷戦、石油危機、夢の原子力、夢の核融合という流れで理系に進んできた私としては、

  • チェルノブイリが爆発したのは制御不足であった。
  • だから、安全制御ができるくらいの力量があれば、夢の原子力は達成可能。
  • そのうちに、20年後ぐらいには、夢の核融合が達成可能

と呑気なことを思っていました。まあ、本音のところは物理学科にはいるほどの偏差値がなくて、原子力学科を選んだんですけどね。そういう打算的なところからスタートなのです。

が、スタートはどうあれ、ひと通り原子力というものを学んで行くと、原子力発電というものは総合工学の分野で、構造建築としても、核燃料を扱う化学としても、拡散方程式を解くコンピュータとしても、放射線・放射性物質を扱う安全教育としても、それなりに興味深い分野ではあります。その中で、大学の教授が授業中に呟いた

  • 「福島原発は既に古いから、危ない」(この話自体が20年以上前)
  • 「軽水炉は確立された技術だから、次は高速増殖炉が研究対象になる」
  • 「原発反対のために入学していくる学生もいるからな、そういうのにはなるなよ」

という言葉を今更ながら、再認識しているところです。
これを踏まえて、ざっと、ETV特集の感想を書き下しておきます。

■福島原発は古い型である

沸騰型軽水炉という炉心自体が非常に古いのと、マーク2という型自体が古いのと、30年と言われていた耐久年数を過ぎていたことと、モロモロのことがあって「いつ壊れておかしくない原子炉(であった)」ということですね。原発自体は、まわりがコンクリートで囲まれています。これはコンクリートが水分を含むために放射線(中性子)を遮るために作られています。巷のブログでは、「放射線は十分には遮れない」というものもありますが、放射線自体は、3次元空間に拡散するので中心から距離の3乗で弱くなります。なので、核分裂による放射線の遮蔽もありますが、建築構造的な強度を保つためのコンクリート建築という意味あいも含んでいます。いまのように剥き出しになってしまって、周りにいるだけで放射線を浴びるのはこまりし、粉じんなどがまき散らされるのも困るわけです。
ですが、日本の原発は海水を二次冷却水についかう理由から海岸線に作ることが多いのです。フランスなんかは内陸国ですから真ん中に作るわけですが、日本の場合は「海水」というのが肝だったわけです。が、この海水が曲者で、長年潮風にあたっているとコンクリートが腐食するわけです。以前、ベットタウンに100年建築と言われてコンクリート式の団地ができたものの20年経たずしてひび割れが走る、というようなことも、実は海に面した原発でも同様なのです。なので、コンクリートの腐食状態をそのままにして、耐久年数を伸ばすというのは不可能なのです。

あと、福島原発では契約上の関係から、ポンプの切り替えができなかったそうで、海抜30メートルの土地を海抜10メートルまで削り取っています。排水溝に近いところではもっと低いでしょう。今回の津波の高さが12,3メートルということを考えると、30メートルのままだったら津波の影響は少なかったと言えます(少なくとも、総電源遺失という事態にはおこらなかったでしょう)。

また、これは知らなかったのですが、根本的に日本の原発はイギリスやアメリカなどから現品で輸入されたもので、自前で作っておりません。だから、と言っても難なのですが、

  • 電力会社は、原発に関しては単なる運転手
  • 故障がおきると、自前では直せない
  • 欠点があっても、リカバリー出来ない

そうです。なるほど、水素爆発が起こりそうなときに社員が逃げ出そうとした、後のもろもろの不手際、汚染水に関するもろもろの誤報、冷却に関する知識不足(というか無知さ加減)は、そこから来るものらしいですね。三菱、日立という会社が原発の輸入、建築、保守を任されていますが、運転自体は下請け、高卒に任されているという現状がここに露呈しています。
いや、「高卒」というのが悪いわけではないのですが、放射線に関する安全教育、危機管理に関する基礎知識は、「現場に入ってもあまり教育されていない」というのが、当時(20年前)の現状だったので…今は違うかもしれません。

そういう訳で、古い型の原子炉を古いままで、だましだまし使っていた、というのが福島原発です。

■軽水炉は確立された技術

原発に関しては、沸騰型軽水炉、圧力型軽水炉があるのですが、沸騰型軽水炉は古い型に属します。特に一次理冷却水が汚染されたまま、タービンを回すことになるので、タービン自体の耐久年数に引っ張られるし、炉心爆発のときの核物質拡散の率が増えてしまいます。なので、圧力型に移行する、そして、「水」では熱伝導が悪いので「ナトリウム」を使う高速増殖炉というのが、学術的な流れだったのですが…どうも違うようです。
あんなに頻繁に止まっているのでは、決して「確立した技術」ではありません。確かに、核分裂を利用した発電という意味では「理論的」に確立したものなのでしょうが、安全基準をクリアするという点において「発電所」としては確立しておりません。かつ、ETV特集で知ったのですが、当時は輸入製品としての軽水炉は日本としては(政府としては)うまみが無くて、次は高速炉の時代、MOX燃料の時代であるという「補助金」の流れが大きく影響しているということが分かりました。大きく、というか「全て」ですね。
また、原発の推進の発端自体が、経済界主導のところにあるので、全ては「稼働率」を上げることと、「儲けが得られること」という商売的な…いや、日本経済の発展のためを考えられたものらしいのです。

ええ、それじゃあ、他人の庭にプルトニウムをまき散らしても、俺知らんもん、になるわけです。当たり前。

そんな訳で、大学で学んできた「核汚染物質の濾過」に関する研究や、「炉心溶解時の安全装置」や、「放射線対策による安全教育」などモロモロのことは、経済的な効率化を目指す「原発」には届いていなかった…と言いますから、所詮、机上の学習という具合だった模様です。

とはいえ、炉心シミュレーションを専門としていた私にとっては「燃料棒」と「制御棒」の組み合わせで計算するだけで、ちまちまと制御棒を動かせば、十分安全に制御できるものなのです。ですが、本来の「安全に運用」の部分はもっと別なところにあって、

  • 人為的なミスを防ぐ装置
  • 配管などを定期的に検査する技術
  • 排水漏れなど直す技術
  • 地震などの災害に対する緊急非難処置

があって初めて「軽水炉は確立された技術」である、と言えるのです。

ですが、安全基準を「お金」勘定し、建築費としてのトレードオフしか考えられない御仁には、ちょっと無理な話なのですね。というわけで、そもそも、「安全」なんかは最初から考えられていなくて、推進のための作られた「安全」が最初にあるだけで、誰も「軽水炉は安全である」というのは断言していないのです。ええ、ETV特集で出てくる科学者、経営者の誰ひとり、そんなことは言っていません。「安全である」ことを売りにして、推進していただけなのです。

■原発反対のために入学する学生

原子力学科に入学するのに、原発を否定するなんて…と思ったのですが、伊方原発訴訟のほうに助手の方が出てきたりと、今更ながらああなるほど、と思いました。大学の教授としては、原発は推進されるほうがよいわけで、反対に進むのは学科の縮小になってしまうためですね。とはいえ、私が卒業したころから原発自体の反対が多く、イメージが悪いことから「原子力学科」という看板がなくなりはじめ「エネルギー学科」とか名前を変えることが多くなりました。そのあたりは、水産学科とか造船学科とかといっしょですね。

そんなこともあり、原子力分野にとっては「失われた10年」ぐらいな話で、技術的に止まってしまった時期からだらだらと流れて、福島原発の件に至っています。原発の建築反対をするのは別に構わないのですが、いま稼働している原発の補修工事などが全くできないまま、現在に至っているのです。改良する技術もかなり失われてしまっていると思われます。このあたりは、winny の件に近いと思います。

技術者からすれば、世論や政治に関係なく、10年20年単位で技術を続けていかなくてはいけなくて、10年20年先にやっとこさ次の技術に切り替わるという現象があります。原発技術に関しては、次の核融合のための繋ぎの技術の意味合いが強くて(核廃棄物の量が多いので)、20年から50年ぐらいまでの科学技術だと言われてきました。残念ながら、核融合のほうはまだまだ実用段階に遠いものがあります。なので、エネルギー問題に関しては、まだまだ、原発、石油、ガスに頼る必要があるのです。

太陽光発電というのも昨今ありますが(ソフトバンクのあれは、まだ続いていますかね?パナソニック町とか?)、一朝一夕にいくものではなく。実は「電力供給」と「電力需要」を分けて考えるのが、それこそスマートですね、と私は言いたいのです。

なので、ETV特集の島村原子力政策研究会についての原子力発電に関して強い意欲を持っている、という結論に、私は非常に同意します。技術者として、手元にあるものをうまく使いこなすこと(それが何であれ)、そして次世代の目途が付けば清く捨てること、が重要です。だから、

  • 原発は、次の電力技術が確立するまでのつなぎの技術である。
  • 原発は、被害を出さないように安全に運用することが最重要である(経済的ではなく)

というのがポイントです。太陽光のパネルを作るには非常に電力が必要です。軽い車を作るためのアルミ製品は非常に電力が必要です。そういう、次世代の電力エコロジーを支えるための「繋ぎの技術」なのですよ。

■「電力需要」を考えるIT技術

ここで自らのIT技術とつなげる訳ですが、「ファジー技術」(というものがありましたね)を初めとして、マイコン技術、自動制御技術の根底には、ソフトウェアの技術が不可欠です。いままで、

  • 点けっぱなしのエアコンは不経済である。

という視点でしか語られなかったものが、

  • 点けっぱなしにしても、自動制御される。

という技術が得られる時代になってきました。「時代になってきました」というのは前年の悪名高い「計画停電」(今年もやるんですよねッ!!! 原発が止まっているわけだし、危機的状況なんですよねッ!!! 何故、今年の冬はしないんでしょうね???)も、スイッチの自動的な ON/OFF 自体を電力供給量と連結させれば非常に簡単なことなのです。工場に対しての電圧の安定供給は必須(モーターの回転数とかがあるからね)なのですが、家庭に関しては多少不安定でもよいのですよ。かつ、数々の電化製品は主電源を切るという人為的ミスを誘うようなものではなく、自前で電力需要を下げるというシステムを組み込めるはずです。

電力需要を末端の装置で下げるというのは、

  • 無線LANに関しては、組み込み技術で10分の1の電力量で済みます。
  • サーバーのHDD、ファン、冷却も集中化、自動停止/自動活性化が可能です。
  • ディスプレイ技術も、電力消費を下げる余地があります。

という感じで、東電様に御迷惑を掛けないように、電力消費を抑え、電気料金を抑えることが可能なのですね。まぁ、ETV特集を観て、電力会社がどうして「殿様」気分なのかがわかりました。何も最近に限ったことではないのですね。なるほど。

そんな訳で、私の結論としては「もともと安全神話」なんてなかった。「原子力は安全です」は宣伝文句でしかなくて、原発推進は経済界の理由が発端であって、だから儲け主義が先に走ってしまって利権がらみの「原子力村」ができてしまったのは当然の帰着であった、ということが分かりました。発電の理論としては安全なんですけどね、扱っている人が駄目駄目なので。そういう言葉もETV特集の中にありましたね。3月頃にも聞いたような気がします。「運転員のミスがなければ、爆発は起こらなかった」、この言葉を使う人は、(精神的に)論理的に破綻しているので、注意したほうがいいです。

以上、年初のメモ代わりに。

カテゴリー: 雑談 | 2件のコメント

中国オフショアな話

前記事で価格競争な話が出てきたので、勢いでオフショアな話も書いておきます。

開発分野を中国にオフショアする場合には、

  1. 日本の案件を、大手SIerが取りまとめ
  2. 設計書を日本で記述
  3. 実装、試験を中国オフショア
  4. 最終試験を日本で行う

という形になります。まぁ、3 のところが、日本の「協力会社」でも変わらないわけで、いわゆるゼネコン式になりますね。ここで、「ゼネコン式」が良い悪いかは別にして、かつ「オフショア自体は効率的かどうか」も別として、中国オフショアの成功率を高める方法を列記しておきます。私の経験から来るものなので、オフショア先によって違いは出るかもしれませんが、大体同じだと思います。

中国オフショアをするときには、第1に「単価が安い」ということでコストカット的な意味あいで出すことが多いのですが、中国のIT会社からすると、物価の差から結構な金額が入ってくることになります。なので、中国IT会社に勤める会社は、結構な高給取りです。なので、高給取りになるということは、結構な高学歴な人が多いのです。ものにとよると、日本のSEよりも中国プログラマのほうが高学歴だったりするのが普通です。まぁ、大手SIerの20代マネージャの下で、外注先のベテランマネージャが働くという「ゼネコン式」と思えば、同じことです。

さて、中国の場合は官僚主義ですから「官」のほうが圧倒的に権力があります。日本もそうですが、東大から官僚に流れるのと同じで、何かと中央に進もうとするのがステータスです。
で、中国プログラマは、高給取り&高学歴ではありますが、官僚に入れなかった人達です。勿論、自分からドロップアウトした人も多いでしょうが(ソフトウェア=理系なところなので、官僚には向かない)、最終地点がが微妙に違ってしまった人達です。

中国の教育ですが、全体主義の教育を受けるので、国策を重視します。命令に従うこと、が非常に当たり前に行われている国です。また実は民族の格差があり、中央を牛耳る漢民族とそれ以外の民族で格差があります。おそらく、小学校からの教育で民族差がでてきていると思われます。
今回は、上海での仕事だったので、なおさら「上海という都会」に出てくる地方の人という位置が彼らに用意されています。

これらをまとめると、

  • 中国プログラマは「命令」に忠実である。
  • 中国プログラマは、高学歴&高プライドを持つ。
  • 中国プログラマは、官僚路線からドロップアウトしている(可能性が高い)。
  • 中国プログラマは、国策を優先する。

という特徴があります。これらは、どこの国とやる時でも同じですが、相手の国の習慣は、自分の国(日本)の習慣とは異なることを忘れてはいけません。目的意識も違うし、価値観も違います。これは個人的な価値観ではなく、国というカテゴリに分けた時の価値観です。

あと忘れてはいけないのが、中国は「国策」として情報統制を行っているので、インターネット上の情報が自由に拾えるとは限りません。実際、今回の仕事でも日本のIT関係の情報ページを中国では読めない状態でした。また、google が撤退しているので、百度を使います。実は、IT関係の情報を得るには、かなり苦労する環境と言えます。

さて、中国オフショアの成功率を高めるためには、これらのことを踏まえて、設計なりプロジェクトを運営する(オフショアする)ことが重要です。

■決め事を「命令」として明確化する

中国の民間人は「命令」されることに慣れているので、「命令」がないとうまく動けません(逆に、官僚になれば「命令」されることが屈辱になりますが)。なので、日本からの意向を伝えるときには、明確に「命令」として決めます。

例えば「これは、1と2の方法があるけれども、どちらかやりやすい方法をやってください」という気遣いをすると却ってうまくいきません。なので「1の方法にしてくれ」と言います。主従関係を明確にするということでは、まるで犬猫の関係なのか?と不思議に思うかもしれませんが、どうも彼らは「自分で決める」こと苦手としています。

ただし、その反動なのかプライベートなことを決められることはしません。むしろプライベートが優先だったりします。ですが、ビジネスとしては「命令」で OK です。

■手取り足取りにしないが、ゴールは明確に。

「命令」をするとは言いましたが、新人ではないので「手取り足取り」する必要はありません。実は「手順」を求められるのですが、そのあたりは彼らの「高いプライド」を刺激するように「命令」を作り替えます。また、命令には明確なゴールを作る必要があります。そうしないと、彼らの「仕事」振りに巻き込まれてしまいます。彼らの「仕事」は動作する完成品を作ることではありません。彼らの「仕事」は、動かなくても「完成品を作る」ことだけなのです。なので、完成品を作るような場合には、「動作する」かつ「完成品を作る」と分けて命令を組み替える必要があります。

例えば、詳細設計書を記述させる「命令」を作る場合には、次のようにします。

  1. 詳細設計のフォーマットを提供する。
  2. 詳細設計のサンプルを提供する(この時、そのプロジェクトで動作するサンプルを記述する)
  3. 自己チェックのポイントを明確にする(関数名、引数名の記述など)
  4. 提出された詳細設計をチェックする場合は、間違いのみ指摘する(完成例は示さない)

ゴールは「間違えていない設計書」なので、4 で間違いのみ指摘します。下手に手直しする必要はありません。
この手直しの部分は、時間がかかるところですが、実は「必要経費」なのでオフショアするときに予算を見誤ってはいけません。

■高プライドを利用する

手取り足取りと同じですが、完璧な「完成品」を作るという目標を彼らに与えます。これは、一般的な中国プログラマの場合は違うかもしれません。高給取りであるというプライドを利用します。
中国プログラマは、いわゆる「人海戦術」が得意です。良くわかりませんが、手間暇かかるものも躊躇せずに手間暇かけてやります。まるで「バカのひとつ覚え」のような気もしますが、国の教育方針がそうであるような気がします。

なので、設計書の不具合や、プログラムの不具合は、中国プログラマが「自ら発見」するよりも、日本側でテストをして「チケットを発行する」という方法にシフトしたほうが品質があがります。
当然、画面も含む打鍵のテストは、日本で試験をするとコストもかかるし(派遣社員を雇ってもコストがかかる)、人員も不足することが多いでしょうから、打鍵の手順を示して中国で行い、結果を日本でチェックする、そして不具合票を日本で発行する、という手順にしておきます。

また、オブジェクト指向設計や、ある程度のフレームワークを組み込むことが可能であるならば、単体試験などの段階から不具合票を細かく発行するのも良いでしょう。中国オフショアの場合は、単体のコーディングレベルでの「高品質」は望めません。いわゆる、中国クオリティになってしまうので、日本の品質に上げるのはそれなりに手間が必要です。

■国策が優先する

小学校教育から大学教育、就職先や周りの人達の価値観、まで含めて考えると、中国プログラマが「個人主義」になる訳がありません。なので、「向上心」という曖昧なものではなく、「国策」という明確なものに従います。
日本の場合は、政治的な無関心(最近では揶揄されますが)というのは「暗黙的な了解」に従っている限りにおいてなので、昨今のような「好き勝手な」な政治手腕を発揮しようとした場合には通用しない…と思います。実際は「団塊世代」というのは、結構ひどいのではないか?と勘繰ってはいるのですが。「暗黙の了解」すら理解できない無恥なのかも。

さて、中国プログラマの場合は、「従う」という以外の選択肢がないために、自らを「従う」という立場に置きます。おそらく父を官僚に持った場合は違うのかもしれませんが、90 % 以上の人民はそうでしょう。なので、仕事の習慣として「国策」に反するものを持ってきても理解ができません。日本の常識と中国の常識が異なるために、ベースを省略することはご法度です。

「情報操作」としての国策は、実は中国のIT産業に結構なダメージを与えていると思います。なので、アジャイルなり単体の自動化なりオブジェクト指向なり、の情報をインターネットから得ることができません…と言うか、「インターネットから得る」という積極積な姿勢はありません。なので、日本のプログラマが(最近は違うかもしれないけど、人によるかもしれないけど)ネットなり書籍なりで自己学習するようなことを、中国プログラマに求めてはいけません。
今回のプロジェクトで必要な知識(オープンソースの利用など)があった場合は、先の「命令」とあわせて明確に手順化する必要があります。
中国中枢の独自なIT技術は方針として「国策」で進めれています。なので、それに沿わないものは「情報」として入ってこないし。情報として彼らは調べることをしないでしょう。君子李下に冠を正さず、ということですね。おそらく。

と、これぐらいが中国オフショアを進めるときのポイントです。では、具体的に仕事の割り振りを決めるのか。具体的にソフトウェアの開発プロセスの中でどの部分が任せら得るのか(価格競争として)、というのは後日。

カテゴリー: プロジェクト管理 | 中国オフショアな話 はコメントを受け付けていません

謹賀新年、地震とか諸々

皆さま、明けましておめでとうございます…と書こうと思った途端の地震ではありますが、去年は地震に明け暮れ、今年も地震の年なのかもしれません。まぁ、原発な年(以降ずっと)なのは確かなことですが。

さて、去年の.NETラボでオフレコで話して、4月以来ずっと想って/考え続けていることが二点あります。

  • 日本の IT 産業は復活できる可能性がある。
  • 経費はトータルコストのみ捉える。

この2つは一見、遠いようでいて近い…と言いますか、日本という国、アメリカという経済(あるいは幻想)、中国という市場(あるいは幻想)という立ち位置を自分の位置と絡め考えると、「近い」ところにあります。まぁ、「日本という国」が「幻想で包まれた日本という国」の「幻想」部分が暴かれてつつあることは確かなことですが。そこは「実体」を見ましょうということで。

■日本のIT産業を復活させる

「復活させる」と書きましたが、実は「興隆を極めた」ことも無い訳で、実はこれからなのかもしれません。「興隆」という点では、2000年ぐらいまでの tron とか v30 あたりの頃までかなと。os という分野で、windows, linux, ios と様々あるのですが、現在の日本は自力で os を開発していません。唯一携帯電話(いわゆるガラケー)の中で tron が使われてきた訳ですが、昨今の android と ios に押しつぶされてしまっています(実は symbian も押しつぶされる)。
パソコンを動作させる時に、os が必須で、その os はサーバーに使われたり、高いライセンス料を払わされたり、毎年の更新料金が必要だったりと、お金が流出している(本社がアメリカということと、株主が海外にいるということと、あるいは「株式」なり「投資」という仕組みそのもの)のは確かなことです。かといって、現在使われているパソコンを全て、国産 os にすれば良いのか、あるいはセキュリティやライセンス絡みで linux にすれば良いのかという議論が長く続いてきたのですが、どうやらスマートフォンやタブレットという形で、パソコンの os の利権は徐々に影響が無くなってきています。
また、ソーシャルゲームという分野では、web 自体が相手になるので、os 自体はあまり問題にはなりません。実質は linux + apache という組み合わせなのですが、ui がブラウザという枠にはまるために、敢えていえばサーバー側が linux というos に縛られ、クライアント側が javascript, html5 という os(のようなもの)に縛られるという形になります。

と言う訳で、os 自体にはあまり魅力が無くなってきている、というのが結論です。

そうなると、os 自体やそれを取り巻く開発環境や運営環境なぞを、扱っている大手企業(あるいは中小企業)は、いわゆる「メンテナス業」としての仕事にシフトします。メンテナンス業の場合は、

  • 如何に、メンテナンス自体を効率化させるか。
  • 如何に、メンテナンスの単価を下げるか。

ということに注力するために「アウトソース」が主流になります。クラウド化もそうなんですが、単価競争になりますね。このあたりは所謂大手の会社(富士通、日本電気、パナソニック、日本IBMなど)諸々に任せてしまうのがよいでしょう。当然、ここを請け負う中小企業も一括で。

さて、「開発」と「保守」という点では、私は圧倒的に「開発」寄りなので「復活」の議論を開発寄りに進めていきます。
「オフショア開発」が言われて久しいのですが、開発自体もアウト―ソースします。これも、大手からの人件費削減という要請から、自然と「単価の安い」会社に発注することに注力します。単価のやすい中国の開発者を使う、単価の安い韓国の開発者(韓国って安い?)という風に動くわけで、今後は単価の安いベトナム開発者へという動きになりでしょう。
しかし、日本の場合「単価の安いインドの開発者へ」には動かないことが多いのです。インドに発注すると、日本のSEが英語の設計書を書く必要があるという理由と、意外とインドに発注しても安くならない、というのが主なりゆうです。

「開発者」を雇用労働者として考える場合、「如何に単価を抑えるのか」という工場的な立場になるので、価格競争に巻き込まれてしまうのは確かなことです。資本主義…というか株式会社、株主、投資家という繋がりがある以上、どのような競争も「開発者」にとっては単価を下げる、単純労働の範囲になります。

しかし、「開発者」をスペシャリスト(他とは切り替えられないひと)として考え直してみると、実は先の「復活」のところに繋がってきます。つまりは、「単価競争」をしない立場にいればよいわけで、

  • なんとかのツールを使えば、早く安くできる
  • なんとかに発注すれば、手軽に作れる

という「大量生産」指向をやめます。いや、大手の場合はそれでよいのですが、個人、零細、フリーランスの場合は「安く」という選択肢を捨てます。「価格競争」をする場合は、自分で会社を作ってから(ある意味で、搾取対象…じゃなくて代わりに働いてくれる人を作ってから)にしたほうが良いでしょう。
となると、何で競争をするのか(あるいは競争をしないか)というと、「職人的な腕」で勝負をします。職人的な腕というのは、数年なり弟子なり師匠なり地道なりで、培わなければ辿り着けない道を敢えて選びます。

その培う技術は何なのか一例をあげると、

  • 画像やパターン認識によるロボットとの連携(今、私がやっている/やろうとしていること)
  • 組み込みシステム全般
  • google, twitter, facebook, apple 自身が組み合わせとして不可能なものを、外部から組み合わせる

要は、アメリカスタイルの最初の部分を「真似ます」。実は、この「真似る」ところ、最初はアメリカの企業が地道に真似るところがスタートしています。
ステップとしては、こんな感じです。

  1. アメリカの技術を踏み台にして、組み合わせる(国産に拘らない)。
  2. 片方は、独自技術に置き換える。
  3. もう片方を、独自技術に置き換える。

という「真似る」ところがスタートします。独自技術あるいは独自の価値に置き換えるのは、先の大手の「価格競争」に巻き込まれないためです。まぁ、「価格」や「原価」という視点自体が、アメリカ資本主義の最大の弱点(あるいは強み)なので、その土俵に「乗ってあげない」という方法です。

■経費をトータルコストで捉える

価格競争なり株式なり投資なりの場合「お金」が最大の基準になります。この幻想は強くて、なかなか離れられないものなのですが、何も「世捨て人」になったり「仙人」にならなければ、離れられないかというとそうでもありません。

「行動経済学」やら「昨今のフジTV」やらを考えると、バイアスという考え方が良くわかります。また「不安を煽る」やり方や「幸運を煽る」というやり方、があまり楽しくない手法であることが分かります。

「満足度」という言葉がありますが、例えば、月収30万円で満足する人が40万円貰えるようになるのと、月収100万円で暮らしている人が40万円貰うようになるのとは、どう「満足度」が違うのでしょうか。当然、後者のほうが「不満足」で、前者のほうが「満足」ですね。同じ40万円という「お金」が、実は個人の満足度には直結しません。なので、GDP が高いとかが国民を満足しさせないから…と言って満足度指数を測るというのも変な話ですね。個々人で「満足」とは何かは違うのですから。

なので、マクロ的な満足度はさておき(おそらく幻想の匂いがします)、ミクロ的な満足を考えます。いわゆる、

  • 家族が幸せに暮らせるとはどういうことか?
  • 自分が満足した日々を送れるとはどういうことか?
  • 健康が満足とどう関係しているのか?
  • 年金や消費税や原発問題などが、自分の満足度とどう関係しているのか?

ということです。極めて個人主義的な考え方ですが、個々人の満足により死に至る争いをする率が減るのであれば(進化論的にはそうはいかないのですが)それはそれで良いのだろうし、ある程度ミクロがまとまってマクロになるであろう、という推測です(勿論、マクロだけを取り扱って満足な方もいるんですけどね、あの国会議事堂にいらっしゃる方とか)。

こうなると、

  • ある程度の満足が得られる収入を持つ
  • ある程度の満足が得られる仕事を持つ
  • 満足が不満足になるゴシップから遠ざかる

のもひとつの方法です。「収入」と書きましたが、お金(札束)である必要はなくて、自分の生活を満足に保てる程度の現品収入でもよいのです。それ以上は、満足度を大きく押し上げる要因ではありません。自分が満たされているというラインよりもちょっと上であれば十分なのです。
なにか宗教がかっているように見えますが、式で書くとこんな風です。

M < m + x

自分が満足される仮定値 M とすると、現実の収入 m よりも若干 x だけプラスであればよいのです。これが仮定値 M よりも若干大きければ「満足」になります。ここが不等号になるのは、x という余剰が、自分の仮定値 M とイコールになる場合には、あまり満足できない、ということを示したいためです。

さて、話を元に戻すと、この「若干」の部分を捉えるためには、駒かいところに注力しても意味がないということになります。仕事を受けるなり、収入を得るなり(雇用で自営であれ)ときには、

  • 戦略的なビジネス価値(いわゆる、ハイリスク・ハイリターン)
  • 安定した収入の確保(いわゆる、ローリスク・ローリターン)

を別々に考える必要があるということです。社員であるときには、ビジネス的な価値を考えることはほとんどなかったのですが(「会社のお金」を使ってビジネス的な価値を考えることはあっても、それは税金を使って東電を助ける、ぐらいの「身に迫らない」発想でしかありませんでした)、これを分かれ目を明確に決めないと、自分の人生を食いつぶしてしまいます。特に自営の場合は。

という訳で、手早く済ませる仕事であれば「経費」プラス「若干の余剰」を見込んで請求する。この若干の「余剰」というのは、先の「M < m + x」を満足させるものであったり、ビジネス的な価値を創造させるための原資であったりします。

なので、ビジネス的な価値を生み出す場合は、

  • 経費的なコストは、感知しない。
  • ただし、期間、必要経費を明確に管理する。

ことが必要です。投資でいうところの損きり、ベンチャーキャピタルで言うところの確率的なものですね。なので、できあがらない場合、リターンが見込めない場合は、ビジネス的にシビアに切ることが必要です。

と言う訳で、だらだらと書きましたが、「単価戦略に乗らない」のと「経費をトータルで考える」の2点を抑えて、今年はやっていく予定です。ええ、「私の好きにやらせて頂きます」という方針ですね。

カテゴリー: 雑談 | 1件のコメント

MSTest の実力は如何ほどのものなのか?

今更ながら、Visual Studio 2010 の Test Framework を試してみました(NUnit は既に試しているのですが)。

  • テストプロジェクトを自動で生成してくれる。
  • テストメソッドのひな型を自動で作ってくれる。

というのが売りらしいのですが、メソッドのひな型のほうは慣れると普通に作れます…というか、最初のひとつが面倒なだけで、あとはコピー&ペーストでいけます。

実行したいメソッドのところで、右クリックして「テストの実行」を選択すると

指定したメソッドが実行できます。

テスト結果は、Visual Studio 上で確認できます。
右上にある「デバッグ」で実行させるとブレークポイントで止めることができるし、結構便利です。

NUnit を使う場合は、デバッグするときの「外部プログラムの開始」を設定するとできるので、同じことができるのですが指定したメソッドだけを動かすというのがなかなか難しいので(GUI 上で選択しないといけないし)。

ちなみにコマンドラインで動かす場合には、MSTest.exe を使います。

mstest /testcontainer:EXHtmlDoc.Test.dll

 

で、NUnit でも MSTest でも、この手の自動単体テストツールを使うといいのは、比較的ごちゃごちゃしたコードでも、そこそこ動くようなものが短時間で作れるということです。

次のような、ごちゃっとしたコードが、

class HtmlTokenizer
{
	public static Dictionary<string, string> SplitAttribute(string xml)
	{
		var WHITESPACE = new char[]{ ' ', '\n' };
		var WHITESPACE_END = new char[] { ' ', '\n', '>' };
		var WHITESPACE_END_EQUAL = new char[] { ' ', '\n', '>','=' };

		Dictionary<string, string> attrs = new Dictionary<string, string>();

		if (xml.Length == 0) return attrs;
		if (xml[0] != '<') return attrs;
		xml = xml.Substring(1);
		int pos = xml.IndexOfAny(WHITESPACE_END);
		if (pos == -1) return attrs;
		xml = xml.TrimStart(WHITESPACE);

		string tagname = &quot;&quot;;
		pos = xml.IndexOfAny(WHITESPACE_END);
		if (pos == -1) return attrs;
		tagname = xml.Substring(0, pos );
		xml = xml.Substring(pos + 1);

		while (xml.Length > 0)
		{
			string attrname = &quot;&quot;;
			string attrvalue = &quot;&quot;;
			xml = xml.TrimStart(WHITESPACE);
			if (xml.Length == 0) break;
			if (xml[0] == '>') break;

			pos = xml.IndexOfAny(WHITESPACE_END_EQUAL);
			if (pos == -1) break;
			if ( xml[pos] != '=' )
			{
				// 属性名のみ
				pos = xml.IndexOfAny(WHITESPACE_END);
				if (pos == -1) break;
				attrname = xml.Substring(0, pos);
			}
			else
			{
				attrname = xml.Substring(0, pos).TrimStart(WHITESPACE);
				xml = xml.Substring(pos + 1).TrimStart(WHITESPACE);
				if (xml.Length == 0) break;
				if (xml[0] == '\'')
				{
					pos = xml.IndexOf('\'', 1);
					if (pos == -1) break;
					attrvalue = xml.Substring(1, pos - 1);
					xml.Substring(pos + 1);
				}
				else if (xml[0] == '&quot;')
				{
					pos = xml.IndexOf('\&quot;', 1);
					if (pos == -1) break;
					attrvalue = xml.Substring(1, pos - 1);
					xml.Substring(pos + 1);
				}
				else
				{
					pos = xml.IndexOfAny(WHITESPACE_END);
					if (pos == -1) break;
					attrvalue = xml.Substring(0, pos);
					xml.Substring(pos);
				}
			}
			if (attrname != &quot;&quot;)
			{
				attrs.Add(attrname, attrvalue);
			}
			xml = xml.Substring(pos + 1);
		}
		return attrs;
	}
}

次のようなテストコードを記述することで、1時間弱で作れます。

	/// <summary>
	///SplitAttribute のテスト
	///</summary>
	[TestMethod()]
	public void TestSplitAttribute()
	{
		string xml = @&quot;<a href='http://moonmile.net'>&quot;;
		Dictionary<string, string> expected = new Dictionary<string, string>();
		expected.Add( &quot;href&quot;, &quot;http:///moonmile.net&quot; );
		Dictionary<string, string> actual = HtmlTokenizer.SplitAttribute(xml);

		Assert.IsNotNull(actual);
		Assert.AreEqual(1, actual.Count);
		Assert.AreEqual(true, actual.ContainsKey(&quot;href&quot;));
		Assert.AreEqual(&quot;http://moonmile.net&quot;, actual[&quot;href&quot;]);

		xml = &quot;<a href=\&quot;http://moonmile.net\&quot;>&quot;;
		actual = HtmlTokenizer.SplitAttribute(xml);

		Assert.IsNotNull(actual);
		Assert.AreEqual(1, actual.Count);
		Assert.AreEqual(true, actual.ContainsKey(&quot;href&quot;));
		Assert.AreEqual(&quot;http://moonmile.net&quot;, actual[&quot;href&quot;]);

		xml = &quot;<a href=http://moonmile.net>&quot;;
		actual = HtmlTokenizer.SplitAttribute(xml);

		Assert.IsNotNull(actual);
		Assert.AreEqual(1, actual.Count);
		Assert.AreEqual(true, actual.ContainsKey(&quot;href&quot;));
		Assert.AreEqual(&quot;http://moonmile.net&quot;, actual[&quot;href&quot;]);
	}
	/// <summary>
	///SplitAttributeのテスト
	/// 属性が複数ある場合
	///</summary>
	[TestMethod()]
	public void TestSplitAttributeDual()
	{
		string xml = @&quot;<a href='http://moonmile.net' title='moonmile solutions' >&quot;;
		Dictionary<string, string> expected = new Dictionary<string, string>();
		expected.Add(&quot;href&quot;, &quot;http:///moonmile.net&quot;);
		expected.Add(&quot;title&quot;, &quot;moonmile solutions&quot;);
		Dictionary<string, string> actual = HtmlTokenizer.SplitAttribute(xml);

		Assert.IsNotNull(actual);
		Assert.AreEqual(2, actual.Count);
		Assert.AreEqual(true, actual.ContainsKey(&quot;href&quot;));
		Assert.AreEqual(&quot;http://moonmile.net&quot;, actual[&quot;href&quot;]);
		Assert.AreEqual(true, actual.ContainsKey(&quot;title&quot;));
		Assert.AreEqual(&quot;moonmile solutions&quot;, actual[&quot;title&quot;]);
	}
	/// <summary>
	///SplitAttributeのテスト
	/// 属性名のみの場合
	///</summary>
	[TestMethod()]
	public void TestSplitAttributeOnlyAttributeName()
	{
		string xml = @&quot;<a mark href='http://moonmile.net'>&quot;;
		Dictionary<string, string> expected = new Dictionary<string, string>();
		expected.Add(&quot;mark&quot;, &quot;&quot;);
		expected.Add(&quot;href&quot;, &quot;http:///moonmile.net&quot;);
		Dictionary<string, string> actual = HtmlTokenizer.SplitAttribute(xml);

		Assert.IsNotNull(actual);
		Assert.AreEqual(2, actual.Count);
		Assert.AreEqual(true, actual.ContainsKey(&quot;href&quot;));
		Assert.AreEqual(&quot;http://moonmile.net&quot;, actual[&quot;href&quot;]);
		Assert.AreEqual(true, actual.ContainsKey(&quot;mark&quot;));
		Assert.AreEqual(&quot;&quot;, actual[&quot;mark&quot;]);

		xml = @&quot;<a href='http://moonmile.net' mark >&quot;;
		actual = HtmlTokenizer.SplitAttribute(xml);

		Assert.IsNotNull(actual);
		Assert.AreEqual(2, actual.Count);
		Assert.AreEqual(true, actual.ContainsKey(&quot;href&quot;));
		Assert.AreEqual(&quot;http://moonmile.net&quot;, actual[&quot;href&quot;]);
		Assert.AreEqual(true, actual.ContainsKey(&quot;mark&quot;));
		Assert.AreEqual(&quot;&quot;, actual[&quot;mark&quot;]);
	}
}

まあ、あまりにもごちゃごちゃし過ぎているので、後でテストコードで確認しながらリファクタリングをしますが。

このあたりの書き方は、

  1. オブジェクトクラスで設計する。
  2. メソッドの役割を決める。
  3. メソッドのフローチャートを書く。
  4. テストコードを書く。
  5. テストコードが動作するように、メソッドの実装を書く。

というコーディングの仕方では、絶対できません…と言いますか、少し入り組んでしまったコードの場合、設計から始めると大抵の場合破綻します。

なので、

  1. 単純に動くテストコードを2,3件作る。
  2. テストコードから、どのようにメソッドを呼び出すかを決める。
  3. テストコードがコンパイルできるように、メソッドを記述する。
  4. テストが通るようにメソッドを記述する。
  5. 大まかなところをメソッドで記述する。
  6. 記述した実装が通るか、テストで確認する。
  7. 5と6 を繰り返す。たまに逆もやってみる。
  8. コードがごちゃごちゃし始めたら、テストコードを元にリファクタリングする。

という書き方になります。
実は 2 のところが重要で、テストからうまく実装クラスを呼び出せない場合は、先行き、複雑化しすぎてバグを含み始めるという(経験上の)現象があります。あるいは、複雑すぎて使えないクラスが大量にできることになります。
なので、利用可能な範囲で、メソッドを「直感的に」使えるようにしておくのが基本です。

まあ、そういう訳で後で SplitAttribute を書き直しますか。

カテゴリー: C#, xUnit | 1件のコメント