2010-12-31

Tcl and the Tk Toolkit (2nd Edition)

前から購入しようと思っていたままだった書籍ですが、遅ればせながらやっと購入しました。この第二版は、John Ousterhout 氏と Ken Jones 氏の共著になっています。が、しかし preface(序文)は Ken Jones 氏が書いているので、実質的には彼が執筆した本のような雰囲気です。

普通の Tcl/Tk 8.5 の入門書といった内容で丁寧に解説されており、Tcl、Tk の入門的内容から、Tcl_Obj (C API) や TEA に基づいた拡張パッケージの作成までカバーされています。700 頁を越える量ですが、わかりやすくまとめられている良書だと思います。製本はペーパーバック(並製本)で、背に頁を糊付けしただけのものです。そのため、永く使い込めば、背が折れて頁が剥がれることになり、もう一冊購入なんてことになりそうです。

2010-12-29

「マイクロソフト戦記」を読んで

書は、1980 年代前半から 1990 年代前半、MSX の標準化から、Windows が業界スタンダードになるまでの筆者トム佐藤氏の体験と、どのように Windows が業界スタンダードになり得たかを描いたノンフィクションかつビジネス戦略を解説した書です。

久しぶりに小説以外でワクワクする書物に出会い、一気に読んでしまいました。本書では、ベンサムが唱えた「最大多数の最大幸福」が、デファクトスタンダードを作り出すことを可能にするキーワードになっています。Windows は他より性能面で抜きん出ていたからではなくて、「最大多数の最大幸福」を実現できたからこそデファクトスタンダードになり得たのです。ビジネスプレゼンテーションによくある美辞麗句ではなく、失敗と悪戦苦闘を重ねる紆余曲折の中で、幸運に恵まれ、市場をつかみ、なんとかテクノロジーの波に乗ることができるまでの様子が実にリアルに伝わってきました。

本書が述べている時代は、自分にとっては、NEC PC98 の MS-DOS 上で Lotus 123 や一太郎を使っていた頃から Macintosh へ仕事のツールが移り変わった時代でした。当時は、MSX や Windows にまるで注目していなかったので、知らなかった時代のアツい側面を知ることができて、なんだか得をした気分になりました。そういえば学生の時、研究室の NEC PC98 上で利用可能であった Windows 1.0 を見て、とても使いものになるとは思えなかったのを憶えています。

仕事において Windows が搭載された PC を利用し始めたのは、当時勤めていた会社の本社 R&D がある米国へ出向した 1994 年の時、Windows for Workgroups 3.11 からです。翌年、Windows 95 が大々的に発売され、その後の展開は周知のとおりです。当時は、Fry's などのコンピューターショップで OS/2 Warp や Windows NT もたくさん店頭に並んでおり、OS の選択肢の多さにクラクラしたものです。デファクトスタンダードが確立されることによって、そういた選択肢が次々と市場から駆逐されてしまうのは、悲しいことですがビジネスの常なのでしょう。

愛用する Linux は、最大幸福の追求はしていないと思うので、デファクトスタンダードとは縁がないのかもしれません。しかし、好きだから、必要だから、というユーザが存在する限り、オープンソースの OS は存在しつづけるような気がします。そう考えると、今までデファクトスタンダードにはなりませんでしたが、今なお健闘している Mac OS X は、不思議な存在です。iOS もそうですが、ソフトウェアとハードウェアをセットにして販売するところがミソなのかもしれません。

本書を読んで、つくづく思ったことは、ビジネスの最前線の仕事とはなんとも過酷だということでした。

[1] マイクロソフト戦記―世界標準の作られ方(新潮新書)

2010-12-04

LLVM に関する話題 2010

LLVM のホットな情報にタイムリーには喰いつけず、ぼんやりしていた自分に悔しかったので、ここで今年一年分の情報をまとめてみたいと思います。
[01] 2010/02/09 あるコンパイラが重要なマイルストーンに到達
[02] 2010/04/19 FreeBSD Daily Topics:2010年4月19日 FreeBSD GCCを置き換えるLLVM Clang,広くテスト呼びかけ
[03] 2010/04/28 LLVM 2.7登場、飛竜を公式ロゴに採用
[04] 2010/04/29 LLVMプロジェクト、対応プラットフォームを強化した「LLVM 2.7」
[05] 2010/05/17 JITコンパイラにLLVMを使用するRuby互換実装「Rubinius 1.0」が登場
[06] 2010/05/18 LLVM、GCC libstdc++をBSDライセンスのlibc++へ置き換え
[07] 2010/05/18 LLVM 2.7、Haskell対応のストーリー
[08] 2010/05/25 【レポート】FreeBSD GCCアップデート停止、LLVM Clangへ移行 - BSDCan 2010
[09] 2010/06/10 FreeBSD Daily Topics:2010年6月10日 LLVM Clang,FreeBSD 9-CURRENTへ統合
[10] 2010/06/11 LLVM Project、次世代デバッガ「LLDB」を開発へ
[11] 2010/10/08 LLVM 2.8登場、C++大幅強化
[12] 2010/10/28 LLVM Clang、Linuxカーネルビルドに成功
[13] 2010/11/19 FreeBSD Daily Topics:2010年11月19日 LLVM Clang 2.8,9-CURRENTにマージ

ところで、Tcl と LLVM を結びつけるようなプロジェクトがないか探して見たところ、llvmtcl という、いわゆる拡張パッケージを開発するプロジェクトがありました。llvmtcl を評価ができたらレポートしたいと思います。

[14] jdc8/llvmtcl - GitHub

2010-11-14

ReactOS 0.3.12 を試す

10 月 20 日付で ReactOS 0.3.12 がリリースされていましたので、VMware 用のイメージをダウンロードして、VMware Player で起動してみました。News #61 - ReactOS ウェブサイトによると、今回のリリースでは当初、トラップ・ハンドラー機構の部分の改良に絞っていたが、結局のこの部分を全て書き直しをすることになり、前回のリリース(2009 年 12 月 16 日)から間が開いてしまったようです。

前回のリリースと比べて、明らかに Application Manager が使いやすくなっていると思います。ただ、アプリケーションの選択肢は増えたものの、全てが現行の ReactOS で動作するわけではなさそうです。



今後、ポピュラーな Windows アプリケーションが動作するようになれば、Windows を使う理由は(少なくとも自宅では)全く無くなりそうです。ReactOS がもっと安定し、主要なアプリケーションを動かせるようになったら、廉価な PC を買ってテスト機として使ってみたいものです。

なお、今回は Flash Player をインストールしたかったのですが、Firefox、Opera 共に Flash Player をインストールできませんでした。LibreOffice もインストール可能だったので試してみましたがインストール後起動せず。なぜだろう??

[1] Frontpage - ReactOS ウェブサイト

2010-11-12

【備忘録】LibreOffice

聞に属する話題ですが、備忘録としてです。

オープンソースのオフィススイート OpenOffice.org の開発コミュニティーは 9月28日、Oracle から独立し「The Document Foundation」を設立すると発表しました[1]。設立の理由は「OpenOffice.org の設立趣意書に書かれている独立性を守るため」ということです。今後はスイート名を「LibreOffice」として開発を進めていくそうです。当サイトからは、LibreOffice がダウンロード出来ます。Linux 用には、本日の時点では英語版だけでしたが、RPM パッケージを tar.gz 形式にまとめて圧縮されたファイルが入手可能です。

Oracle が Sun を買収して以来、オープンソース陣営との関係がうまくいっていないという情報を目にするようになりました。営利を追求する企業とオープンソースとの関係はなかなかうまくいきません。一方、自分が勤めている会社(外資ではありません)の IT 部門をみてみると、まともなソフトウェアは購入するのが当たり前、サポートも外注が当たり前で、オープンソースの利用など、まるで想定外の部署です。オープンソース社会の動向が気になる自分とのギャップに腹立たしく思いますが、IT 部門に所属していないので致し方ありません。

[1] OracleからOpenOffice.orgが独立し、「The Document Foundation」を設立 - ITmedia News
[2] Apache、Javaコミュニティー脱退を示唆――Oracleのテストキットライセンス拒否に反発 - ITmedia News
[3] Oracle、Googleを提訴――AndroidのJava特許侵害で (1/2) - ITmedia News
[4] 【Infostand海外ITトピックス】 OpenSolarisプロジェクトが終了? Oracleのオープンソース戦略への危機感 -クラウド Watch
[5] オラクルとオープンソース

2010-11-03

Fedora 14

Fedora 14 がリリースされました。Fedora Project のサイトがリニューアルされていました。日本語のサイトがとりあえずありますが、日本語に翻訳する協力者が少ないのでしょうか、相変わらずほとんど英語の説明です。自分も翻訳に協力したいのですが、時間が割けません。

