F#で試す関数型言語
.NET関係のプログラミング言語は列挙するとかなり多い。
- C#
- Visual Basic .NET
- C++/CLI
- J#
- JScript .NET
- F#
- IronPython
- IronRuby
- Dynamic VB.NET
- Managed JScript
- Windows PowerShell
まぁいろいろある。最初の5つは専らコンパイルして使うもの、下6つはインタプリタで使うもの。
PowerShellはさておき、IronPythonからManaged JScriptまでの5つは.NET Framework 4.0からはDLRで動くことになってる。
いろいろありますが、その中で異色なのがF#だと思う。これは他の言語とは異なり、関数型言語なのだから。
関数型言語と言えばLispとかHaskellが有名ですよね。まぁ何書いてるか全く分からんが。
その関数型言語でOCamlというやつがある。割と有名だが。
これを元にして.NET用に作られたのがF#ということになる。FはfunctionalのFでしょうね。
元々OCamlと互換性があったのだが、現在はF#独自の要素が増えているよう。
今まで関数型言語はやったことがないから、ちょっとこれで遊んでみることにした。
なんてったてF#はVisual Studio 2010に標準で搭載されてるからね。
VS2010 Command Promptで fsi と打ち込む。するとF# Interactiveが立ち上がる。対話的にF#ができる。
とりあえずやってみますかね。
let rec fact n = if n=0 then 1 else n*fact (n-1);; fact 10;;
こんな風に打ち込む。;;は文の終わりを表すものね。忘れるとうまくいかんので。
これは階乗を求める関数factを定義したものだが、ちょっと変な感じ。
letというのは昔のBASICで変数への代入に使ってたが、F#では違う。値を束縛したり関数を定義するのに使う。
値を束縛するというのは定数の定義に近い気がする。
そして上でやってるのは関数の定義。nという引数を取る関数factを定義している。
再帰的に呼び出すときにはrecを付ける必要がある。
これで再帰的に階乗を求める関数が書けた。
しかし他にも階乗の求め方はあるわな。1..nを全部掛ければいいわけですよ。
という発想でやるならばリストとSeq.reduceを組み合わせるといい。
let fact n = [1..n] |> Seq.reduce (*);;
[1..n]でPerlなどと同じように連続したリストを作ることができる。
|>は前の結果を次の関数へ渡すもの。Seq.reduce (*) [1..n] と書くのと同じ。
(*)はかけ算をする関数を表すもの。
let mul x y = x*y let fact n =[1..n] |> Seq.reduce mul
こう書くのとそう変わらない。こんな風に関数を受ける関数というのを高階関数と言うそうな。
まぁしかしこういうのはRubyではよく見ますよね。
こんな風に前にletで用意しといてもいいけど、その場で匿名関数として用意することも出来る。
let fact n = [1..n] |> Seq.reduce (fun x y->x*y)
まぁこんな風に関数をガリガリするから関数型言語なのね。
他にもいろいろやったのだが、いろいろおもしろいものがあった。
そして結構Rubyとかで取り入れられてるなとも思った。さっきのSeq.reduceはEnumerable#injectに取り入れられてるしね。
関数型言語はスマートに書けるものであることは確かなようです。
まぁけどなかなか慣れんけどな。まぁそれは追々。
Author : hidemaro
Date : 2010/05/19(Wed) 23:58
.NET Framework | comments (0) | trackback (0)
Windows Live Writerに機能を追加する
昨日、XML-RPCを使った投稿できるようにしたがバグを発見して少々修正していた。
それでいろいろ試していたのだが、どうもxfy Blog Editorには連続した空白の取り扱いがおかしいようだ。
あと編集時の挙動がおかしい。まぁこれは僕のミスかも知れませんが。
Windows LIve Writerなら問題ないようなのでそれを使おうかなと思った。が、少々問題があった。
というのが今挿入した水平線。Windows Live Writerには水平線を挿入する簡単な方法がないのです。
ではどうしようか。挿入する方法を自分で作ればいいんですよ。そういうことができるのがこのソフトのすごいところかな。
Windows Live Writer用のプラグインを開発する(japan.internet.com デベロッパー)
.NETの開発環境があれば簡単にできるようです。Visual Studio 2010 Bataでやった。
C:\Program Files\Windows Live\Writer\WindowsLive.Writer.API.dll を参照設定に追加して、プラグインクラスをWindowsLive.Writer.Api.ContentSourceを継承して作るだけですね。
この記事に書かれていることに沿って作れば<hr />を挿入するだけのものは簡単に作れると思います。
ただ注意するべきことが2点ありまして、1つは.NET Frameworkのバージョンですが4.0ではだめでした。2.0ならいけた。
それとImagePathですが、これは画像のサイズは16x16らしいが間違っているようだ、ということと画像ファイルをリソース埋め込みするのを忘れないようにということ。
さてソースコードを貼ろうと思っても実は整形済みテキストも簡単には入れられないのです。
まぁさすがにこれはCode Snippet plugin for Windows Live Writerなどあるのでわざわざ作る必要ないかも知れない。
ただ、ソースコードでもないのにこれを使うのはなぁと思ったのでこれも作った。
内容は出てきたダイアログに入れて、それで挿入とすればコードを生成して挿入するという風にした。
簡単簡単。こうやって整形済みテキストも挿入できるようになりました。
ソースコードはこんな感じ。
namespace WriterHR {
[WindowsLive.Writer.Api.WriterPlugin("{7d6bdcb9-1628-477a-a52a-88df578b5a34}", "HR inserter",
Description = "水平線を挿入します。", HasEditableOptions = false, Name = "HR inserter",
PublisherUrl = "http://hdmr.org", ImagePath = "hr.gif")]
[WindowsLive.Writer.Api.InsertableContentSource("水平線")]
public class HRInserterPlugin : WindowsLive.Writer.Api.ContentSource {
public override System.Windows.Forms.DialogResult CreateContent(System.Windows.Forms.IWin32Window dialogOwner, ref string content) {
content = "<hr />";
return System.Windows.Forms.DialogResult.OK;
}
}
}
水平線挿入するのはたったこんなけです。
整形済みテキストはダイアログ表示させてOKだったらそのテキストボックスの内容をStringBuilderに入れてReplaceやらしてから書き出してるだけ。
本当はこれを公開したいのですが、ちょっとWindows Live Galleryへの登録法がわからないのでそれは後にでも。
dllファイルだけ配布してもいいんだが、それではなんか残念ですし。
Author : hidemaro
Date : 2009/12/27(Sun) 19:43
.NET Framework | comments (0) | trackback (0)
余白0mmなのに余白10cmになる不思議
パラメータの説明を書き忘れたので何だったけなとソースコードを見るはめに。
だめですね、ちゃんと説明を書いておかないと。
というわけで書いておこうと思う。
さて、自作したプログラムなのでなかなか調子も悪い。
何が悪いかというと余白の取り扱いで、余白10mmにしても余白がないという不思議。
左余白15mmにして8mm程度の余白ができるんですよ。なんかおかしいですね。
一方右余白は0mmなのに10mmある。不気味ですね。
.NET Frameworkの提供する能力をほぼそのまま使っているからかなぁ。
今回の原稿は最終的に横幅195mmになったんですが、左余白15mm・右余白0mmになりました。
PDFで出すと不気味なんですが、直接出力するとそんなにおかしくない。
なんか変な感じです。
プリンタの操作というのは難しいです。
できるだけパラメータのチェックは仮想プリンタであるPDFプリンタに出力してチェックしています。
だって紙もったいないから。
けど余白パラメータの設定だけはそうもいかないんですね。
今回は3回目でいけた。勘がよかったからかな。
去年、友人が印刷関係のプログラムを作っていたんですが、実際動作させる環境になるとうまくいかんと調整していた覚えがある。
確か同じ型のプリンタで実験してなかったっけと思ったんですけど、なぜかうまくいかんと。
自作プログラムではそうですが、さすがにAdobe Readerとかはどの環境でもきっちり出力できますよね。
すばらしい!! ありがたみがよくわかりますね。
しかし、きっちり印刷できるフレームワークとかないんかねぇ。
調べたことないんだけど、あったら使いたかったなとか。
まぁ今はこのプログラムでやっていきますか。
Author : hidemaro
Date : 2009/08/24(Mon) 23:45
.NET Framework | comments (0) | trackback (0)
C# 4.0の残念なタプル
その人がC# 4.0についていろいろ書いているのだが、
どうもタプルが導入されるそうです。
タプルとはなにかというのをちょっと考えてみる。
Rubyで整数・整数・文字列の組み合わせを表すなら単に配列を使えばいいねというのは誰もが考えること。
x=[123,1,'カール']配列とかハッシュとかというのは便利なものです。
puts "#{x[2]} #{x[0]*x[1]}円"
しかしこれと同じことをC#でやろうとすると厄介。
だって配列は同じ型しか入れられないから。
intとintとstringの入る配列はobject[]だが、そうすると型の情報が消えてしまう。
Rubyの場合は型は動的に判定するから配列になんでもかんでも投げ込んでいいが、
なかなかそうもいかない。
まぁそのためにいちいちクラスを作ればいいんだがそれもめんどくさい。
というわけでタプルという道具があるわけだ。
Tuple<(Of <(T1, T2, T3>)>) Class
ジェネリックを使っていますね。最近はおなじみになってきたよね。
ここでTuple<int,int,string>を使えばさっきの条件を満たすものが作れる。
このTupleは作ったら内容は変えられないようです。
とりあえずこれを使えばこんな風にさっきのことはかける。
System.Tuple<int,int,string> x=new System.Tuple<int,int,string>(123,1,"カール");なるほどね。
System.Console.WriteLine("{0} {1}",x.Item1*x.Item2,x.Item3);
とはいうもののその人はあんまりおもしろくないとも言っていた。
というのも多重代入とかできないしと。
多重代入というのはPerlやRubyではおなじみですね。(参考記事 : 座標のように代入する変数の入れ替え)
タプルといえばこれでしょと。
Rubyでもさっきのxについてこんなことはできます。
price,pieces,name = *xまぁ*yに代入する相当のことはタプルを作ることなのでできますけどね。
*y = price,pieces,name
x,y=y,x
まぁ確かにこう言う仕組みが欲しいなぁという気持ちはありますよね。
とはいえ、この辺は言語の仕様を変えないと。
そのためにはタプルのリテラルがいりますよって。
そういうのがないとようやらんでって。そこまではC# 4.0ではできないようです。
しかし多重代入ってありがたい機能なんですけどね。
なんでかというのはさっきの参考記事でも見てください。
Author : hidemaro
Date : 2009/07/29(Wed) 23:57
.NET Framework | comments (0) | trackback (0)
再利用がはかどるC# 4.0の共変性・反変性
これについては別の所に書いたのでもう書かない。
まぁ曇りだったけど少しは見えたかな。なかなかよかったね。
ちょっと前に教えてもらったんだが、C#の配列はなかなか変である。
というのもこういうことができてしまう。
string[] strs=new string[] { "a","b" };
object[] objs=strs;
objs[1]=System.Text.Encoding.UTF8;string[]をobject[]にキャストできると。しかも暗黙的に。問題ではないと思う人もあるかもしれんが、strsとobjsは同じものを指しているというのが問題。
string型配列として作られたところにそれ以外の型のオブジェクトを入れることはできない。
というわけで代入するときにSystem.ArrayTypeMismatchExceptionが出る。
なのでこのキャストは明らかに不正なんですが、コンパイルは通ってしまう。
まぁこんな不気味な変換はやったことなかったから知らなかったわ。
同じようなことはSystem.Collections.Generic.List<T>なんかにはできません。
そりゃ一般に通用する技じゃないですからね。
これがC# 4.0から変わるらしいです。
その前に今まででもできていたことを書いておく。
static string ToString(object obj) {
return obj.ToString();
}
static void Main(string[] args) {
Func<object,string> fo2s=ToString;
Func<string,object> fs2o=ToString;
}object型を引数にとりstring型を返すメソッドがあったと。このメソッドはFunc<object,string>デリゲートを使って表すことができると。
けどこのメソッドはobject型という範囲を狭めてstring型に限定して、返すstring型はobject型でもあると拡大解釈して、
string型を引数に取り、object型を返すメソッドであるとしてFunc<string,object>デリゲートでも表せる。
不思議な性質ですよね。けど理屈には合ってる。
引数の型の範囲を狭める方を反変性、返り値の型を拡大解釈する方を共変性と言うそうだ。
まぁ詳しいことはデリゲート#covariance と contravariance (++C++)を見て欲しい。
このことによれば入口はどれだけ狭めても構わない、出口はいくら広げても構わないと。
これをC# 4.0からはジェネリックにも拡大しましょうということです。
残念ながらSystem.Collections.Generic.List<T>のメソッド・プロパティは入口にも出口にもTを使っている。
例えばインデクサ(mylist[idx]の形式でアクセス出来るプロパティ)では入口であるセッターも出口であるゲッターもT型。
残念ながらそれは叶わない。
けど出て行く一方のSystem.Collections.Generic.IEnumerable<T>とかは共変性が認められると。
すなわちIEnumerable<string>はIEnumerable
IronRubyを試してみる
計算結果にエネルギーが蓄えられるわけではないですし。
まぁ実のところ熱になっているだけで、そうとするならばコンピュータは暖房だということになる。
最近は低消費電力のCPUとか流行ってますけど、そりゃ当然のことではありますよね。
だって究極的には電力いらないはずなんだから。
白熱電球がいかに効率が悪いと言われても、あれは光というエネルギーに変換されてるわけですから。
さて、なんかIronRubyとかあるので試してみた。
IronRuby
IronPythonのパクリのような気もしますが、まぁそういうものです。
iirbを起動してみた。IronRubyのirbだからiirbなんですね。
あと本体の名前はirなんだね。多分IronRubyだからってirbにしたら対話的コンソールのirbとかぶるからだと。
起動遅いですけど、これは.NET Frameworkの初期化が結構遅いというのが理由かなと。
IronPythonのもっとも残念だったのはそこだと思います。それはIronRubyでも一緒ですね。
まぁ.NET Framework 4.0からDLRの内蔵が始まるみたいだからこれによって速くなったらうれしいなとかいうのはある。
それまでは残念ですが広く使うのは難しそうです。
起動したら普通のRubyのようにirbできるはず。
p (1..100).map{|x| x**2}.inject(0){|s,i| s+i}基準となってるRubyのバージョンはよくわからないけど1.8.7以前であることは間違いないでしょう。さてここからがむしろポイントで.NETのライブラリを使えるよって所です。
まぁ試してみましょうか。
System::Console.WriteLine "Foobar".GetType.ToStringまず、Rubyのオブジェクトの型を聞いてみると、IronRuby.Builtins.MutableStringという。
fs=System::IO::StreamReader.new('C:\work\req.txt')
begin
puts fs.read_to_end
ensure
fs.Close
end
a=System::Array[System::Int32].new(10)
(0..a.Length-1).each do |i|
a[i]=i**2
end
puts a.inject(0){|s,i| s+i}
l=System::Collections::Generic::List[System::Int32].new(a)
f=System::Action[System::Int32].new do |x|
x.times do
print "#{x}"
end
print "\n"
end
f.Invoke(10)
l.ForEach(f)
IronPythonではこれについてはSystem.Stringであると言ってきたので違いますね。
ところでRubyではメソッドの呼び出しに括弧は不要なのでこんな調子でOKです。
一見するとメソッドかプロパティか区別がつきませんが、これはRubyのよいところではありますね。
System::Console.WriteLineというのはちょっと変わった表記ですね。なんで::やねんと。
どうもIronRubyではクラス名まではこう書くみたいです。
System::ConsoleってのはRubyの言葉で言うと、Class型のオブジェクトなんですね。
まぁ.NETなんかの感覚だとわからないけど、Rubyとかはクラス名自体がオブジェクトなんですね。
というかクラスの定義自体Classクラスの拡張という形でやっているはずだし。
なのであえてSystem::ConsoleのWriteLineメソッドっていうわけでSystem::Console::WriteLineとは書いてません。
実際はSystem::Console::WriteLineでも使えるんですけどね。
それでStreamReaderを使ってみましたが、特にusing構文はないよと。
そういうわけでRubyのbegin~rescue~ensure~end構文で書いています。
Pythonではwith構文ってのが追加されたはずだけどRubyではそんなものいらないので。
ところで.NETってメソッド名はReadToEndのように書きますけど、Rubyではread_to_endのように書くんですね。
これを読み替えて使えるみたいです。
Ruby使いにも親しみやすいようにとのこと。
ジェネリックな型はSystem::Collections::Generic::List[System::Int32]のように書きます。
そういえばIronPythonもこうでしたね。
それで配列もSystem::Array[System::Int32]のようにかけるようになっています。
配列の作成は個数指定でやるのは普通にnewでいけますね。
ただRubyの配列からのコピーはどうやってやるんだろ。この辺はIronPythonと逆ですね。
Rubyといえばブロック付きメソッド呼び出しですが、これはデリゲートの作成に使えます。
できたデリゲートはRubyのProcみたいにf[10]とかできるかと思いきやできない。
残念。こうやってInvokeメソッド使えばいいですね。
List<T>のForEachメソッドに渡しているが、この引数を省略してブロック付きメソッド呼び出しではだめみたい。
まだかゆいところに手が届かないという感じではありますけど悪くはないですね。
最後のデリゲート型の引数を省略してブロック付きメソッド呼び出しできるようにしたり、
RubyのArrayと.NETのArrayの変換を簡単にしたりというのはやってほしいものですが。
まぁIronPythonも当初は散々でしたから今後改善される分もあるでしょうし。
ただ何よりも問題なのは初期化に時間を食うこと。
それさえ無ければ全部IronRubyのアプリケーションを作ったりしても楽しいと思うんだけどな。
まぁ最近は.NETで開発しなきゃいけないことは減りましたけど、やっぱりWindowsフォームは.NETで作ると楽ですからね。
この辺でIronRubyとか活用して行けたらと思うのだけど…
まぁ期待しておきましょう。
Author : hidemaro
Date : 2009/05/09(Sat) 23:42
.NET Framework | comments (0) | trackback (0)
C# 3.0にはinjectもmapもgrepもuniqも住んでいる
やはりデリゲートを(sender,e)=>{ } とかすれば作れるのは便利ですね。
C# 3.0の新機能ですが、ありがたいものです。
それで新しくIEnumerable<T>の拡張メソッドがいろいろあるわけですが、
中にはRubyやらPerlで身近なものも結構あることに気付いた。
まぁとりあえず見てみよう。
static void Main(string[] args) {
Dictionary<string,int> hash=new Dictionary<string,int>();
hash["Foo"]=32;
hash["Bar"]=53;
hash["John"]=52;
hash["Taro"]=54;
hash["Ken"]=62;
hash["Bob"]=65;
System.Console.WriteLine(hash.Select((kv) => kv.Value).Aggregate(0,(sum,cur) => sum+cur));
foreach (var group in hash.Where((kv) => kv.Value>50).OrderBy((kv) => kv.Key).GroupBy((kv)=>kv.Value/10)) {
System.Console.Write("{0} : ",group.Key);
foreach (var cur in group) System.Console.Write("{0} ",cur.Value);
System.Console.WriteLine();
}
Random myrnd=new Random();
var uniq=Range(1,100).Select((dummy) => myrnd.Next(100)).Distinct().OrderBy((cur)=>cur);
foreach (int cur in uniq) {
System.Console.Write("{0} ",cur);
}
System.Console.WriteLine();
}
static IEnumerable<int> Range(int from,int to) {
for (int i=from; i<=to; i++) yield return i;
}PerlやRubyのmapはSelectまたはSelectManyと覚えておきましょう。SelectはSQLの言葉ですね。SelectManyはリストを渡したら開いてくれる、Selectはそういう処理をしてくれないやつ。
Rubyのinject、PerlのList::Util::reduce相当のものがAggregate、
一見気付きませんよね。けどこれです。ここでは総和を求めている。まぁSum使えばいいんだけどね。
Whereはgrep相当のものです。まぁこれもSQLの言葉です。というか大概SQL由来だったりします。
SortByだってSQL由来ですね。IEnumerableをソートするというのも相当不気味な状況ですが。
Rubyのuniq、PerlのList::MoreUtils::uniq相当のものはDistinct、言うまでもなくSQL由来。
ところで標準では範囲でIEnumerable作れなかったから、Rangeという静的メソッドを用意しておいた。
これで1から100までの数字を順番に渡していくオブジェクトが出来た。
結構いろいろありますね。
あとList::MoreUtils::natatimeとかあればいいんですけどね。
それと、ForEach拡張メソッドとかも用意して欲しかったな。
いや、C#のforeach構文とは別のものなんですけどね。
System.Collections.Generic.List<T>クラスにあるんですけどこんなの。
List<int> list=new List<int>();引数にデリゲートを取るわけです。なんの意味があるんだという話ですが、
list.ForEach((int cur) =>
{
System.Console.Write("{0} ",cur);
});
実はちょっと性能がいいらしいです。不思議な話ですが。
メソッド呼び出しの回数がだいぶ違うそうです。IEnumerable使ってると呼び出しが多いんだとか。
まぁそういう理屈で言えば、IEnumerableの拡張メソッドにForEachなんて用意しても性能では意味はないけど、
しかしList<T>で使えて、他のIEnumerable<T>でも使える方がおもしろいと思うんだよね。
まぁそんなところ。
たまには活用してあげてください。Aggregateとかすごい活用しがいがあると思いますよ。
あとはSelectぐらいかなぁ。Whereも時々役に立つかも知れないけど。
そうそう、C# 3.0から型推論に対応しているから、型が明らかならvarを書くとタイプ量が減らせる。
var uniqの行のvarは本来ならSystem.Linq.IOrderdEnumerable<int>と書くべきなのだが、それはあまり重要じゃない。
というわけでvarとか使ってます。
ただ、foreach (var cur in group)のvarは、System.Collections.Generic.KeyValuePair<string,int>だから結構重要かも。
だから書いておいた方がいいかもしれないけど、Visual Studioなら、そのvarのところにカーソルを重ねれば型名が見れる。
なのでその点ではあまり心配ないかも。
なお型推論するのはコンパイラなので、コンパイルしてできたコードはちゃんと型名を書いたのと一緒ですよ。
そういう面でも心配する必要はないですね。
Author : hidemaro
Date : 2009/04/15(Wed) 23:49
.NET Framework | comments (0) | trackback (0)
C#で動的型なんて流行らないと思うけど
IronPythonで開発してて思ったが、.NET Frameworkのライブラリは複雑すぎる。
System.Drawing.SizeとSystem.Drawing.Pointを使い分ける意味がわからない。
別に大きさ2のタプルでもいいじゃないか? と思わんこともない。
そういう都合を考えると、コンパイル時確認していただける仕組みは便利なんですよ。
そしてVisual Studioもそのように設計されている。
C# 3.0で型推論のvarが導入されたけど、あれは匿名型を使うときに必要だからですね。
var foo=new {first="Taro",last="Yamada"};この無名の型の名を知らないから、varというキーワードを使うと。それ以外にも使い道はあれど、あまり意味はないでしょう。
もっとも動的型導入の理由というのはJavaScriptとの連携とかいうのもあるそうだが。
.NET Frameworkのリフレクションの力でどの程度できるか試してみた。
といっても当然のことを書いてるだけですが…
public static class DynamicWorld {
static public object call(this object self, string name, params object[] args) {
return self.GetType().GetMethod(name, Type.GetTypeArray(args)).Invoke(self, args);
}
static public object prop(this object self,string name) {
return self.GetType().GetProperty(name).GetValue(self, null); ;
}
static public void prop(this object self, string name, object value) {
self.GetType().GetProperty(name).SetValue(self, value, null);
}
static public object newobj(this string type, params object[] args) {
return Type.GetType(type).GetConstructor(Type.GetTypeArray(args)).Invoke(args);
}
static public object callstat(this string type, string name, params object[] args) {
return Type.GetType(type).GetMethod(name, Type.GetTypeArray(args)).Invoke(null, args);
}
}未だC# 3.0をまともに使ってるわけではないが、これはC# 3.0が前提。これ拡張メソッドなのよね。
sb.prop("Length", 0);
DynamicWorld.prop(sb,"Length",0);下のように表記すべきを上のように簡略化できると。とりあえずインスタンスのメソッドとプロパティにだけ対応させてみた。
本当はインデクサとかにも対応できるのだがかなりめんどくさいので。
IronPythonも結局こういうものの固まりだと思う。
さて、試しに全部これでプログラム書いてみた。
object sr="System.IO.StreamReader".newobj(@"C:\test.txt");拡張メソッドと可変長引数のおかげでそれなりに違和感なく使えます。
object sb="System.Text.StringBuilder".newobj();
sb.prop("Length", 0);
while (!(bool)sr.prop("EndOfStream")) {
sb.call("AppendLine", sr.call("ReadLine").call("Trim").call("PadRight", 20, '_'));
}
sr.call("Dispose");
"System.Console".callstat("Write",sb);
"System.Console".callstat("ReadKey", false);
ただ、条件はbool型だろとかいうので、型変換が必要だったりというのはある。
しかしこれだけで動的に呼び出しているのです。
試しに、どっか大文字と小文字を入れ替えてみる。
コンパイルは通るものの、動かすとどっかで例外が出る。
これが動的に呼び出している証拠です。
作ってみて言うのもあれだが、あんまり使い道は思いつきませんけどね。
ただ割と簡潔に書けていますね。
.NET Frameworkのリフレクションの強さの証明と言えないこともない。
本当になんでもありの環境ですね。
それにしてもC# 4.0か…けど.NET Frameworkは2.0系のままなんだろうな。
だから動的型使ったソースコードも上に書いたようにされるのかな?
まぁそんな風に思わんこともない。
Author : hidemaro
Date : 2008/11/23(Sun) 23:10
.NET Framework | comments (0) | trackback (0)
ラベルで作る切り絵はおもしろい
11月はゆとりある一ヶ月になってほしいですね。
けどそれは無理だと思う。試験あるし。
Windowsフォームでラベルというのはよく使う。
その変な使い方というわけで、Regionプロパティの話。
import clrIronPythonなら簡単に試せますね。
import System
clr.AddReferenceByPartialName("System.Windows.Forms")
clr.AddReferenceByPartialName("System.Drawing")
myform=System.Windows.Forms.Form()
mylabel=System.Windows.Forms.Label()
mylabel.Size=System.Drawing.Size(100,100)
mylabel.BackColor=System.Drawing.Color.LightPink
mylabel.TextAlign=System.Drawing.ContentAlignment.MiddleCenter
mylabel.Text="丸"
zone=System.Drawing.Drawing2D.GraphicsPath()
zone.AddEllipse(0,0,100,100)
mylabel.Region=System.Drawing.Region(zone)
myform.Controls.Add(mylabel)
myform.ShowDialog()
重要なのは、zoneというGraphicsPathのインスタンスを作って、
そこに円状の領域を設定、これでもってRegionを作成し、設定。
これにより何ができるのかというとコントロールの表示領域が円状になる。
実際実行してみると、ピンク色の円の中に"円"という字が書いてある。
このピンク色の円こそがラベルです。
まぁ今回は文字列が書いてあるからラベルっぽいけど、
文字列ないにも関わらずラベルでこれを描いているときがある。
無駄じゃないかというのはあるかも知れんが、実は便利。
PictureBoxの場合、一時的に書くのは非常に容易なのだが、
もしウインドウの影に隠れたりして、再描画するとき復帰しない。
復帰するためにはGrapicsに空のBitmapをセットしてから書けばよいのだが…
まぁその都合は@ITで紹介されてるな。「PictureBoxコントロールにグラフィックを描画するには?」
有名な事実だとは思いますけどね。
けど、この方法ならそんなこと気にしなくてもOK。
しかもこのRegionで切り抜く方法は重なり合いが発生したときにも有利。
mytbox=System.Windows.Forms.TextBox()驚くことに、ラベルの下にきちんとテキストボックスが表示されてる。
myform.Controls.Add(mytbox)
myform.ShowDialog()
これは実はかなりすごいことなんです。
Regionで指定した範囲しかコントロールの領域じゃないんです。
だからClickイベントを設定して、丸まってる部分をクリックしても反応無し。
ただ透明に設定することとの違いについては、
mylabel.BackColor=System.Drawing.Color.Transparentとしたときの動作を見ればよくわかります。
myform.BackColor=System.Drawing.Color.LightBlue
背景色が透明になれば、テキストボックス全体が透けそうですね。
しかし、透明であるべきラベルの領域は全域真っ青。
この透明というのは、親コントロールの上に子が配置される前の表示そのものが背景という意味。
なので、親コントロールの上に配置した2つ以上のものが重なり合うと悲しい結果になる。
まぁ、この性質を理解して、Aの上にB、Bの上にCを重ねるという方針なら、
Aの子にBを、Bの子にCを登録すればいいのです。わかりやすい。
まぁなかなか理解してない人は混乱する話です。
まぁこんな風にしてラベルでいろいろ作りました。
ラベルは切り絵のように使うことができるということです。
明らかに使い方がおかしいだろと言われたけど、
しかし使えるものは便利に使いたいものです。
Regionプロパティはそれを支援してくれますよ。
Author : hidemaro
Date : 2008/11/01(Sat) 23:23
.NET Framework | comments (0) | trackback (0)
難解だがgzipの圧縮展開である
未知関数の微分積分を含む方程式ということで、
基本的には関数当てゲームというわけで、そりゃ難しい。
式を変形して積分して解くんですけどね。
一階線形微分方程式の解の公式なんてのがあって、まぁ覚えればいいと思うのだが、
とんでもない式だし、うまく積分できないならその方程式は解けんぞと。
しかし、自然界のいろいろを表す式って微分方程式も多いそうだ。
例えばだけど、RLC直列回路に直流電圧をかけたら、正弦波交流電流が流れることとか。
L(di/dt)+Ri+(1/C)∫idt=E、という方程式を解けばわかるんだよね。
まぁいろいろあるんですよということです。
最近3つのソフトウエアを同時に設計・開発してる。
1つはほとんど完成してるのだが、残り2つが大変。
プロトタイプを作って、機能を実装して…ということを繰り返してる。
まぁ小規模なプログラムだからできることですよね。
それで、Rich Text Formatを取り扱おうかなとか思って調べてた。
RTFなんだが、これが非常に冗長。圧縮するとすごい容量が減る。
というわけで圧縮したものをBase64に変換しておこうと。
IronPythonで開発してるが、try~finallyだらけで読みにくいので、C#で書こう。
どうもPython 2.5からwith構文というのがあるそうだが、残念ながらIronPythonでは実装されてない。
まずファイルを開き、圧縮したものをBase64に変換してコンソールに出力する件。
using( System.IO.FileStream fst=new System.IO.FileStream(fname,System.IO.FileMode.Open) ){
byte[] buf=new byte[fst.Length];
fst.Read(buf,0,buf.Length);
using(System.IO.MemoryStream mst=new System.IO.MemoryStream()){
using(System.IO.Compression.GZipStream gzst=
new System.IO.Compression.GZipStream(mst,System.IO.Compression.CompressionMode.Compress)){
gzst.Write(buf,0,buf.Length);
}
byte[] gzbuf=mst.ToArray();
Console.WriteLine( System.Convert.ToBase64String(gzbuf) );
}
}using構文でC#ですら目が痛いが、IronPythonでは全部try~finallyで書いてるのでさらに目が痛い。System.IO.Compression.GZipStreamにおいては、
書き込まれたら圧縮処理して、コンストラクタで指定されたストリームに書き出すストリームを提供、
コンストラクタで指定されたストリームから展開処理したものを、読み込むことができるストリームを提供、
のどっちかをできる。
しかし使いにくい仕様なんですよ。
圧縮時、あらかじめ、System.IO.MemoryStreamで圧縮結果を受けるストリームを作っておく、
コンストラクタで、このストリームと、System.IO.Compression.CompressionMode.Compressで圧縮モードだとする。
それで、GZipStreamのに、Writeで書き込む。
そして、GZipStreamを閉じたら、圧縮処理完了。受けるストリームには完全な結果が入ってる。
展開処理だが、これは明らかにめんどくさい。
Base64をバイト列に変換して、MemoryStreamのストリームに書き込んでおく。
GZipStreamのこのストリームを展開するストリームを作る。
このストリームを直接使えればいいのだが、Seek不可なので困ることがある。
System.Windows.Forms.RichTextBoxのLoadFileメソッドでRTFのストリームを渡したかったのだが、
Seekできんみたいな例外を投げるので、無理なんだなと。
そうなるとMemoryStreamのストリームにGZipStreamのストリームからReadしたのをWriteすると…
こんな作業をしないといけない。これがかなりめんどくさい。
以上の処理をするもの。
byte[] gzbuf=System.Convert.FromBase64String(gzb64)これはひどい。上で説明したとおりのことが書いてあります。
using(System.IO.MemoryStream rtfst=new System.IO.MemoryStream()){
using(System.IO.MemoryStream gzmst=new System.IO.MemoryStream()){
gzmst.Write(gzbuf,0,gzbuf.Length);
gzmst.Position=0;
using(System.IO.Compression.GZipStream gzst=
new System.IO.Compression.GZipStream(gzmst,System.IO.Compression.CompressionMode.Decompress)){
byte[] buf=new byte[1024];
while(true){
int readlen=gzst.Read(buf,0,buf.Length);
if(readlen==0) break;
rtfst.Write(buf,0,buf.Length);
}
}
}
rtfst.Position=0;
rtfboxLoadFile(rtfst,System.Windows.Forms.RichTextBoxStreamType.RichText);
}
gzmst.Position=0; rtfst.Position=0;
はいずれもMemoryStreamにWriteした後、改めてスタート地点に戻って、
ここからReadしていただくという意味です。
ToArrayで配列に変換する分にはあまり気にしないけど、Readで読むときは重要。
あと、GZipStreamを読み取るのに、1024Bytesのバッファを用意して、
ループ回して、読み取り成功分だけMemoryStreamに書いていく。
Readの返り値は読み取り成功のバイト数だが、読めなくなれば0になるのでこれで抜けてる。
さっきのFileStreamでは、Lengthプロパティがあったけど、GZipStreamでは使えない。
だからこういう、まぁ正攻法でやらんといかんと。
これの使い道だが、TCPとかで通信するときデータを圧縮すると。
そのとき、データをGZipStreamのにWriteして、受けるMemoryStreamの中身を送信すると。
受信してMemoryStreamに投げ込んだら、これをGZipStreamで受けてReadしてMemoryStreamにWrite、
最後にMemoryStreamの中身を確認すれば本当に送りたかったデータがあると。
まぁこういう使い道が多そうです。
ちなみに。System.IO.Compression.DeflateStreamという類似したクラスがあります。
DeflateはgzipやZIPの原理です。Deflateを使ってgzipを実現してるのね。
今回のは別にgzipでなくてもDeflateでもよかった気はするが、gzipの方がデバッグに便利だしいいや。
こんな手間をかけてやるかは知らんが、有用なライブラリなので使うといいと思う。
Author : hidemaro
Date : 2008/10/24(Fri) 23:27
.NET Framework | comments (0) | trackback (0)