さて、今回のリリースの目玉はというと...、ニュースの記事のように一言ではまとめられないので、Releases/14/FeatureList を、下記に転載しました。

Name Summary
Boost 1.44 Update Boost to the upstream 1.44 release
D Programming Add a D compiler (LDC) and D standard runtime library (Tango)
EC2 We will provide a concurrent release of Fedora 14 and above on the Amazon EC2 cloud
Eclipse Helios Update Fedora's Eclipse stack to Helios releases
Erlang R14 Update Erlang to the upstream R14 release
Gdb Index This project will remove the useless indices from the .debug files, and will replace them with an index that greatly speeds up gdb
GNUstep GNUstep development environment for Fedora
ipmiutil An easy-to-use fully-featured IPMI server management utility
libjpeg-turbo libjpeg-turbo is fork of the original libjpeg project. It contains numerous performance related enhancements and is at least twice faster in JPEG compression/decompression than original libjpeg on platforms with MMX/SSE instruction set.
KDE 4.5 Rebase to KDE Plasma Desktop 4.5
Memory Debugging Tools The gdb debugger has been extended with new commands that make it easier to track down and fix excessive memory usage within programs and libraries
NetBeans 6.9 Re-base to the NetBeans 6.9
Open SCAP Provide open-source Security Content Automation Protocol (SCAP) framework, basic set of applications and OVAL/XCCDF security content
Perl 5.12 Update to Perl 5.12
Python 2.7 Update Python to the upstream 2.7 release
Rakudo Star The Rakudo Star releases includes the Rakudo compiler, modules, Blizkost and documentation. Rakudo is an implementation of the Perl 6 specification for the Parrot virtual machine.
Spice Spice aims to provide a complete open source solution for interaction with virtualized desktops
Sugar 0.90 Provide the latest Sugar Learning Environment (0.90), including an enhanced activity set to provide an stable demo environment for Sugar as well as an environment for developers
Ruby 1.8.7 Update Ruby to the upstream 1.8.7 release
 

2010-10-31

統計解析ツール ~ 歩き始め ~

元配置の分散分析ができたからといって、全て見通しが立ったわけではありません。しかし、自分が納得できる統計解析ツール、特に実験計画法の為の解析ツールを作りたいという欲求を抑えることができず、少しずつ GUI の部分を作り始めています。

GUI では、かゆいところに手が届くような操作性と柔軟性を実現したいので、じっくり時間をかけて作り込む予定です。若いころに BBN RS/1 というツールを VAX Station 上で使っていましたが、実験計画用のモジュール (Discover とか Explorer という名前でした)の使い心地が忘れられません。今なら GUI でもっともっと使い易く出来るはずです。



実は、むかしむかしも同じようなツールを自分で作りたいと考えていました。その時は応答曲面法 (RSM) を全面に出していましたが、行列計算の量が多く、手許の PC で実用的な処理速度を得られず、挫折してしまいました。

そもそも Tcl/Tk を使い続けている理由も、こういうツールを自分で作りたいという欲求があるからです。かつての人気はすっかり無くなってしまいましたが、Tcl/Tk の開発は止まっていません。Tktable も BLT (RBC) も利用できます。Eclipse などの統合開発環境が利用出来るので、ソフトウェアの開発が格段に楽になってきてもいます。

今回は、オーソドックスに一元配置の分散分析からはじめて、直交表までをカバーして、その次に中心複合計画やコンピュータの計算応力を駆使する最適計画など応答曲面法までを目指したいと考えています。

さて、完成はいつになるやら…。まずはアルファ版を公開することを目指します。とりあえずは決意表明をしておこう。

参考/引用文献

[1] 岩崎 学 著, 統計的データ解析入門 実験計画法 (東京図書)ISBN4-489-00725-6
 

2010-10-16

RBC - Refactored BLT Component

Fedora では Tcl/Tk 8.5 で BLT パッケージ(2.4z にパッチをあてたもの)が利用できますが、nightly-cvs の Tcl/Tk8.6 を自分でコンパイルして使用している環境では、同サイトで入手できる blt (3.0) をどうしてもうまくビルドできません。なんとかならないかとインターネット上で情報をあさっていたところ、古い記事ですが BLT 2.4z を リファクタリングした RBC についての議論を見つけました[1]。どうやら Mayo SPPDG - Mayo Clinic and FoundationRobert W Techentin 氏が、院生のチームと一緒になって取り組んだ成果のようです。

SourceForge.net にサイト[2] が開設されているので、早速ダウンロードしてビルドしてみました。試行錯誤の後、結局、以下のようにしてビルドしました。ビルドした環境は Fedora 14 (beta) x86_64 です。
$ tar zxvf rbc0.1.src.tar.gz
$ cd rbc
$ CFLAGS="-DUSE_INTERP_RESULT" \
> ./configure --prefix=$HOME --with-tcl=../tcl/unix --with-tk=../tk/unix
$ make
$ make install
コンパイルフラグ USE_INTERP_RESULT は、Tcl 8.6 ではレガシーな (Tcl_Interp) interp->result にアクセスする際に必要になります[3]
テスト用に BLT 用の以前用意したサンプルを RBC 用に書き換えて実行してみました。
#!/bin/sh
# the next line restarts using wish \
exec wish8.6 "$0" "$@"
 
package require rbc

proc init_x {} {
    set xList {}
    for {set i 0} {$i <= 200} {incr i} {
        lappend xList [expr $i / 5.]
    }
    return $xList
}

proc init_y {xList} {
    set yList {}
    foreach x $xList {
        lappend yList [expr sqrt($x) * sin($x)]
    }
    return $yList
}
 
# ----------------------------------------------------------------------------
# メイン
# ----------------------------------------------------------------------------
wm title . "rbc::graph"

set grp ".grp"
rbc::graph $grp \
        -title           "Sample Graph" \
        -font            "helvetica 16" \
        -rightmargin     15 \
        -plotbackground  "light cyan" \
        -plotpadx        0 \
        -plotpady        0 \
        -plotborderwidth 0
pack .grp

# ズームスタックを有効にする
#Blt_ZoomStack $grp

# X と Y 軸
$grp axis configure x \
        -subdivisions 10 \
        -tickfont     "courier 8" \
        -title        "X Axis" \
        -titlefont    "helvetica 12"
$grp axis configure y \
        -subdivisions 10 \
        -tickfont     "courier 8" \
        -title        "Y Axis" \
        -titlefont    "helvetica 12"

# グリッド
$grp grid configure \
        -color "turquoise" \
        -mapx  x \
        -mapy  y
$grp grid on

# 凡例
$grp legend configure \
        -font     "helvetica 10" \
        -ipadx    10 \
        -position bottom \
        -relief   ridge

# データ
set xList [init_x]
set yList [init_y $xList]
$grp element create func \
        -label        "sqrt(x) * sin(x)" \
        -xdata        $xList \
        -ydata        $yList \
        -smooth       quadratic \
        -linewidth    1 \
        -color        blue \
        -symbol       circle \
        -pixels       0.0625i \
        -scalesymbols yes \
        -outline      red \
        -fill         green

# ---
# sample_graph.tcl
実行例を以下に示しました。日本語フォントが表示できないし、 BLT のすべての機能が使えるわけでもなく制限はありますが、Tcl/Tk 8.6 の環境で手軽に(そこそこの)グラフを作成できそうです。

参考サイト

[1] RBC - Refactored BLT Components - Initial Release - Rhinocerus
[2] The RBC Toolkit | Download The RBC Toolkit software for free at SourceForge.net
[3] DIRECT ACCESS TO INTERP->RESULT - Tcl_SetResult manual page
 

2010-10-10

ちょっと分散分析(2)

p 値の計算をするためにガンマ関数やらベータ関数を計算するルーチンを C で作成し、Tcl の拡張パッケージ上の拡張された算術関数として利用できるようにして、その上で P 値を計算するルーチンを作ってはみましたが、計算した値が合いません。きっと自分の理解不足によるもののと考え、とりあえず諦めてしまいました(泣)。

そのかわり近似計算をしているサイトをいろいろ探し、サイト[1] を参考にして P 値を与えるルーチンを追加しました。悔しいのでもっと勉強して、自分で納得できるルーチンを作成していきます。

#!/bin/sh
# the next line restarts using tclsh \
exec tclsh8.6 "$0" "$@"
# 分散分析
# $Id: anova_1way.tcl,v 1.6 2010/10/10 05:15:34 bitwalk Exp bitwalk $

# サンプルデータ1
set a [list {3.42  3.84  3.96  3.76}]
set b [list {3.17  3.63  3.47  3.44  3.39}]
set c [list {3.64  3.72  3.91}]

# サンプルデータ2
#set a [list {25 30 20 32}]
#set b [list {30 33 29 40 36}]
#set c [list {32 39 35 41 44}]

# データ
set data [list $a $b $c]

# -----------------------------------------------------------------------------
#  クラス anova
# -----------------------------------------------------------------------------
oo::class create anova {
    constructor {a} {
        my variable x way

        # データリスト
        set x $a
        # 分散分析の対象フラグのリセット
        set way ""
    }
    # -------------------------------------------------------------------------
    # 一元配置
    # -------------------------------------------------------------------------
    method oneway {} {
        my variable x way
        my variable n k
        my variable SSb SSw SSt
        my variable df_b df_w df_t
        my variable MSb MSw
        my variable F p

        # Sum of Squared Deviations
        # SSt (TOTAL)
        set x_simple [my list_simple $x]
        set n [llength $x_simple]
        set x_mean [my mean $x_simple $n]
        set SSt [my var $x_simple $x_mean]
        # SSb (BETWEEN GROUPS)
        set k [llength $x]
        set SSb 0
        for {set i 0} {$i < $k} {incr i} {
            set x_b($i) [my list_simple [lindex $x $i]]
            set n_b($i) [llength $x_b($i)]
            set x_mean_b($i) [my mean $x_b($i) $n_b($i)]
            set SSb [expr $SSb + $n_b($i) \
                * ($x_mean_b($i) - $x_mean) \
                * ($x_mean_b($i) - $x_mean)]           
        }
        # SSw (WITHIN GROUPS)
        # set SSw [expr $SSt - $SSb]
        set SSw 0
        for {set i 0} {$i < $k} {incr i} {
            set x_w($i) [my list_simple [lindex $x $i]]
            set n_w($i) [llength $x_w($i)]
            set x_mean_w($i) [my mean $x_w($i) $n_w($i)]
            set SSw [expr $SSw + [my var $x_w($i) $x_mean_w($i)]]
        }
        # Degree of Freedom
        set df_b [expr $k - 1]
        set df_w [expr $n - $k]
        set df_t [expr $n - 1]
        # Mean Square
        set MSb [expr $SSb / $df_b]
        set MSw [expr $SSw / $df_w]
        # F value, the ratio of the two variance estimates
        set F [expr $MSb / $MSw]
        set p [my f_pval $F $df_b $df_w]
        # flag
        set way "One Way"
        return 1
     }
    # -------------------------------------------------------------------------
    # result
    # 結果の表示
    # -------------------------------------------------------------------------
    method result {} {
        my variable way

        switch $way {
            "One Way" {
                my result_1way
            }
            default {
                puts "No result yet!"
            }
        }
    }
    # -------------------------------------------------------------------------
    # result_1way
    # 結果の表示(一元配置)
    # -------------------------------------------------------------------------
    method result_1way {} {
        my variable x
        my variable n k
        my variable SSb SSw SSt
        my variable df_b df_w df_t
        my variable MSb MSw
        my variable F p

        puts "ANOVA (1way)"
        puts "Source        SS       df       MS    F val    P Val"
        puts -nonewline "Between"
        puts -nonewline [format " %8.3f" $SSb]
        puts -nonewline [format " %8d" $df_b]
        puts -nonewline [format " %8.3f" $MSb]
        puts -nonewline [format " %8.3f" $F]
        puts -nonewline [format " %8.6f" $p]
        if {$p < 0.001} {
            puts " ***"
        } elseif {$p < 0.01} {
            puts " **"
        } elseif {$p < 0.05} {
            puts " *"
        } else {
            puts ""
        }
        puts -nonewline "Within "
        puts -nonewline [format " %8.3f" $SSw]
        puts -nonewline [format " %8d" $df_w]
        puts -nonewline [format " %8.3f" $MSw]
        puts ""
        puts -nonewline "Total  "
        puts -nonewline [format " %8.3f" $SSt]
        puts -nonewline [format " %8d" $df_t]
        puts ""
    }
    # -------------------------------------------------------------------------
    # mean
    # 平均値の計算
    # -------------------------------------------------------------------------
    method mean {xList num} {
        set sum [my sum $xList]
        return [expr $sum / $num]
    }
    # -------------------------------------------------------------------------
    # sum
    # 合計の計算
    # -------------------------------------------------------------------------
    method sum {xList} {
        set total 0.0
        foreach x $xList {
            set total [expr $total + $x]
       }
        return $total
    }
    # -------------------------------------------------------------------------
    # var
    # 分散の計算
    # -------------------------------------------------------------------------
    method var {xList xMean} {
        set var 0.0
        foreach x $xList {
            set var [expr $var + ($x - $xMean) * ($x - $xMean)]
        }
        return $var
    }
    # -------------------------------------------------------------------------
    # f_pval
    # P値(F分布の上側確率の算出)
    # -------------------------------------------------------------------------
    method f_pval {f df1 df2} {
        set m2pi 0.636619772367581343076
        set m_pi 3.14159265358979323846
        set i1 [expr 2 - ($df1%2)]
        set i2 [expr 2 - ($df2%2)]
        set w [expr $df2/($df2 + $df1*$f)]
        set y [expr 1.0 - $w]
        set p [expr sqrt($w)]
        set s [expr sqrt($y)]

        if {[expr 2*$i1 - $i2] == 0} {
            set p [expr 1.0 - $s]
            set c [$s*$w/2.0]
        } elseif {[expr 2*$i1 - $i2] == 1} {
            set c [expr $s*$p/$m_pi]
            set p [expr 1.0 - atan2($s, $p) * $m2pi]
        } elseif {[expr 2*$i1 - $i2] == 2} {
            set p $w
            set c [expr $w*$y]
        } else {
            set c [expr $y*$p/2.0]
        }
        for {set i2 $i2} {$df2 > $i2} {incr i2 2} {
            set p [expr $p - 2.0/$i2*$c]
            set c [expr $c*$w*($i1 + $i2)/$i2]
        }
        for {set i1 $i1} {$df1 > $i1} {incr i1 2} {
            set p [expr $p + 2.0/$i1*$c]
            set c [expr $c*$y*($i1 + $i2)/$i1]
        }
        return [expr ($p < 0.0 && abs($p) < 1e-10) ? 0.0 : $p]
    }
    # -------------------------------------------------------------------------
    # list_simple
    # 入れ子になっているリストをシンプルなリストに変換
    # -------------------------------------------------------------------------
    method list_simple {xList} {
        regsub -all {[\{\}]} $xList {} foo
        regsub -all {\s{2,}} $foo { } baa
        return $baa
    }
}
# -----------------------------------------------------------------------------
#  メイン
# -----------------------------------------------------------------------------
set obj [anova new $data]
if {[$obj oneway]} {
    $obj result
} else {
    puts "failed"
}
# ---
# END PROGRAM
実行例を以下に示しました。
$ ./anova_1way.tcl
ANOVA (1way)
Source        SS       df       MS    F val    P Val
Between    0.318        2    0.159    4.615 0.041749 *
Within     0.310        9    0.034
Total      0.628       11

参考サイト

[1] 統計関数の確率計算
[2] The Free Statistics Calculators Website - Home (算出値の検証用)
 

追記

この記事を書いた後、なぜ用意した関数でうまくできなかったのかが突然判ってしまいました。長年 Tcl を使っているのに、Tcl 特有の計算の落とし穴にすっかりハマっていたのです。

以下は、用意していた規格化された不完全ベータ関数 betai を使って書き直したメソッド f_pval です。Tcl では、整数÷整数の結果は自動的に整数になってしまいます。浮動小数の結果が必要な場合は、分母分子のどちらか一方を浮動小数の値に明示にしておく必要があります。
    # -------------------------------------------------------------------------
    # f_pval
    # P値(F分布の上側確率の算出)
    # -------------------------------------------------------------------------
    method f_pval {f df1 df2} {
        set x [expr $df1 * $f / ($df2 + $df1*$f)]
        return [expr 1 - betai($df1/2.0, $df2/2.0, $x)]
    }
実行例を以下に示しましたが、上記と同じ結果が得られています。
$ ./anova_1way.tcl
ANOVA (1way)
Source        SS       df       MS    F val    P Val
Between    0.318        2    0.159    4.615 0.041749 *
Within     0.310        9    0.034
Total      0.628       11

今のところ規格化された不完全ベータ関数 betai の算出には、下記の GSL の関数を利用しています。
double gsl_sf_beta_inc (double a, double b, double x)
以下のようなルーチンを使って Tcl の拡張パッケージを作成しています。
/*****************************************************************************
 * int func_betai
 * 規格化された不完全ベータ関数の処理部
 *
 * Tcl 側のコマンド
 *   expr betai($a, $b, $x)
 *****************************************************************************/
int
func_betai (ClientData clientData, Tcl_Interp * interp,
        Tcl_Value * args, Tcl_Value * resultPtr)
{
  double a, b, x;

  a = args->doubleValue;
  args++;
  b = args->doubleValue;
  args++;
  x = args->doubleValue;

  /* 計算結果 */
  resultPtr->type = TCL_DOUBLE;
  resultPtr->doubleValue = gsl_sf_beta_inc (a, b, x);

  return TCL_OK;
}
ただ、GSL の配布ライセンスは GPLv3 (GNU General Public License) に従っているので、扱い難いと感じています。計算方法の検証ができたので、他に BSD などの配布ライセンスで良いライブラリがあれば乗り換えようと思っています。オープンソースを推進したい者としては矛盾した考えですが、しかたがありません。
 

2010-10-02

ちょっと分散分析

々から作りたいと思っていて、なかなか重い腰を上げて取り組もうとしなかった分散分析のライブラリについて、ようやく少しずつ作り始めました。

本当は C# で作りたかったのですが、GUI 化の手間と、ゆくゆくは超越関数(Γ関数など)を扱う統計値の計算ルーチンを作ることを考えれば、慣れている Tcl/Tk (+ Tktable) と C の組み合わせで作る方が現実的と考えました。

また、TclOO を利用したかったので、Tcl の nightly-cvs のサイトから Tcl/Tk の最新のソース (8.6b1.2) をダウンロード、Fedora 上でビルドして利用しています。

まずは一元配置の簡単なルーチンを作ってみました。参考サイト [1] と [2] で検算をしています。
#!/bin/sh
# the next line restarts using tclsh \
exec tclsh8.6 "$0" "$@"
# 分散分析
# $Id: anova_1way.tcl,v 1.4 2010/10/02 15:00:00 bitwalk Exp bitwalk $

# サンプルデータ1
set a [list {3.42  3.84  3.96  3.76}]
set b [list {3.17  3.63  3.47  3.44  3.39}]
set c [list {3.64  3.72  3.91}]

# サンプルデータ2
#set a [list {25 30 20 32}]
#set b [list {30 33 29 40 36}]
#set c [list {32 39 35 41 44}]

# データ
set data [list $a $b $c]

# -----------------------------------------------------------------------------
#  クラス anova
# -----------------------------------------------------------------------------
oo::class create anova {
    constructor {a} {
        my variable x way

        # データリスト
        set x $a
        # 分散分析の対象フラグのリセット
        set way ""
    }
    # -------------------------------------------------------------------------
    # 一元配置
    # -------------------------------------------------------------------------
    method oneway {} {
        my variable x way
        my variable n k
        my variable SSb SSw SSt
        my variable df_b df_w df_t
        my variable MSb MSw
        my variable F

        # Sum of Squared Deviations
        # SSt (TOTAL)
        set x_simple [my list_simple $x]
        set n [llength $x_simple]
        set x_mean [my mean $x_simple $n]
        set SSt [my var $x_simple $x_mean]
        # SSb (BETWEEN GROUPS)
        set k [llength $x]
        set SSb 0
        for {set i 0} {$i < $k} {incr i} {
            set x_b($i) [my list_simple [lindex $x $i]]
            set n_b($i) [llength $x_b($i)]
            set x_mean_b($i) [my mean $x_b($i) $n_b($i)]
            set SSb [expr $SSb + $n_b($i) \
                * ($x_mean_b($i) - $x_mean) \
                * ($x_mean_b($i) - $x_mean)]           
        }
        # SSw (WITHIN GROUPS)
        # set SSw [expr $SSt - $SSb]
        set SSw 0
        for {set i 0} {$i < $k} {incr i} {
            set x_w($i) [my list_simple [lindex $x $i]]
            set n_w($i) [llength $x_w($i)]
            set x_mean_w($i) [my mean $x_w($i) $n_w($i)]
            set SSw [expr $SSw + [my var $x_w($i) $x_mean_w($i)]]
        }
        # Degree of Freedom
        set df_b [expr $k - 1]
        set df_w [expr $n - $k]
        set df_t [expr $n - 1]
        # Mean Square
        set MSb [expr $SSb / $df_b]
        set MSw [expr $SSw / $df_w]
        # F value, the ratio of the two variance estimates
        set F [expr $MSb / $MSw]
        # flag
        set way "One Way"
        return 1
     }
    # -------------------------------------------------------------------------
    # result
    # 結果の表示
    # -------------------------------------------------------------------------
    method result {} {
        my variable way

        switch $way {
            "One Way" {
                my result_1way
            }
            default {
                puts "No result yet!"
            }
        }
    }
    # -------------------------------------------------------------------------
    # result_1way
    # 結果の表示(一元配置)
    # -------------------------------------------------------------------------
    method result_1way {} {
        my variable x
        my variable n k
        my variable SSb SSw SSt
        my variable df_b df_w df_t
        my variable MSb MSw
        my variable F

        puts "ANOVA (1way)"
        puts "Source        SS       df       MS    F val"
        puts -nonewline "Between"
        puts -nonewline [format " %8.3f" $SSb]
        puts -nonewline [format " %8d" $df_b]
        puts -nonewline [format " %8.3f" $MSb]
        puts -nonewline [format " %8.3f" $F]
        puts ""
        puts -nonewline "Within "
        puts -nonewline [format " %8.3f" $SSw]
        puts -nonewline [format " %8d" $df_w]
        puts -nonewline [format " %8.3f" $MSw]
        puts ""
        puts -nonewline "Total  "
        puts -nonewline [format " %8.3f" $SSt]
        puts -nonewline [format " %8d" $df_t]
        puts ""
    }
    # -------------------------------------------------------------------------
    # mean
    # 平均値の計算
    # -------------------------------------------------------------------------
    method mean {xList num} {
        set sum [my sum $xList]
        return [expr $sum / $num]
    }
    # -------------------------------------------------------------------------
    # sum
    # 合計の計算
    # -------------------------------------------------------------------------
    method sum {xList} {
        set total 0.0
        foreach x $xList {
            set total [expr $total + $x]
       }
        return $total
    }
    # -------------------------------------------------------------------------
    # var
    # 分散の計算
    # -------------------------------------------------------------------------
    method var {xList xMean} {
        set var 0.0
        foreach x $xList {
            set var [expr $var + ($x - $xMean) * ($x - $xMean)]
        }
        return $var
    }
    # -------------------------------------------------------------------------
    # list_simple
    # 入れ子になっているリストをシンプルなリストに変換
    # -------------------------------------------------------------------------
    method list_simple {xList} {
        regsub -all {[\{\}]} $xList {} foo
        regsub -all {\s{2,}} $foo { } baa
        return $baa
    }
}
# -----------------------------------------------------------------------------
#  メイン
# -----------------------------------------------------------------------------
set obj [anova new $data]
if {[$obj oneway]} {
    $obj result
} else {
    puts "failed"
}
# ---
# END PROGRAM
実行例を以下に示しました。
$ ./anova_1way.tcl
ANOVA (1way)
Source        SS       df       MS    F val
Between    0.318        2    0.159    4.615
Within     0.310        9    0.034
Total      0.628       11
まだ、p値の計算はできていません。この部分は C で Tcl のライブラリを作成する予定です。あとは、すこしずつ GUI を作りながら、二元配置、直交表の分散分析にも対応させていく予定です。

参考サイト

[1] 一元配置分散分析
[2] One Way ANOVA
 

2010-09-10

Tcl/Tk 8.5.9 リリース


Tcl/Tk 8.5.9 が 9 月 8 日付けでリリースされました。Tcl/Tk 8.5 の 9 番目のパッチリリースです。バグフィックスが中心で、付属パッケージのバージョンアップもされています。
最近のリリース周期はすっかり穏やかなペースになり、今回は、8.5.8 から約 10 ヶ月ぶりのリリースになりました。
Date: Wed, 08 Sep 2010 16:42:34 -0400
From: Donald G Porter <donald.porter at nist.gov>
To: Tcl List Core <tcl-core at lists.sourceforge.net>
Subject: [TCLCORE] Tcl/Tk 8.5.9 RELEASED
Tcl/Tk 8.5.9 Release Announcement
September 8, 2010

The Tcl Core Team is pleased to announce the 8.5.9 releases of the Tcl
dynamic language and the Tk toolkit.  This is the ninth patch release
of Tcl/Tk 8.5.  More details can be found below.  We would like to
express our gratitude to all those who submit bug reports and patches.
This information is invaluable in enabling us to identify and eliminate
problems in the core.

Where to get the new releases:
------------------------------

Tcl/Tk 8.5.9 sources are freely available as open source from the
Tcl Developer Xchange web site at:

         http://www.tcl.tk/software/tcltk/8.5.html

This web page also contains additional information about the releases,
including new features and notes about installing and compiling the
releases.  Sources are always available from the Tcl SourceForge
project's file distribution area:

         http://sourceforge.net/project/showfiles.php?group_id=10894

Binaries for most major platforms are available from:

         http://www.activestate.com/Tcl

For additional information:
---------------------------

Please visit the Tcl Developer Xchange web site:

         http://www.tcl.tk/

This site contains a variety of information about Tcl/Tk in general, the
core Tcl and Tk distributions, Tcl development tools, and much more.

Summary of Changes since Tcl/Tk 8.5.8:
--------------------------------------

The following were the main changes in Tcl/Tk 8.5.9.  A complete list
can be found in the changes file at the root of the source tree.  The
more complete ChangeLog is also included with each source release.  This
is a patch release, so it primarily includes bug fixes and corrections
to erratic behavior.  Below are only the most notable changes.

    * TIP 359: new X11 option: [wm attributes -type]
 - stops inappropriate Compiz animation of Tk menu & combobox.

    * TIP 360: modernize menus on X11.

    * New widget [ttk::spinbox].

    * [lappend arr(elem)] no longer fires read traces as it inconsistently
      has in some situations, but not in others.
         *** POTENTIAL INCOMPATIBILITY ***

    * [tk_getOpenFile] on Windows: unlimited multiple-file selection.

    * [load] uses LOAD_WITH_ALTERED_SEARCH_PATH for fewer surprises when
      bringing in DLLs via dependencies on Windows.
         *** POTENTIAL INCOMPATIBILITY ***

    * Updated [send] security rules to current Ubuntu/Fedora conventions.

    * Fixed [wm iconphoto] on LP64 unix systems.

    * [chan copy] of more than 2**31 bytes is now possible.

    * min() and max() functions now work in safe interps.

    * [$menu delete $tooBig end] now properly a no-op.

    * canvas items now properly updated when canvas state changes.

    * Revised [ttk::sizegrip] to accommodate Compiz.

    * Revised default Text bindings for  and  to account
      for insertion point relationship to the selection.

    * Corrected result of [tcl::mathfunc::abs 0.0].

    * New version 1.432 of msgcat package.

    * New version 1.0.9 of platform package.

    * New version 0.8.6 of tile package.

    * Changes to support building with MSVC++ 2010.

    * Prevent race condition in some XIM implementations.

    * Fixed bad results from [file rootname].

    * Prevent consumption of all memory when [chan copy] copies to slow channel.

    * [wm transient] now works even with withdrawn windows.

    * $DISPLAY can now contain "::".

    * Restored compatibility of [entry] validation with Itcl variable scope.

    * Fixed line-sensitive matching by [regexp].

    * Fixed memory corruption in complex canvas tag searches.

    * Fixed crash in encoding finalization.

    * Fixed crashes in widget destruction.

    * Fixed crash in GetFontFamilyName().

    * Fixed crash in menu deletion.

    * Fixed crash in peer text dump.

    * Fixed crash when bind scripts are empty.

    * Fixed crash in unset traces during [array unset].

    * Fixed crash deleting vars during callframe pop.

    * Fixed crash in [open |noSuchFile rb].

    * Fixed crash in [chan postevent].

    * Fixed programming error in [clock format] in he_IL locale.

    * Safe Base and Safe Tk rewrites

--
Tcl Core Team and Maintainers
Don Porter, Tcl Core Release Manager



-- 
| Don Porter          Mathematical and Computational Sciences Division |
| donald.porter at nist.gov          Information Technology Laboratory |
| http://math.nist.gov/~DPorter/                                  NIST |
|______________________________________________________________________|

------------------------------------------------------------------------------
This SF.net Dev2Dev email is sponsored by:

Show off your parallel programming skills.
Enter the Intel(R) Threading Challenge 2010.
http://p.sf.net/sfu/intel-thread-sfd
_______________________________________________
Tcl-Core mailing list
Tcl-Core at lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/tcl-core

2010-08-29

自治体に広がるOpenOffice.org

2010/08/26 付けの ITPro の記事で、オープンソースのオフィスソフト OpenOffice.org を採用する自治体が増えているという記事がありました。この場合、オープンソースであることは、たぶんどうでも良いことなのでしょう。Microsoft Office と比較すると無料で入手できること、つまり経費を節約出来ることを第一に着目して採用することは、仕方がないことなのかもしれません。こういった動きの中で OpenOffice.org のユーザーが増えて、開発者やユーザーグループの活動が活発になり、結果としてオープンソースであることの恩恵を享受でき、なにがしかの貢献ができるようになれば良いのです。

[1] 自治体に広がるOpenOffice.org

自分が勤めている会社では、OS やアプリケーションは Microsoft の製品がほとんどです。残念ながら、自社の情報システム部門がオープンソース系のシステム導入に前向きになる日が来るとしても、それはまだまだ先です、きっと。
 

2010-08-14

CLR のバージョンの取得

Mono で開発したアプリケーションを Windows でも実行することを想定しているので、ビルド時と実行時の CLR のバージョンを把握できる方法があればなにかと便利です。情報がないか探したところ、下記の参考サイト [1] にぴったりな情報があったので、早速試してみました。

using System;
using System.Reflection;
using System.Runtime.InteropServices;

namespace bitWalk
{
class VerTest
{
public static void Main (string[] args)
{
string clrVersionBuildtime = Assembly.GetExecutingAssembly ().ImageRuntimeVersion;
Console.WriteLine ("built CLR version: " + clrVersionBuildtime);
string clrVersionRuntime = RuntimeEnvironment.GetSystemVersion();
Console.WriteLine ("runtime CLR version: " + clrVersionRuntime);
}
}
}

プログラム名は VerTest.exe です。Linux の端末エミュレータ上で実行してみると、CLR のバージョンはどちらも v2.0.50727 でした。

Wine 上だと、実行バージョンが変わるかもしれないと期待して試してみましたが、両方とも v2.0.50727 でした。

それでは Windows 上であれば、と期待して実行してみましたが、結果はやはり同じ v2.0.50727 です。

なんとなく納得できないので、今度は、Windows XP 上の Visual Studio 2010 Express で同じソースを VerTest_VS.exe としてコンパイルして実行してみました。Windows 上では v4.0.30319 と表示されています。

Linux / Mono 上で実行すると、今度は確かに実行 CLR のバージョンが変わりました (v1.1.4322)。

Mono でコンパイルしたプログラムで直接確認できませんでしたが、機能的には確認できたので、とりあえず納得です。

参考サイト


[1] @IT:.NET TIPS ビルド時および実行時のCLRバージョンを取得するには? - C# VB.NET
[2] @IT:.NET TIPS サイド・バイ・サイドによりCLRバージョンを指定するには?
 

2010-08-13

Math.NET Numerics

C# で手軽に使える、オープンソースの数値計算ライブラリがないか探してみました。演算速度のパフォーマンスを求める場合は、C# 以外の環境を検討する必要がありますが、それにしても「ある程度」の数値計算ぐらいは C# で開発していれば C# で処理したいですし、その「ある程度」がどの程度なのかも評価しなければなりません。

Mono プロジェクトの、ライブラリを紹介しているページの Math の項目に、dnAnalytics というライブラリが紹介されていますが、調べてみると、当の dnAnalytics と、Math.NET Iridium という他の数値計算ライブラリを開発するプロジェクトが一緒になり、現在 Math.NET Numerics というプロジェクトになっていることが判りました。ライセンスは MIT/X11 になっています。(ちなみに、以前の Math.NET Iridium は LGPL でした。)

早速、ダウンロードのサイトから MathNet.Numerics.2010.1.28.762.zip をダウンロードして、MonoDevelop で簡単なサンプルを動かしてみました。最初、Math.NET Numerics Code Samples - Math.NET Numerics Documentation にあるサンプルをそのままビルドしようとしましたが、以前の Math.NET Iridium 用のサンプルのままで、現時点で存在しないメソッドを使っていたので、少し直しました。動作確認だけだったので面白いサンプルではありません。

ドキュメントもある程度揃っているので、簡単なサンプルを作るなどして、使い勝手を評価していこうと考えています。

参考サイト


[1] Math.NET Numerics
[2] Math.NET on Mono (and Linux) - Math.NET Numerics Documentation
注)以前の Math.NET Iridium の時のままですが同じことですので読み替えてください。
 

2010-08-12

Mono で GUI プログラミング (4)

週は、お盆休みで時間に余裕があるので、インターネット上で入手できる .NET の GUI アプリケーションの C# ソースがあれば、ダウンロードして mono でビルドできるかどうか試してばかりいます。

今回は、Yukun's Blog で紹介されていたサンプルをビルドしてみました。サンプルは、以下の URL で紹介されている「キッチンタイマー」です。

C#でキッチンタイマーを作ろう - Yukun's Blog

このサイトで紹介されているコードは、FormTimer.csFormTimer.Designer.cs の二種類です。これに、次のメインメソッドを記述した FormTimer.Main.cs を加えてビルドしました。

using System;
using System.Windows.Forms;

namespace Sample
{
class Program
{
static void Main ()
{
Application.Run (new FormTimer ());
}
}
}

複数のソースファイルから、単一の実行ファイルをビルドするには、-out オプションで出力ファイル名を指定して、ビルドするソースファイルを列挙します。

$ gmcs -pkg:dotnet -target:winexe -out:FormTimer.exe FormTimer.Designer.cs FormT
imer.Main.cs FormTimer.cs

$ mono FormTimer.exe &

Linux での実行例

MonoDevelop の利用


まともなアプリケーションを作ろうとすると、ソースコードは長大なファイルになるか、あるいは、いくつもの分割されたファイルになります。上記の例のように 3 つぐらいのソースコードであれば我慢ができますが、それ以上になると管理しきれなくなります。そんなとき IDE を使うと便利です。Mono 用の IDE に MonoDevelop があります。Fedora だと yum コマンドでインストールできます。以下は、MonoDevelop (2.2.2) で、上記のサンプルを編集している例です。

 

2010-08-08

Mono で GUI プログラミング (3)

Excel のようなスプレッドシートを使いたければ、それこそ Excel とか OpenOffice.org Calc を使えば良いのですが、GUI アプリケーションでちょっとしたスプレッドシートを使いたい時があります。

DataGridView コントロールを利用した例を [1] で見つけたので、それを少し加工したサンプル DataGridViewTest.cs を紹介します。

///////////////////////////////////////////////////////////////////////////////
// DataGridViewTest.cs - DataGridView コントロールを使ったサンプル
//
// 参考/引用:
// Koders Code Search: Test.cs - C#
// modified by Fuhito Suguri, 7-Aug-2010
///////////////////////////////////////////////////////////////////////////////

using System;
using System.Windows.Forms;
using System.Drawing;
//
namespace bitWalk
{
public class Program
{
public static void Main (string[] args) {
DataGridViewTest p = new DataGridViewTest();
p.SampleDataShow();
Application.Run(p);
}
}
//
public class DataGridViewTest : Form {
DataGridView myDataGridView;
/////////////////////////////////////////////////////////////////////////////
// DataGridViewTest constructor

public DataGridViewTest () {
this.Width = 400;
this.Height = 200;
this.Text = "DataGridViewTest";
//
this.Closed += OnClosed;
//
myDataGridView = new DataGridView();
myDataGridView.ColumnCount = 5;
//
DataGridViewCellStyle style1 = myDataGridView.ColumnHeadersDefaultCellStyle;
style1.BackColor = Color.Navy;
style1.ForeColor = Color.White;
style1.Font = new Font(myDataGridView.Font, FontStyle.Bold);
//
DataGridViewCellStyle style2 = new DataGridViewCellStyle();
style2.BackColor = Color.Beige;
style2.ForeColor = Color.Brown;
style2.Font = new Font("Arial", 8);
myDataGridView.AlternatingRowsDefaultCellStyle = style2;
//
myDataGridView.EditMode = DataGridViewEditMode.EditOnEnter;
myDataGridView.Name = "myDataGridView";
myDataGridView.AutoSizeRowsMode =
DataGridViewAutoSizeRowsMode.DisplayedCellsExceptHeaders;
myDataGridView.ColumnHeadersBorderStyle =
DataGridViewHeaderBorderStyle.Raised;
myDataGridView.CellBorderStyle = DataGridViewCellBorderStyle.Single;
myDataGridView.GridColor = SystemColors.ActiveBorder;
myDataGridView.Columns[0].Name = "リリース日";
myDataGridView.Columns[1].Name = "トラック";
myDataGridView.Columns[1].DefaultCellStyle.Alignment =
DataGridViewContentAlignment.MiddleCenter;
myDataGridView.Columns[2].Name = "タイトル";
myDataGridView.Columns[3].Name = "アーティスト";
myDataGridView.Columns[4].Name = "アルバム";
myDataGridView.Columns[4].DefaultCellStyle.Font =
new Font(DataGridView.DefaultFont, FontStyle.Italic);
myDataGridView.SelectionMode = DataGridViewSelectionMode.FullRowSelect;
myDataGridView.MultiSelect = false;
myDataGridView.BackgroundColor = Color.Honeydew;
myDataGridView.Dock = DockStyle.Fill;
myDataGridView.CellFormatting +=
new DataGridViewCellFormattingEventHandler(MyDataGridViewCellFormatting);
myDataGridView.CellParsing +=
new DataGridViewCellParsingEventHandler(MyDataGridViewCellParsing);
myDataGridView.CellValidating +=
new DataGridViewCellValidatingEventHandler(MyDataGridViewCellValidating);
this.Controls.Add(myDataGridView);
//
DataGridViewColumnCollection columns = this.myDataGridView.Columns;
columns[0].DisplayIndex = 3;
columns[1].DisplayIndex = 4;
columns[2].DisplayIndex = 0;
columns[3].DisplayIndex = 1;
columns[4].DisplayIndex = 2;
}
// Populating the DataGrid
public void SampleDataShow () {
DataGridViewRowCollection rows = this.myDataGridView.Rows;
rows.Add(new string[] {"11/22/1968", "29", "Revolution 9", "Beatles", "The Beatles [White Album]"});
rows.Add(new string[] {"4/4/1960", "6", "Fools Rush In", "Frank Sinatra", "Nice 'N' Easy"});
rows.Add(new string[] {"11/11/1971", "1", "One of These Days", "Pink Floyd", "Meddle"});
rows.Add(new string[] {"4/4/1988", "7", "Where Is MY Mind?", "Pixies", "Surfer Rosa"});
rows.Add(new string[] {"5/1981", "9", "Can't Find My Mind", "Cramps", "Psychedelic Jungle"});
rows.Add(new string[] {"6/10/2003", "13", "Scatterbrain. (As Dead As Leaves.)", "Radiohead", "Hail to the Thief"});
rows.Add(new string[] {"6/30/1992", "3", "Dress", "P J Harvey", "Dry"});
}
//
void MyDataGridViewCellValidating (object sender, EventArgs args) {
}
//
void MyDataGridViewCellParsing (object sender, EventArgs args) {
}
//
void MyDataGridViewCellFormatting (object sender, EventArgs args) {
}
//
void OnClosed (object o, EventArgs e) {
Console.WriteLine("Closed!");
Application.Exit();
}
}
}
// END PROGRAM

Fedora 上でのビルド、および実行は次のようにします。

$ gmcs -pkg:dotnet -target:winexe DataGridViewTest.cs
$ mono DataGridViewTest.exe &


Windows XP へ DataGridViewTest.exe をコピーして、(ダブルクリックして)実行した結果を以下に示しました。

アプリケーションで使用するスプレッドシートでも、Excel の機能すべてが必要ではありませんが、それでも、ほとんどの場合で必要になる共通な基本機能があります。これらを用意してある程度汎用性があるクラスを用意しておけば、利用価値が高くなります。

参考サイト


[1] Koders Code Search: Test.cs - C#
 

2010-07-31

Mono で GUI プログラミング (2)

C# の練習に、電卓プログラムを作ってみました。Fedora では、mono 用の IDE として MonoDevelop が利用できますが、GUI については Gkt# が利用できるようになっており、.NET Framework の Form クラスを手軽に利用出来るようになっていなかったので、とりあえずテキストエディタでガリガリ記述してみました。

やや長いですが、ソース (dentaku.cs) を以下に示します。

///////////////////////////////////////////////////////////////////////////////
// dentaku.cs - 電卓プログラム
//
// created by Fuhito Suguri, 31-Jul-2010
///////////////////////////////////////////////////////////////////////////////

using System;
using System.Drawing;
using System.Windows.Forms;
//
namespace bitWalk
{
class Program
{
static void Main()
{
Application.Run(new Dentaku());
}
}
/////////////////////////////////////////////////////////////////////////////
// class Dentaku
/////////////////////////////////////////////////////////////////////////////

class Dentaku : Form
{
// DISPLAY
private Label lab_0;
// KEY
private Button but_dot;
private Button but_equal;
private Button but_plus;
private Button but_minus;
private Button but_mul;
private Button but_div;
private Button but_mrc;
private Button but_mminus;
private Button but_mplus;
private Button but_clear;
private Button but_sign;
private Button but_percent;
private Button but_root;
private Button but_00;
private Button but_0;
private Button but_1;
private Button but_2;
private Button but_3;
private Button but_4;
private Button but_5;
private Button but_6;
private Button but_7;
private Button but_8;
private Button but_9;
// background color of KEY
private Color bgcolor_Disp = Color.PaleGreen;
private Color bgcolor_Num = Color.LightCyan;
private Color bgcolor_Dot = Color.PowderBlue;
private Color bgcolor_Op = Color.Lavender;
private Color bgcolor_Eq = Color.LavenderBlush;
private Color bgcolor_Func = Color.LemonChiffon;
private Color bgcolor_Memo = Color.LightPink;
private Color bgcolor_Cls = Color.HotPink;
// KEY size
private Size key_Size = new Size(50, 30);
private Size key_Size2 = new Size(50, 60);
// layout
private int[] row = {10, 60, 90, 120, 150, 180};
private int[] col = {10, 60, 110, 160, 210};
// for dentaku calculation
private bool entry_started = false;
private bool entry_dot = false;
private decimal entry_result = 0;
private decimal entry_memory = 0;
private string entry_operation = null;
/////////////////////////////////////////////////////////////////////////////
// Dentaku constructor

public Dentaku()
{
this.Width = 280;
this.Height = 250;
this.Text = "電卓";
this.FormBorderStyle
= System.Windows.Forms.FormBorderStyle.FixedDialog;
// DISPLAY
this.lab_0 = new Label();
this.lab_0.Font = new Font("MS UI Gothic", 14);
this.lab_0.BackColor = bgcolor_Disp;
this.lab_0.BorderStyle = BorderStyle.FixedSingle;
this.lab_0.Location = new Point(col[0], row[0]);
this.lab_0.Name = "lab0";
this.lab_0.Size = new Size(250, 40);
this.lab_0.Padding = new Padding(5);
this.lab_0.Text = "0";
this.lab_0.TextAlign = ContentAlignment.MiddleRight;
this.Controls.Add(this.lab_0);
// KEY √
this.but_root = new Button();
this.but_root.BackColor = bgcolor_Func;
this.but_root.Location = new Point(col[0], row[1]);
this.but_root.Size = key_Size;
this.but_root.Text = "√";
this.but_root.Click += new EventHandler(but_func_Click);
this.Controls.Add(this.but_root);
// KEY MRC
this.but_mrc = new Button();
this.but_mrc.BackColor = bgcolor_Memo;
this.but_mrc.Location = new Point(col[1], row[1]);
this.but_mrc.Size = key_Size;
this.but_mrc.Text = "MRC";
this.but_mrc.Click += new EventHandler(but_memo_Click);
this.Controls.Add(this.but_mrc);
// KEY M-
this.but_mminus = new Button();
this.but_mminus.BackColor = bgcolor_Memo;
this.but_mminus.Location = new Point(col[2], row[1]);
this.but_mminus.Size = key_Size;
this.but_mminus.Text = "M-";
this.but_mminus.Click += new EventHandler(but_memo_Click);
this.Controls.Add(but_mminus);
// KEY M+
this.but_mplus = new Button();
this.but_mplus.BackColor = bgcolor_Memo;
this.but_mplus.Location = new Point(col[3], row[1]);
this.but_mplus.Size = key_Size;
this.but_mplus.Text = "M+";
this.but_mplus.Click += new EventHandler(but_memo_Click);
this.Controls.Add(this.but_mplus);
// KEY ÷
this.but_div = new Button();
this.but_div.BackColor = bgcolor_Op;
this.but_div.Location = new Point(col[4], row[1]);
this.but_div.Size = key_Size;
this.but_div.Text = "÷";
this.but_div.Click += new EventHandler(but_ope_Click);
this.Controls.Add(this.but_div);
// KEY %
this.but_percent = new Button();
this.but_percent.BackColor = bgcolor_Func;
this.but_percent.Location = new Point(col[0], row[2]);
this.but_percent.Size = key_Size;
this.but_percent.Text = "%";
this.but_percent.Click += new EventHandler(this.but_func_Click);
this.Controls.Add(this.but_percent);
// KEY 7
this.but_7 = new Button();
this.but_7.BackColor = bgcolor_Num;
this.but_7.Location = new Point(col[1], row[2]);
this.but_7.Size = key_Size;
this.but_7.Text = "7";
this.but_7.Click += new EventHandler(but_num_Click);
this.Controls.Add(this.but_7);
// KEY 8
this.but_8 = new Button();
this.but_8.BackColor = bgcolor_Num;
this.but_8.Location = new Point(col[2], row[2]);
this.but_8.Size = key_Size;
this.but_8.Text = "8";
this.but_8.Click += new EventHandler(but_num_Click);
this.Controls.Add(this.but_8);
// KEY 9
this.but_9 = new Button();
this.but_9.BackColor = bgcolor_Num;
this.but_9.Location = new Point(col[3], row[2]);
this.but_9.Size = key_Size;
this.but_9.Text = "9";
this.but_9.Click += new EventHandler(but_num_Click);
this.Controls.Add(this.but_9);
// KEY ×
this.but_mul = new Button();
this.but_mul.BackColor = this.bgcolor_Op;
this.but_mul.Location = new Point(this.col[4], this.row[2]);
this.but_mul.Size = this.key_Size;
this.but_mul.Text = "×";
this.but_mul.Click += new EventHandler(this.but_ope_Click);
this.Controls.Add(this.but_mul);
// KEY ±
this.but_sign = new Button();
this.but_sign.BackColor = bgcolor_Func;
this.but_sign.Location = new Point(col[0], row[3]);
this.but_sign.Size = key_Size;
this.but_sign.Text = "±";
this.but_sign.Click += new EventHandler(but_func_Click);
this.Controls.Add(this.but_sign);
// KEY 4
this.but_4 = new Button();
this.but_4.BackColor = bgcolor_Num;
this.but_4.Location = new Point(col[1], row[3]);
this.but_4.Size = key_Size;
this.but_4.Text = "4";
this.but_4.Click += new EventHandler(but_num_Click);
this.Controls.Add(this.but_4);
// KEY 5
this.but_5 = new Button();
this.but_5.BackColor = bgcolor_Num;
this.but_5.Location = new Point(col[2], row[3]);
this.but_5.Size = key_Size;
this.but_5.Text = "5";
this.but_5.Click += new EventHandler(but_num_Click);
this.Controls.Add(this.but_5);
// KEY 6
this.but_6 = new Button();
this.but_6.BackColor = bgcolor_Num;
this.but_6.Location = new Point(col[3], row[3]);
this.but_6.Size = key_Size;
this.but_6.Text = "6";
this.but_6.Click += new EventHandler(but_num_Click);
this.Controls.Add(this.but_6);
// KEY −
this.but_minus = new Button();
this.but_minus.BackColor = bgcolor_Op;
this.but_minus.Location = new Point(col[4], row[3]);
this.but_minus.Size = key_Size;
this.but_minus.Text = "−";
this.but_minus.Click += new EventHandler(but_ope_Click);
this.Controls.Add(this.but_minus);
// KEY C
this.but_clear = new Button();
this.but_clear.BackColor = bgcolor_Cls;
this.but_clear.Location = new Point(col[0], row[4]);
this.but_clear.Size = key_Size;
this.but_clear.Text = "C";
this.but_clear.Click += new EventHandler(but_clear_Click);
this.Controls.Add(this.but_clear);
// KEY 1
this.but_1 = new Button();
this.but_1.BackColor = bgcolor_Num;
this.but_1.Location = new Point(col[1], row[4]);
this.but_1.Size = key_Size;
this.but_1.Text = "1";
this.but_1.Click += new EventHandler(but_num_Click);
this.Controls.Add(this.but_1);
// KEY 2
this.but_2 = new Button();
this.but_2.BackColor = bgcolor_Num;
this.but_2.Location = new Point(col[2], row[4]);
this.but_2.Size = key_Size;
this.but_2.Text = "2";
this.but_2.Click += new EventHandler(but_num_Click);
this.Controls.Add(this.but_2);
// KEY 3
this.but_3 = new Button();
this.but_3.BackColor = bgcolor_Num;
this.but_3.Location = new Point(col[3], row[4]);
this.but_3.Size = key_Size;
this.but_3.Text = "3";
this.but_3.Click += new EventHandler(but_num_Click);
this.Controls.Add(this.but_3);
// KEY +
this.but_plus = new Button();
this.but_plus.BackColor = bgcolor_Op;
this.but_plus.Location = new Point(col[4], row[4]);
this.but_plus.Size = key_Size2;
this.but_plus.Text = "+";
this.but_plus.Click += new EventHandler(but_ope_Click);
this.Controls.Add(this.but_plus);
// KEY 0
this.but_0 = new Button();
this.but_0.BackColor = bgcolor_Num;
this.but_0.Location = new Point(col[0], row[5]);
this.but_0.Size = key_Size;
this.but_0.Text = "0";
this.but_0.Click += new EventHandler(but_num_Click);
this.Controls.Add(this.but_0);
// KEY 00
this.but_00 = new Button();
this.but_00.BackColor = bgcolor_Num;
this.but_00.Location = new Point(col[1], row[5]);
this.but_00.Size = key_Size;
this.but_00.Text = "00";
this.but_00.Click += new EventHandler(but_num_Click);
this.Controls.Add(this.but_00);
// KEY ・
this.but_dot = new Button();
this.but_dot.BackColor = bgcolor_Dot;
this.but_dot.Location = new Point(col[2], row[5]);
this.but_dot.Size = key_Size;
this.but_dot.Text = "・";
this.but_dot.Click += new EventHandler(but_dot_Click);
this.Controls.Add(this.but_dot);
// KEY =
this.but_equal = new Button();
this.but_equal.BackColor = bgcolor_Eq;
this.but_equal.Location = new Point(col[3], row[5]);
this.but_equal.Size = key_Size;
this.but_equal.Text = "=";
this.but_equal.Click += new EventHandler(but_equal_Click);
this.Controls.Add(this.but_equal);
}
///////////////////////////////////////////////////////////////////////////
// but_num_Click - NUMBER key enter

private void but_num_Click(object sender, EventArgs e)
{
Button but = (Button)sender;
string n = "0";
switch (but.Text)
{
case "0" : n = "0"; break;
case "00" : n = "00"; break;
case "1" : n = "1"; break;
case "2" : n = "2"; break;
case "3" : n = "3"; break;
case "4" : n = "4"; break;
case "5" : n = "5"; break;
case "6" : n = "6"; break;
case "7" : n = "7"; break;
case "8" : n = "8"; break;
case "9" : n = "9"; break;
}
if (entry_started == false)
{
lab_0.Text = "0";
}
if (lab_0.Text == "0")
{
if (n != "00")
{
lab_0.Text = n;
}
}
else
{
lab_0.Text = lab_0.Text + n;
}
entry_started = true;
}
///////////////////////////////////////////////////////////////////////////
// but_dot_Click - DOT key enter

private void but_dot_Click(object sender, EventArgs e)
{
if (entry_dot == false)
{
if (entry_started == false)
{
lab_0.Text = "0.";
}
else
{
lab_0.Text = lab_0.Text + ".";
}
entry_started = true;
entry_dot = true;
}
}
///////////////////////////////////////////////////////////////////////////
// but_func_Click - Function key enter

private void but_func_Click(object sender, EventArgs e)
{
decimal n;
Button but = (Button)sender;
switch (but.Text)
{
case "±" :
n = decimal.Parse(lab_0.Text) * -1;
lab_0.Text = n.ToString();
break;
case "%" :
n = (decimal)(double.Parse(lab_0.Text) / 100);
lab_0.Text = n.ToString();
break;
case "√" :
try
{
n = (decimal)Math.Sqrt((double)decimal.Parse(lab_0.Text));
lab_0.Text = n.ToString();
}
catch
{
lab_0.Text = "E";
}
break;
}
entry_started = false;
entry_dot = false;
}
///////////////////////////////////////////////////////////////////////////
// but_ope_Click - OPERATION key enter

private void but_ope_Click(object sender, EventArgs e)
{
Button but = (Button)sender;
if (entry_operation != null)
{
but_equal_calculation();
}
entry_operation = but.Text;
entry_result = decimal.Parse(lab_0.Text);
entry_started = false;
entry_dot = false;
}
///////////////////////////////////////////////////////////////////////////
// but_equal_Click - EQUAL key enter

private void but_equal_Click(object sender, EventArgs e)
{
if (entry_operation != null)
{
but_equal_calculation();
}
entry_started = false;
entry_dot = false;
}
///////////////////////////////////////////////////////////////////////////
// but_equal_calculation - sub for EQUAL key enter

private void but_equal_calculation()
{
decimal n = 0;
switch (entry_operation)
{
case "+" :
n = entry_result + decimal.Parse(lab_0.Text);
lab_0.Text = n.ToString();
break;
case "−" :
n = entry_result - decimal.Parse(lab_0.Text);
lab_0.Text = n.ToString();
break;
case "×" :
n = entry_result * decimal.Parse(lab_0.Text);
lab_0.Text = n.ToString();
break;
case "÷" :
try
{
n = entry_result / decimal.Parse(lab_0.Text);
lab_0.Text = n.ToString();
}
catch
{
lab_0.Text = "E";
}
break;
}
entry_operation = null;
}
///////////////////////////////////////////////////////////////////////////
// but_clear_Click - CLEAR key enter

private void but_clear_Click(object sender, EventArgs e)
{
lab_0.Text = "0";
entry_started = false;
entry_dot = false;
entry_result = 0;
entry_operation = null;
}
///////////////////////////////////////////////////////////////////////////
// but_memo_Click - MEMORY key enter

private void but_memo_Click(object sender, EventArgs e)
{
Button but = (Button)sender;
switch (but.Text)
{
case "M+" :
entry_memory = entry_memory + decimal.Parse(lab_0.Text);
break;
case "M-" :
entry_memory = entry_memory - decimal.Parse(lab_0.Text);
break;
case "MRC" :
lab_0.Text = entry_memory.ToString();
entry_memory = 0;
break;
}
entry_started = false;
entry_dot = false;
}
}
}
// END PROGRAM

電卓の機能についてはデバッグが不十分です、ご了承ください。
Fedora 上でのビルド、および実行は次のようにします。

$ gmcs -pkg:dotnet -target:winexe dentaku.cs
$ mono dentaku.exe &


Windows XP へ dentaku.exe をコピーして、(ダブルクリックして)実行した結果を以下に示しました。

 

2010-07-25

Mono で GUI プログラミング (1)

Monoは、Ecma 標準に準じた .NET Framework 互換の環境を実現するためのオープンソースソフトウェアプロジェクトです。
FAQ [1] によると、Mono はスペイン語で猿の意味で、We like monkeys とあります。ロゴはゴリラをデザインしたもののようです。

Mono を利用すると、クロスプラットフォーム間(ここでは Linux と Windows)で簡単に同一の GUI アプリケーションを動作させることができそうです。

自宅では Linux、会社では Windows(しかもいまだに Windows XP)を使っているので、手軽に双方で動作するアプリケーションを Linux で開発するのであれば、Mono はまさにピッタリの開発環境です。MinGW クロスコンパイル環境よりもお手軽な感すらあります。

そこで、(C# 使いの Windows ユーザの方にとっては「今更」な感があるでしょうが)Windows 上で動作させることを意識した Mono の GUI プログラミングについて、調べた結果をまとめていきたいと思います。

最初は Hello World


まずは、おきまりの Hello World プログラムで動作確認です。当面は C# を使います。以下のソースを hello.cs として適当な場所に保存しておきます。

using System;
using System.Windows.Forms;
 
class HelloWorldApp {
public static void Main() {
MessageBox.Show("こんにちは、世界!", "mono");
}
}


mono のインストール


乱暴な方法ですが、私は以下のように mono- で始まるパッケージ(と関連するパッケージ)を全部インストールしてしまっています。使用している Linux は Fedora 13、mono のバージョンは 2.6.4 です。

$ su
パスワード:
# yum install mono-*
読み込んだプラグイン:presto, refresh-packagekit
...
(以下省略)


C# コンパイラとビルド


Mono 2.6.4 で利用できる C# コンパイラは以下の 4 種類です。[2]

mcs: compiler to target 1.1 runtime (to be deprecated with Mono 2.8).
gmcs: compiler to target the 2.0 runtime.
smcs: compiler to target the 2.1 runtime, to build Moonlight applications.
dmcs: the C# 4.0 compiler, and references the 4.0 runtime.

ここでは gmcs を使い、次のように hello.cs をビルド・実行します。

$ gmcs -pkg:dotnet -target:winexe hello.cs
$ mono hello.exe


コンパイルの際、-target:winexe を省略すると、Windows 上で実行した時に、コマンドプロンプトのウィンドウも表示されます。

同じく Linux 上の Wine で実行した結果を以下に示しました。

$ wine hello.exe


Wine には .Net Framework 2.0 をインストールしています。


Windows XP へ hello.exe をコピーして実行した結果は以下の様になります。

ちなみに XP では .Net Framework 3.5 までインストールされています。そのため、今のところ gmcs を使っていれば十分のようです。

参考サイト


[1] FAQ: General - Mono
[2] CSharp Compiler - Mono
 

【注記】情報が古くなりましたので、下記で内容を更新しました。