アリスは無限にプラダから猫を取り出す

アリスはプラダがお好き | Moonmile Solutions Blog
http://www.moonmile.net/blog/article/alice-plada

アリプラシリーズの第24弾めは、イテレーターの話…というか、とある林檎な国では for がなくなって for in だけになってしまったものだから、いやいや、配列とコレクションがごっちゃになってないか?と思って作ろうと思ったのだけど、横道に逸れてイテレーターになってしまったという訳。

最近のプログラム言語ではコレクションの全ての要素に対して何かやるときは、foreach を使う訳ですが(林檎の国では for in ですね)、これはコレクションの中身のすべてのアクセスするというだけで、特に順序は決まっていません。まあ、実装上は最初の要素からアクセスするんだけど、Javascript のように実はとびとびだったり、そもそもコレクションと配列の違いってそういう「順序性」のところにあります。順序が保証されていないってことですね。順序が保証されないってことは、逆順ってのもないわけで、すべての要素に1回ずつアクセスするってだけです。実際のところは、各プログラム言語のスペックによるのだろうけど、配列とコレクションの違いってのはそういうところにある。
なので、コレクションを foreach で探索したときに、ひとつ前の要素とあわせて何かする(たとえばフィボナッチ関数)というような他の要素をアクセスして順序が問題になる場合は、再帰関数を使ったりするのだが、まあ、それの例っぽいものを示しておこう。

こんな感じに、Bag クラスと Cat クラスを作ってみる。鞄の中には猫か鞄を入れられる。鞄の中には鞄が入るので、実は無限に鞄が入るとい不思議な Bag クラスになっている。いや、これは DOM と一緒で XNode がそれの仕組みなっているので、一般的なツリー構造のアプローチになっている。

class IObject { }
class Cat : IObject
{
    public string Name { get; set; }
    public Cat( string name ) {
        Name = name;
    }
}
class Bag : IObject
{
    public List<IObject> Items { get; set; }
    public Bag () {
        this.Items = new List<IObject>();
    }
    public Bag Add( params IObject[] items )
    {
        foreach ( var it in items ) { this.Items.Add(it); }
        return this;
    }
    // 猫だけ取り出す
    public IEnumerable<Cat> GetCats()
    {
        foreach (var it in this.Items)
        {
            if (it is Cat)
            {
                yield return it as Cat;
            }
            if (it is Bag)
            {
                foreach (var ii in (it as Bag).GetCats())
                {
                    yield return ii;
                }
            }
        }
    }
}

Bag クラスには、猫だけを取りだすための GetCats メソッドがあって、イテレーターを返すようにしてある。yield return を使って、入れ子になった Bag の中も探索できるようにしている。
Add メソッドは、子要素(Bag か Cat)を追加できるようにしてあって、これは XElement と同じ仕組みになっている。

猫のみ取り出す

Bag prada = new Bag().Add(
    new Cat("mike"),
    new Bag().Add( 
        new Cat("hop"),
        new Cat("step"),
        new Cat("jump")),
    new Cat("kuro"),
    new Cat("shiro")
    );
// 猫のみ取り出す
foreach ( var it in prada.GetCats() )
{
    Console.WriteLine("cat: " + it.Name);
}

prada の鞄に、猫と鞄を入れるんだけど、これを実行すると GetCats メソッドで綺麗に猫だけを取り出せる。

これでアリスは満足ですよね。

結果を見るとわかるんですが、鞄の中身を取り出す順番は実装依存なので、どういう順番で猫が出てくるのかは分かりません。けれども、ひとつの要素を1回ずつアクセスして取り出しています。

無限に猫を取り出す

さて、Bag クラスを見ると、Bag クラス自身を入れ子にしているので、実は自分自身を要素に加えることができる。そう、循環参照というやつだ。

// 無限バックを作る
Bag infbag = new Bag().Add(new Cat("Cheshire"));
infbag.Add(infbag);
// 無限に猫を取り出せる
foreach (var it in infbag.GetCats())
{
    Console.WriteLine("cat: " + it.Name);
}

バッグ infbag を作って、自分の鞄に追加するという無限バッグを完成させる。でもって、これも GetCats メソッドで取り出すとどうなるか?アリスは、無限にチェシャ猫を取り出すことができるのだろうか?

20160921_02

一見、無限に取り出せるように見えるけど、GetCats メソッド内で入れ子になっているからスタックオーバーフローになっちゃんですよね~、ちゃんちゃん。

サンプルコード

サンプルコードはこちら
https://1drv.ms/u/s!AmXmBbuizQkXgf0CI2eEOEnAdsI-_w

カテゴリー: 開発, C# | アリスは無限にプラダから猫を取り出す はコメントを受け付けていません

[ソフトウェア開発者の道具箱] イチゴジャムの法則

6月末に出版した 現場ですぐに使える! Visual Basic 2015 逆引き大全 520の極意 なのですが、この第19章には不思議な Tips が入っています。章自体は「トラブルシューティングの極意」ということで、運用保守や設計段階の落とし穴について書いた章なのですが、最後の10個の tips が「対人関係」になっています。端的に言えば、トラブルシューティングなんて、その場その場の経験の積み重ねと、根本的な対処の方法を知っていればよいだけなので、あまり表面的な対症療法なものは、これ以上追加したくなかったんですよね。というのと、試しに「技術書に技術的なじゃないものを紛れ込ませてみたかった」というのがあります。真面目に書くと、えらい時間が掛かるのだけど、Tips 風に1頁にまとまるぐらい(1000字ぐらい)を目標に書き下してみました、という具合です。

でもって、ものがVBだったからなのか、やっぱり読者対象がずれていたのか分かりませんが、まったく反響がないので、ここにブログの記事としていくつかついかしておきます。中身的には、見積もり本舗とか消えてしまった別のブログに書いてきたものでもあるのですが、なんらかの形で検索に引っ掛かるようにするもひとつかと。あと技術顧問的なことをやるので「ここ読んで」という具合にできるようにしておきます。

タイトルはワインバーグ著「コンサルタントの道具箱」から持ってきてるので、いわゆる「ソフトウェア開発者の道具箱」です。コンサルタントだと、どこかのシャチョーさんとかマネージャーを相手にするのですが、ソフトウェア開発者の場合は同僚やプロジェクトリーダー、新人、顧客の担当者が相手になります。書籍のほうは、改変が効かないのである程度オブラートに包んで話をしていますが、バックグラウンドには行動経済学とか心理学とか孫子とかそんなものも入っています。参考文献としては「コンサルタントの道具箱」しか出てきませんが、そのあたりは「読み取って」頂くというのが主旨でもあったり、そもそも「イチゴジャムの法則」ってのがそんなものなのです。

中心に何か伝達したいものがある、というのをイチゴジャムの法則では「粒」と表しています。その粒は、広げようと薄めようと逆に濃くしようとも同じなわけで、過不足なく書けば「粒」そのもになるけど、人によって受け取り方が違ったり詳しい説明が必要だったりするときには、粒の外側に解説を付け加えます。まあ、それが書籍の売りだったりするし、「コミュニケーション」とか「会話」とか呼ばれるものですよね。ツーカーの関係であれば、メール一本で済むようなところも、顧客との関係であれば電話をするなり相手先まで行くなりの「礼儀」的なコミュニケーションを必要とします。それをしないと「コミュ障」と呼ばれたり、相手からコミュニケーションの取れない人と排斥されてりします。書籍にちらっと書きましたが、このコミュニケーションを過剰に付ける技というものあります。いわゆる「広告」的なものだったり、上滑りな議論だったり、暇つぶしのお昼のワイドショーだったりします。「粒」がないのですから、どこまでも薄く薄く引き伸ばせるし、そもそも「粒」がないのだから、議論にも反論にもなりません。ただ、ぴーちくとさえずっているだけになります。そう、暇つぶしだったらそれでいいんですけどね。残念ながら、人によっては「粒」のありかが何処なのか見えなくなってしまって、疑似的な「粒」に引っ張られて議論がどこかに飛んで行ったりします。いや、もともと「粒」がないのだから、議論自体にもならないんですけどね。

そういうわけで、とあるコミュニケーション(顧客との間柄だったり、チーム内の間柄だったり)の中には「粒」があったりなかったりするわけで、若い頃には「粒」がない会話にイライラすることもあるでしょうが、いやいや歳をとると「粒」ない会話はそれはそれとして、頭の中で別な「設計」を考えたりできるようになるので大丈夫です。そのあたりの話は、けらえいこ著「たたかうお嫁さま」や「セキララ結婚生活」に詳しく書いてあります。

ちなみに、「粒」とはいっても、クオリアみたいにこれと示せるものではありません(つーか、クオリア自体がアレなので、そういう意味では「粒」ない会話を「クオリア的な」と言ってもよいかしれません)。そこは双方の合意のものとに通じるものでもあり、共通の個人的体験の重なりや、重ならないけどツーカーに考えられる、というようなひとつの単語や文では表せないものかもしれません。明示的なものであれば、UMLのようにあらわせるのですが、概念的なものは伝えにくいので暗喩(メタファ)を使います。メタファーを重ねることにより色濃くなる中央を明らかにするんですね。これは質問の応酬という形でも表せる(ディスカッションとは違います)ので、双方、それなりの技能が必要です。そういう意味では、文体が読者を選ぶというものもあり、かつこの文章自体もそういうメタファーに含まれているという意味で、やっぱり「粒」的なものを強化するために「イチゴジャムの法則」をもう一度持ち出してみるわけです。

河合隼雄著「中空構造日本の深層」のように、中空であるからこそ中央にあるものを双方が了解できるという場合も多いのです。私はこの方法を好みますが、最近の世の中はそうではなくなりつつありますよね。実際に中央にあるものを右に寄せたり左に寄せたりすれば、当然ならが双方の主張が異なるのだから議論が一致するはずはありません。一致することもなく、妥協もせずなのだから、そこは社会は広く階層化されている(ねじれの位置にあるという意味で)交差位置で満足すればよいのです。

さて、話を元に戻すと、ある程度価値観が一緒だったり、共通の体験があれば粒を使ったコミュニケーションが楽≒効率よくなります。まあ、そこがアジャイル開発的なチームビルディングなんですけどね。スクラムの本にもそう書いてあります。ある意味で、チームで残業をしたりチームで経営的な視点を持ったりチームで趣味を共有できたり、というのは、濃厚なコミュニケーションを欲しているのではなく(そういう場合もあるけど)、仕事上の煩雑なコミュニケーションを省略するために「あらかじめ」価値観を共有するという目的があります。なので、チームビルディングにおいて、スタート時点の「価値共有」と同時に「仕事上の価値の共有をお互いに認めることができるか?」というハードルも必要となり、組織だからこ組織に成り得るという分岐がそこにはあります。

そうそう、もうひとつのチームビルディングの仕方として「オーケストレーション」もあるので、こっちのは別の機会に。いわゆる LLC(合同会社) です。

カテゴリー: ソフトウェア開発者の道具箱 | [ソフトウェア開発者の道具箱] イチゴジャムの法則 はコメントを受け付けていません

Bitbucket にプライベートな git を作る方法

月7$ 払って github にプライベートレポジトリを作ればいいんだけど、その前にお試し的に Bitbucket で作って運用してみるテスト。大抵の場合は、面倒なので public で github で作ってしまえばいいのだが、ちょっと内部的なのだと非公開で作りたいときもあるので、メモとして。

既存のものからアップロードしようとしたり、あれこれやったんだけど、なんかうまくいかない。したかがない(?)ので、Bitbucket の手順に従ってちまちまやるのが一番手っ取り早いです。

image

リポジトリの作成から、新規リポジトリの作成

image

新しいリポジトリが作成できたら、コマンドラインの「私はゼロからスタートします」をコピペしながら実行する。「SourceTreeにクローン」のほうは、Firefox で動かなかったので

image

 

mkdir /path/to/your/project
cd /path/to/your/project
git init
git remote add origin https://moonmile@bitbucket.org/moonmile/sample.git

 

git init と git remote を使って、新規作成したレポジトリをローカルの PC に作れば ok.

あとは、ローカル PC で、Visual Studio でプロジェクトを作るとか、他に保存してあるプロジェクトをコピーするとかして、commit, push すればよい。

他の PC や Mac では git clone すればよいので、これでコードの同期が簡単にできる。

カテゴリー: 開発 | Bitbucket にプライベートな git を作る方法 はコメントを受け付けていません

MVVM+MVCパターンを大規模システムにスケールする

大規模プロジェクトで MVVM を導入するときの注意 | Moonmile Solutions Blog をもう少し突っ込ん考えてみます。大規模といっても、みずほみたいにできあがらなくて破綻したパターンもあれば、特許庁の場合のように設計時で破綻した場合もあれば、豊洲のように作ってからそもそも破綻しているのもあるわけで、常々プロジェクトの成功率は30%程度なので、大抵は破綻します。まあ、途中でプロジェクトとしてうまく失敗させる(一気に解散する)か、うまくプロジェクトを軟着陸できずにだらだとお金をつぎ込んで「失敗」させることがことができず終わる二百三高地になるやもしれません。

さて、それなりの資源(人と金)をつぎ込んでペイができるかどうかのバランスを保つ場合(公共事業の場合は、バランスを保たなくてもよい場合もあるので別)、「失敗が許されない」パターンがあります。しかし、失敗が許されないの対偶を人は「必ず成功する/させる」と受け取りますが、実は「全面的に失敗という状態にならければよい」わけで、部分的に成功であればつぎ込んだモノへの回収ができます。端的に言えば、工事進行基準になるのですが、実際ソフトウェアの開発工程に工事進行基準を持ち込んで、プロジェクトが半分で頓挫したときにそのプロジェクトには半分の価値があるのか?というと怪しいところがあります。と言いますか、大抵の場合、無いです。例えば、脱サラでよくやる飲食店のパターンは、初期投資でキッチンを購入したりするのですが、途中で潰れるとキッチンを下取りできるので、そこそこペイができます。が、ソフトウェア開発の場合、注力するもののほとんどが「人件費」なので、途中で頓挫したからといって、それを中古として出すことはできません。まあ、頓挫したサイトを売り買いするパターンもあるけど(会員データ込みとか?)大抵の場合は、工事進行基準とかEVのように価値が右上がりになるのではなく、プロジェクトの終了直前(完了直前)に一気に価値があがるのが普通です。ですが、長期のプロジェクトは人数が多い大規模なプロジェクトになると、最後の一気にあがる価値の時期に達しない場合は、まるごと価値がなくなるということなり、EV的(アーンドヴァリュー的)になだらかな価値が生み出されているという訳ではありません。

image

発注側としては、まるで既製品の車を買うように完成した品を即使いたいわけですから、納品直前に支払いをするほうが得なので、↑の図は望ましい形になります。支払を遅らせるという点で、資金活用もできますからね。ですが、請負側としては、支払いは納品により一括だし、途中の破綻のリスクを負わなければならず、たまったものではないので、プロジェクト工程を分けて分割支払いにします。

image

こんな風に段階的にリリースして、価値を発注側に提供して支払いを得ます。プロジェクト工程でいれば、要件定義とか設計工程とか製造工程とかいうやつですね。実際、それぞれの工程に確かな「価値」があるかどうかはわかりませんが、価値があるかのように振る舞うわけです。それぞれの工程が別の人物/会社にて行われているときには、いわゆる「作り逃げ」の手が使えるわけで、豊洲とか特許庁のあれはそのパターンです。その工程の本来の「価値」とは何かを忘れ去っています。

さて、段階的なリリースをプロジェクトの各工程にあてはめましたが、アジャイル開発のイテレーション開発の方法では、この段階的なリリースは「完成品」をしてひとつのプロジェクトとしています。スクラムで言えば、2週間ごとにリリースするとか、コアな部分だけ先に作って後から顧客の要望を取り入れる(あるいは削っていく)という方法がありますね。そのような、短期的なリリース(分割リリース)をもって、全体のプロジェクトを長期に引き延ばせば、マイグレーション開発というスタイルになるだろうし、そこは保守/運用を絡めて継続的に開発していくというスタイルを取ります。勿論、この継続したスタイルや段階リリースがすべてのプロジェクトに当てはまるわけではありません。近いところでは、Apple の Map の惨事は、ひょっとして「段階リリースの一部」といえば許せるかもしれませんが、製品とみたときはダメですよね。紙の書籍であっても、買ったときは表紙だけあって、あとから中身をちょっとずつ届けますと言われても、表紙が紙の状態では買いません(当たり前)。段階的なリリースあっても、顧客の視点からみて「何らかの形で価値が完成されて」いないと、価値とはみなされませんし、対価を支払う気にはなりません。そうそう、「月刊○○」のように毎月500円ずつ買わせるというパターンは、実は段階的な価値提供の成功例かもしれません。

発注視点で分割する

車のように完成したものしか価値がない場合と違って、実はソフトウェア開発において「分割リリース」は、発注側にとってもメリットがあるものです。必ず、そのプロジェクトが完成すると保障されているとしても(プロジェクトが失敗するリスクを0としたとしても)、発注側にとって5年後に5億円を支払って価値を得るよりも、1年目に1億円を支払って活用したしたときの価値を得たほうが投資率はいいですよね。また、4年後のオリンピックを目標にするならば、4年後に4億円を支払って価値を得るというスタイルよりも、1年目に2億円の支払いをして半分の価値を得たほうが、先行者利益を得られる可能性が高まります。

そんな訳で、長期かつ大規模なプロジェクトの場合、発注側の視点での「分割リリース」が意外と重要になってきます。大抵の場合は、ウォーターフォール開発やイテレーション開発の視点でプロジェクト工程として分けたり、アジャイル開発にして顧客の要望に沿わせたりするのですが、それとは違う視点での「分割」の仕方が必要なことがわかります(そういう視点で、アジャイル開発をしているならば別ですが)。

大規模プロジェクトを発注視点で分割する

5から10名程度がアジャイル開発の基本ならば、人海戦術的にウォーターフォールやPMBOKを活用するのが規模の大きい開発の基本とはなるのでしょうが、もう少し平易に、前段階の時点での分割を考えます。

先に話したように、顧客/発注側の都合を加味してプロジェクトを分割してみると、

  • 投資効果の大きい部分を先行リリースする。
  • 先行リリースされたものを使う場合は、完成品よりも人手がかかる。
  • 先行リリースされたものは不備/不足が多いため、人手で回避する。

ということになります。どこかの Xamarin.Forms のプロジェクトを想像させますが、まあ、あれと似たような感じです。OSS ならば、ひとまず公開してしまって(Xamarin.Forms はクローズドからスタートだけど)、それ自身に価値があるのかどうかを市場評価したうえで、継続的に開発をすすめるか/あるいは撤退するかの判断ができますよね。開発者は無限に時間を使えるわけではないのですから。

これは、多少ポンコツではあっても駅まで荷物を運べる車が便利とか、多少メンテナンス料金がかかったって時間が1/10で済むのであればロボットで自動化させたお菓子の生産ライン、みたいな感じですよね。人は、最初の材料を入れるところと梱包のところを任されていたり、ロボットが動かなくなったときのために張り付くわけです。

そうそう、iPhone のような完成したスタイルを求める場合は、分割リリースのほうが嫌われるわけで、そのあたりは Apple Map の叩かれ具合と Bing Map の叩かれ具合から察することができます。

分割リリースによる多少の不具合/不備を、発注側が受け入れるかわりに、先行投資としての価値を発注側が最終リリースのときよりも多く受け取れる、という価値を発注者側が熟知している、という前提が重要になります。

image

それらを前提にして、発注側視点で分割をしたとき、たとえば MVVM パターンの場合、大規模開発に参画している会社/チームは、View や ViewModel などに特化している訳ではなく、それぞれの業務知識の範囲≒ドメイン内で分割します。

請負視点を加味する

さて、発注視点であれば、投資効果の高い部分からの開発&リリースをすればよいということになります。ということは、設計段階において、いくつかの制限がでてきます。

  • とあるモジュール/ライブラリ/コンポーネントが、先行リリースで独立して動けること
  • とあるモジュール/ライブラリ/コンポーネントが、継続的に改修可能であること
  • とあるモジュール/ライブラリ/コンポーネントが、他システムと連携して動作可能であること

一見、当たり前のように見えますが、最終リリース型のウォーターフォール開発を行ったとき(最終リリース型のアジャイル開発でも同じ)、各コンポーネントは独立して動く必要なく他コンポーネントと密接な連携をしていても構いません。また、最終系までリリースが延期されるので、リリース後の改修を考慮に入れなくてもよいパターンが存在します。それにより、拡張性を無視することによる開発の効率化やコストカットが可能です。また、リリースまで動作が保証されないので、他システム/コンポーネントとの連携動作は最終リリース時まで保留になっていてもよいのです。たとえば、テスト環境できっちり動作することがプロジェクト運行時に保証されていれば、それが途中途中で他コンポーネントと連携させて無理に動作させる必要はありません。いわゆる、糊やにかわ的な部分を作る必要がなくなり、これもコストカットに貢献しています。

このため、多分にしてコスト高になりがちな分割リリースですが、そのコスト高をうわまる先行投資のペイを発注側が求めているならば(あるいは、発注側に認めさせることができれば)、このような分割リリースのための設計は意味があります。

ひとつの方法は「デザイン・ルール」のモジュール化により、各依存関係(設計時の変更工数も含め)を求めて、できるだけ関係の少ない状態にします。この本自体は、アイデアに過ぎず実体は伴わないのですが、私は「アイデア」として利用しています。

MVVM+MVC と TDD を活用する

肝としては、「設計段階で分割を考慮した設計をする」だけなのですが、そのあたりをアーキテクトと呼んだり呼ばなかったりしますが、まあ、少なくとも分割を考慮しない設計をした場合は、分割リリースはできません。継続リリースも困難ですよね。

image

一例を出すと、

  • ブラウザ
  • クライアントアプリ C#
  • サーバーシステム PHP

というシステムの分割の仕方があります。クライアントサイドでは MVVM パターンを使い、サーバーサイドでは MVC パターンを使う。Model はシリアライズ機能を用意しておき、ネットワーク系でデータのやり取りを行う。データベースは、サーバーサイドにあるパターンですね。クライアントは、Windowsデスクトップクライアントと、ブラウザでアクセスするクライアントを用意します。ブラウザからは、jQuery で Web API を調節呼び出してもよいし、別途 CakePHP 側にブラウザ用の View を作ることもできます。

当然のことながら、それぞれのコンポーネントは業務に直結しているわけではないので、各システム(在庫システムとか顧客管理システムとか)に応用が可能だし、実際応用ができます。コンポーネント自体は、独立で動作できるように TDD を用意しておきます。

難点としては、何かの機能を付け加えようとしたときに、クライアントサイドからサーバーサイドの一貫を見直さなければいけません。機能を追加したときに(特に削減したときに)、他のシステムに提供している機能が変化していないかを随時チェックする必要があります。それらを、手作業/人海戦術でやっていては、お金がいくらあっても足りません。何らかの形で自動化を行います。逆に言えば、自動化できないのであれば、そこの機能は常に切り捨てられるものであり、一品ものでしかないということです。

上記のシステム例で言えば、それぞれのコンポーネントのインターフェースが揃った状態で、ひとまず分割リリースの用意ができています。インターフェースしかないので、中身がからっぽだから何も動作はしないけど、でも、分割リリースができる程度の動作はするわけです。つまり、動くものの価値を発生さることが可能です。ほら、さきの EV 的な価値創出に近づいたでしょう。

image

そりゃ、要件定義も大切だけど(機能を絞るとか)設計がボロボロだったら、何もできあがりませんよね…ってな具合です。

横断型の TDD は、コンポーネントごとに TDD を作る方法とは違うので、これはまた別途書きます。あと、連携するコンポーネントが多くなると、先に書いた通り機能を追加する旅にあちこち手を入れる(MVC パターンだったら必ず3箇所に手を入れるとか)になるので、ここも別途手法を変えます。じゃないと手数ばかりかかって余計大変になるので失敗する率があがります。

カテゴリー: 設計 | MVVM+MVCパターンを大規模システムにスケールする はコメントを受け付けていません

アリスは System.Json に出会う

ということで、さっそくダウンロードして、無理矢理 .NET Framework のほうでビルドします。ええ、.NET Core じゃなくて、普通の .NET Framework に持って来るわけですね。

corefx/src/System.Json at master ・ dotnet/corefx
https://github.com/dotnet/corefx/tree/master/src/System.Json

から System.Json の src 以下をごっそり持ってきます。

Visual Studio 2015 でクラスライブラリを作成

実は、そのまま corefx のプロジェクトを持って来て少し変えるだけでいけるかなと思ったのですが、無理でした。

エラー時に System.SR という内部的なリソースにクラスにアクセスしているので、ビルドができません。仕方がないので、以下のようなダミークラスを作ります。

internal sealed class SR
{
    public static string ArgumentException_ArrayMustEndWithBracket { get; internal set; }
    public static object ArgumentException_ExpectedXButGotY { get; internal set; }
    public static object ArgumentException_ExpectedXDiferedAtY { get; internal set; }
    public static string ArgumentException_ExtraCharacters { get; internal set; }
    public static string ArgumentException_ExtraDot { get; internal set; }
    public static string ArgumentException_IncompleteEscapeLiteral { get; internal set; }
    public static string ArgumentException_IncompleteEscapeSequence { get; internal set; }
    public static string ArgumentException_IncompleteExponent { get; internal set; }
    public static string ArgumentException_IncompleteInput { get; internal set; }
    public static string ArgumentException_InvalidLiteralFormat { get; internal set; }
    public static string ArgumentException_LeadingZeros { get; internal set; }
    public static object ArgumentException_MessageAt { get; internal set; }
    public static string ArgumentException_NoDigitFound { get; internal set; }
    public static string ArgumentException_StringNotClosed { get; internal set; }
    public static object ArgumentException_UnexpectedCharacter { get; internal set; }
    public static string ArgumentException_UnexpectedEscapeCharacter { get; internal set; }
    public static object[] NotSupported_UnexpectedParserType { get; internal set; }

    internal static string Format( params object[]  para)
    {
        throw new NotImplementedException();
    }
}

これで、ビルドは通るようになりました。

呼び出し側は、

class Program
{
    static void Main(string[] args)
    {
        var json = @"{ ""name"": ""JSON"", ""number"": 13 }";
        var jr = new JavaScriptReader(new StringReader(json), false);
        var o = jr.Read();
        var pairs = o as KeyValuePair<string, object>[];

        Console.WriteLine("hello " + pairs[0].Value);
        Console.WriteLine("number is " + pairs[1].Value);
    }
}

な感じで、JavaScriptReader クラスを new して、Read メソッドを使います。元の System.Json のテストプロジェクトには JsonValue のテストコードしかないのですが、まあ、これでいけます。ただし、JavaScriptReader は internal なクラスになっているので、「public class JavaScriptReader」に書き替えてしまいます。

Read メソッドの戻り値は object 型なので、適当な型に直します。これ、最終的にどうするつもりかわからないのですが、ひとまず、KeyValuePairの配列で来ているので「KeyValuePair<string, object>[]」でキャストします。

実行すると、こんな感じで、アリスが JSON に出会えます。

どうも、プロパティ名はダブルクォートで囲まないと駄目なので、RFCに沿い過ぎているというか…「{ name: “JSON” }」な JavaScript の連想配列な JSON も通してほしいですね。そういうときは、NuGet で Newtonsoft.Json をインストールせよ、という話なんですが :)  コード的にはReadNumericLiteral でプロパティ名をクォートで拾っているので、それ修正すれば ok.

サンプルコード

AliceJson-v0.1-src.zip
https://1drv.ms/u/s!AmXmBbuizQkXgfxWteUSWlBU7zrT1g

カテゴリー: C# | アリスは System.Json に出会う はコメントを受け付けていません

アリスはプラダに猫がいることを知らない

な感じで、内部データを遅延して持って来る例を作ってみる。

class Program
{
    public static List<Cat> cats = new List<Cat>();
    static void Main(string[] args)
    {
        cats.Add(new Cat(&quot;tora&quot;));
        cats.Add(new Cat(&quot;mike&quot;));

        var pradaRed = new Prada();
        var pradaBlue = new Prada();
        var pradaYellow = new Prada();

        Console.WriteLine(&quot;1: {0}&quot;, pradaRed.Cat?.Say());
        Console.WriteLine(&quot;2: {0}&quot;, pradaRed.Cat?.Say());
        Console.WriteLine(&quot;3: {0}&quot;, pradaBlue.Cat?.Say());
        Console.WriteLine(&quot;4: {0}&quot;, pradaYellow.Cat?.Say());

        return;
    }
}

class Cat
{
    string _name = &quot;&quot;;
    public Cat(string name)
    {
        _name = name;
    }
    public string Say()
    {
        return $&quot;{_name}: nya-&quot;;
    }
}
class Prada
{
    Cat _cat = null;
    public Cat Cat
    {
        get
        {
            if (_cat == null)
            {
                _cat = Program.cats.FirstOrDefault();
                if (_cat != null)
                {
                    Program.cats.Remove(_cat);
                }
            }
            return _cat;
        }
    }
}

実行するとこんな感じ

意味合いとしては、アリスはプラダのバックを3つ持っている(pradaRed、pradaBlue、pradaYellow)んだが、中に猫がはいってるかどうかは分からない。どの順番で開ける猫がいるかわからないし、猫がいるかどうかも知らない。でも、開けると猫が入っているか、また猫が入ってないかわかる。というシュレディンガーなプラダな猫の話。

cats は最初から用意されているので、外部データとして存在します。メインプログラムのアリスから見れば、最初は、Prada のオブジェクトを作るだけなので、内部の _cat が存在するかどうかは知らない。というか、知らなくてもいい状態。Cat プロパティを呼んだときにはじめて、中の cat を取り出すことができる。鞄クラスの Prada から見える Cat プロパティは、外部からみれば単純にデータを返しているように見えるが、実際は別に用意されている cats から取り出している。
つまり、MVVM パターンでいえば、View が VM に対してプロパティアクセスでデータを取り出しているときに、VM は Model からデータを引っこ抜くわけだが、そこは View が VM に問い合わせたときまで遅延される方式になっている。MVVM の大抵の例は、Model が省略されていて、View から VM のアクセスしかないのだが、ここで VM と Model との違いが明確になるよという話だ。

こんな感じで MVVM パターンを使える。Model は常に VM から参照される「制限」を付けることで、MVVM パターンの役割が担えるということだ。

じゃあ、Model(データベースや計測器など)からデータが上がってくるようなときはどうするのか?ってのは別パターンを使うことになる。この話は別の記事に書くことにしよう。

カテゴリー: 設計 | アリスはプラダに猫がいることを知らない はコメントを受け付けていません

Xamarin.Forms ビルドメモ

以前より懸案だった、OSS になった Xamarin.Forms を弄ろうと思ってコードを DL してざっと目を通しているところ。

xamarin/Xamarin.Forms: Xamarin.Forms official home
https://github.com/xamarin/xamarin.forms

image

たくさんのプロジェクトがあるが、肝なところは、

  • Xamarin.Forms.Platform
  • Xamarin.Forms.Core
  • Xamarin.Forms.Pages
  • Xamarin.Forms.Xaml
  • Xamarin.Forms.Build.Tasks
  • Xamarin.Forms.Xaml.Xamlc
  • Xamarin.Forms.Xaml.Xamlg

なところですかね。

Xamarin.Forms をビルドする

Makefile があるが、Windows 上では動きません。Linux 上でビルドします。

  1. Ubuntu に github からダウンロードして
  2. Install Mono on Linux | Mono を参照にインストール
    1. mono-devel
    2. mono-complete
    3. referenceassemblies-pcl … これがないと、/usr/lib/mono/xbuild-frameworks/ が空かも。
  3. あとは、make すれば ok. *.dll は 各プロジェクトの bin/Release 等の下にできるので、あとでかき集める?
$ ls -la /usr/lib/mono/xbuild-frameworks/
total 16
drwxr-xr-x  4 root root 4096 Sep 13 01:19 .
drwxr-xr-x 16 root root 4096 Aug 29 02:26 ..
drwxr-xr-x 11 root root 4096 Aug 29 02:20 .NETFramework
drwxr-xr-x  5 root root 4096 Sep 13 01:20 .NETPortable

ビルド時に PCL を参照するのですが、先頭が . になっているので ls –a を付けないと確認できないという罠があります。

あとは、出来上がった *.dll を Xamarin.Forms プロジェクトのものと差し替えてやればいいんじゃないかな。たぶん、ローカルの nuget フォルダーを作って、そこから適当なバージョンでダウンロードできるようにするとよいでしょう。

Xamarin.Forms.Xaml.Xamlg が *.g.cs を作成する

いくつか見ていくと、結構面白いものがみつかります。XAML を使うと、コードビハインドの対象として、*.g.cs ファイルが作られます。XAML で書かれていた x:Class とか x:Name とかは、このコードに変換されるんですよね。でもって、これが C# なゆえに、F# の XAML が使えなくてタイププロバイダーで XAML を作ったりするんですが、F# のビルド時の DLL と Xamarin で実行するときの DLL が異なるからうまくいかない、という変な状態に陥っている訳ですが(この変な状態は、以前調査したときよりも分かるようになったので、別の機会に解説を)、ならば、*.g.fs を出力するようにすればよいわけです。そうすると、Xamarin.Forms の XAML に対してコードビハインドの F# コードを作るようにできるので、XAML を扱う F# が書けますよね。まあ、現状の F# の場合は PCL の問題があって構成がややこしくなるので、.NET Core 版の Xamarin が出て来たときにもうちょっと突っ込んだほうがいい気もしますが。

ちなみに、以前つくていた、moonmile/XFormsPreviewer は、動的に XAML をロードして、XAML のタグから直接 x:Name をバインドする方法なので、タイププロバイダーともコードビハインドの *.g.cs 方式とも違います。このあたりは、状況にあわせていろいろな方法があったほういいので。

でもって、もう少し、Xamarin.Forms.Xaml.Xamlg を眺めてみて、F# コードにコンバートできるか試してみますか。というところ。

カテゴリー: Xamarin | Xamarin.Forms ビルドメモ はコメントを受け付けていません

dynamic を利用して ExDoc を書き直してみる(1)

「メタプログラミング .NET」をざっと読み終わったので、手元の github から ExDoc をダウンロードしてきて、dynamic 版の ExDoc を作っているところです。

旧 ExDoc は、演算子のオーバーライトと暗黙のキャストを使って、/,*,%演算子を使って XML を探索できていました。まあ、奇妙といえば奇妙だけど、LINQ の派生みたいな感じで作れるわけです。タグを指定するのが文字列なのがあれなのと、だったら XPath で指定してもいいんじゃないか、という話もありますが、まあ実験的に。ちなみに、このライブラリは実運用で4年ほど使っています。CakePHP をサーバーにして、クライアント側の XML パースを ExDoc を使ってパースしているという技ですね。

public void TestQuery()
{
    var xml = "<root>" + 
        "<person id='1'>masuda</person>" +  
        "<person id='2'>tomoaki</person>" +  
        "<person id='3'>yumi</person>" +  
        "</root>";

    var doc = ExDocument.LoadXml(xml);

    // query element
    ExElement el = doc * "person" % "id" == "2";
    Assert.AreEqual("tomoaki", el.Value);
    // query elements
    ExElements els = doc * "person";
    Assert.AreEqual(3, els.Count);
    Assert.AreEqual("masuda", els[0].Value);
    Assert.AreEqual("tomoaki", els[1].Value);
    Assert.AreEqual("yumi", els[2].Value);
}

で、常々、PHP の SimepleXML がうらやましかった訳ですが、そんな風に直接プロパティで設定できるのが、dynamic の良いところです。まあ、インテリセンスが効かなくなってしまうのですが、どうせ XML タグ名なのだから、そこは割り切りということで。dynamic で受けて、指定の型へキャストし直して使う方法もあるので、それもおいおいと実装する予定。「メタプログラミング .NET」にも書いてあるけど、複数のインターフェースを使って、片方のインターフェースを隠す技が使えます。確か、Xamarin.Forms の XAML ツリーのところで使っています。

dynamic 版の ExDoc を使うと、こんな風に XML タグをプロパティのように扱えます。属性に連想配列を使っているのは、SimpleXML の真似でもあるのと、doc.person.@id のようにはできなかったからなのです。先頭の @ は有効になるんだけど、TryGetMember メンバでだと、「id」としか取れないので、「@id」と「id」は同じ名前として扱われるためです。残念。

public void TestQuery()
{
    var xml = @"
<root>
<person id='1'>masuda</person>
<person id='2'>tomoaki</person>
<person id='3'>yumi</person>
</root>
";
    var doc = ExDocument.LoadXml(xml);
    // query element
    var el = doc.person["id"] == "2";
    Assert.AreEqual("tomoaki", el.Value );
    // query elements
    var els = doc.person;
    Assert.AreEqual(3, els.Count);
    Assert.AreEqual("masuda", els[0].Value);
    Assert.AreEqual("tomoaki", els[1].Value);
    Assert.AreEqual("yumi", els[2].Value);
}

まあ、それでも、連想配列は属性で、配列は子要素で示せるわけで、なかなかいい感じでパースができます。

子孫要素も含めて調べるときは「*」演算子のかわりに、タグ名に「_」を付けます。プロパティ名やメソッド名を後付けでできる(実行時に内部で分解して分岐させる)のが dynamic の良いところです。

public void TestSelectAttr()
{
    var xml = @"
<html>
<body>
<a id='a1' href='link001.html'>title</a>
<a id='a2' href='link002.html'>title</a>
</body>
</html>
";

    var st = new StringReader(xml);
    var doc = XDocument.Load(st);
    var edoc = ExDocument.Load(doc);

    var el = edoc._a["id"] == "a2";
    Assert.AreEqual("title", el.Value);
    Assert.AreEqual("link002.html", el["href"]);
}

これらをどう実装しているかというと、参照だけならば以下で ok です。

– System.Dynamic.DynamicObject を継承する
– TryGetMember を実装する
– TryGetIndex を実装する
– == 演算子を再定義する。

まあ、先行きは値の設定までやるので、TrySetMember や TrySetIndex も実装していきます。
具体的な説明は、.net core の DynamicObject のコードを読むか(コードに詳しい説明が書いてあります)、「メタプログラミング .NET」にざっと書いてあります。

public class ExElement : System.Dynamic.DynamicObject
{
    public string TagName { get { return _el.Name.LocalName; } }
    public string Name {  get { return TagName; } }
    public string Value { get { return _el.Value; } }
    internal XElement _el = null;

    public ExElement( XElement el = null )
    {
        _el = el;
    }
    public override bool TryGetMember(GetMemberBinder binder, out object result)
    {
        var els = new ExElements();

        if (binder.Name[0] == '_')
        {
            string name = binder.Name.Substring(1);
            els = SelectNodes(_el, els, name, true);
        } else
        {
            els = SelectNodes(_el, els, binder.Name);
        }
        result = els;
        return true;
    }
    private ExElements SelectNodes( XElement x, ExElements els, string name, bool deep = false )
    {
        foreach (var el in x.Elements())
        {
            if (name == el.Name)
            {
                els.Add(new ExElement(el));
            }
            if (deep)
                els = SelectNodes(el, els, name, true);
        }
        return els;
    }

    public override bool TryGetIndex(GetIndexBinder binder, object[] indexes, out object result)
    {
        result = ExDocument.EmptyElement;
        if (indexes.Count() == 1)
        {
            if (indexes[0] is int)
            {
                int i = (int)indexes[0];
                if (i < _el.Parent.Elements().Count())
                {
                    result = new ExElement(_el.Parent.Elements().ToList()&#91;i&#93;);
                }
            }
            else if (indexes&#91;0&#93; is string)
            {
                string s = indexes&#91;0&#93; as string;
                var attr = _el.Attribute(s);
                if (attr != null)
                {
                    result = new ExAttr(attr);
                }
            }
        }
        return true;
    }

    /// <summary>
    /// 探索
    /// </summary>
    /// <param name="lst"></param>
    /// <param name="value"></param>
    /// <returns></returns>
    public static ExElements operator ==(ExElement el, string value)
    {
        var result = new ExElements();
        foreach (var it in el._el.Elements())
        {
            if (it.Value == value)
            {
                result.Add(new ExElement(it));
            }
        }
        return result;
    }
    public static ExElements operator !=(ExElement el, string value)
    {
        var result = new ExElements();
        foreach (var it in el._el.Elements())
        {
            if (it.Value != value)
            {
                result.Add(new ExElement( it));
            }
        }
        return result;
    }
    public override bool Equals(object obj)
    {
        return base.Equals(obj);
    }
    public override int GetHashCode()
    {
        return base.GetHashCode();
    }

    /// <summary>
    /// 文字列にキャスト
    /// </summary>
    /// <param name="el"></param>
    public static implicit operator string(ExElement el)
    {
        return el.Value;
    }
    /// <summary>
    /// 数値にキャスト
    /// </summary>
    /// <param name="el"></param>
    public static implicit operator int(ExElement el)
    {
        return int.Parse(el.Value);
    }
    /// <summary>
    /// 実数にキャスト
    /// </summary>
    /// <param name="el"></param>
    public static implicit operator double(ExElement el)
    {
        return double.Parse(el.Value);
    }
}

もうちょっと整理して、前バージョンの ExDoc と同じように使えたら NuGet にアップしましょう。

カテゴリー: C#, EXDoc | dynamic を利用して ExDoc を書き直してみる(1) はコメントを受け付けていません

ぐるっと巡って、再び DLR へ

一時期、ブームだからと思って手に取った(リフレクションやT4目当てで)「メタプログラミング .NET」なんですが、本棚を整理するときにうっかり捨ててしまったらしく、Kindle で買いなおしました → Amazon.co.jp: メタプログラミング.NET 。どうせだから、洋書で買おうかと思ったけど、英語版の Kindle は無くて、翻訳版のほうが安いってのはどうなの?

後付けでメソッドを付けるというのを考えて、最近 Python でそれができるのを知って、そういえば IronPython ってどうなったの?と思って、再び dynamic と System.Dynamic.ExpandoObject を見直し見たら、ああ以前に ExDoc の expando 版を作ろうと思っていた残骸が HDD に残っていたのを再発見、ってのが現在位置です。でもって、「メタプログラミング .NET」の第8章の最後をよんで、やっぱり DLR って死んでたのね、と想った次第。

Iron系のプログラミングは、現在どうなっているのかわかりませんが(書籍で言及しているのが WinRT 直前だったりするので)、現在 dynamic が使われいるところとすれば、

  • ASP.NET MVC の ViewBag
  • Office COM との相互運用(主に Excel やね)

ってところでしょう。Office 組み込みの .NET は完全に死んだ感じがするし、JavaScript プラグインは生きているのか死んでいるのかも分かりません。Excel VBA は未だに健在なわけですが、VBA 自体(あるいは VB6.0)の完全互換な後継を作らない限り、Office 系の VBA はそののままで、Office 386 あたりのブラウザ版が完全に「代替」になる(デスクトップ版のExcelを駆逐するという意味で)になるかは、怪しいですよね。

特に、C# や F# に別の言語体系を組み込みたいわけではないので、DLR を詳細にやろうとは思わないのですが(Unix的には、別コマンドで呼び出せばいいわけだし)、基本的な C# の使い方とはちょっと違う面を、C# の世界に取り込みたいという願望はあります。ExDoc の演算子のオーバーロードと暗黙のキャストを多用した方式や、 D4Reflection でプライベートメソッドをリフレクションを使って呼び出してみたり、XFormsPreviewer で動的に XAML を読み込んで動的にイベントメソッドに結び付けたり、という感じで、少しメタちっくに動かしています(まあ、それぞれ頓挫しているわけですが)。

メタプログラミング .NET 当時は、System.Dynamic.ExpandoObject の詳細は見れなくて(M$ と契約している場合は別だろうけど)あれこれと、互換コードを作るしかなかったのですが、.NET Core としてオープンソース化されたので、dynamic も ExpandoObject のコードも github 上のcorefx/ExpandoObject.cs at master · dotnet/corefx で確認ができます。また、Xamarin.Forms で XAML をローディングしているところも、Xamarin.Forms 自体がオープンソースになったので、Xamarin.Forms/XamlLoader.cs at master · xamarin/Xamarin.Forms でコードを確認できます。いまのところ、.NET Core に WPF や UWP の XAML は含まれていないのですが、おおむね Xamarin.Forms.XAML と似たコードになっていると推測ができます…つーか、ここの XAML をアセンブリ内のクラスに動的にバインディングしている部分を切り離せば、各種の XAML を Parser と Loading/Binding に分けられたのに、なんでこう一括してしまうかなー、という具合なのですが。まあ、このあたりのコードをまるっと持ってきて、整合性をあわせれば、オレオレ ExpandoObject や XAML 動的ローダーも作れるというわけです。

そんな訳で、System.Dynamic.* あたりを使うと Python と同じように後付けのメソッドやプロパティを作れることが分かりました。ああ、作れるな、ってのが分かって、

class ExDoc : IDynamicMetaObjectProvider,
    IDictionary<string, Object>, 
    ICollection<KeyValuePair<string, Object>>,
    IEnumerable<KeyValuePair<string, Object>>, 
    IEnumerable, 
    INotifyPropertyChanged

な感じで用意はしていたんですよね。 でも、あっという間にくじけてしまったらしい(4年前のコードだったよ)。このあたりで ExDoc を実装していくと、XML のパーサーが、

 
var name = doc.Person.Name; 

のような感じで、プロパティのように XML のタグ名を使うことができます。これは「メタプログラミング .NET」の中にも例があります。

 
// 属性値を取る 
int age = doc.Person[0].Name.@age; 
// 要素の値を比較して、タグを検索 
var person = doc.Person.Name == "masuda"; 

こんな風な裏技ができそうな気がします。dynamic は型推論をする F# と相性が悪いので、ちょっと頓挫していたのですが、RPi + Python の組み合わせでやり直そうと思っているので、もう少し突っ込んで行こうかなと思っています。

そうそう、後で、F# と PCL の闇を書かないと。どうやら .NET Core で PCL の闇が晴れそうな気がするのですが、久し振りに PCL プロジェクトを作ろうとして、こんな風になって、闇が続いていることが判明(苦笑)

image

まあ、.NET Core に統一すればいいだよね、おそらく。

カテゴリー: C# | ぐるっと巡って、再び DLR へ はコメントを受け付けていません

メソッドの後付けを python で考える

以前に、

後付け言語 post を考えてみる | Moonmile Solutions Blog
http://www.moonmile.net/blog/archives/5866

というのを考えていたのだけど、実は pytohn でそれができることをつい最近知った。

和訳 なぜPythonのメソッド引数に明示的にselfと書くのか | TRIVIAL TECHNOLOGIES 4 @ats のイクメン日記
http://coreblog.org/ats/translation-of-why-explicit-self-has-to-stay/

Raspberry Pi を触っていると Python コードに触れる機会が多いはずなのだが、Windows IoT Core を乗せたり、mono + F# という組み合わせでやっているうちに遠ざかってしまった(苦笑)ので、全然書いたことがなかった。
機械学習がやりたいわけではないけど(画像の特定ぐらいはやらないと駄目なんだが)、

Raspberry Pi Cookbook for Python Programmers [Kindle edition] by Tim Cox
https://www.amazon.co.jp/gp/product/B00JQEJE12/ref=oh_aui_d_detailpage_o01_?ie=UTF8&psc=1

という、ちょうど RPi と Python が学べそうな本があるので、買ってみた。これ、早くに駆っておくべきでしたよ。これだったら Raspbian で電子工作ができそうです。自分が結構躓いたところが丁寧に解説してありました。
RPi のほうは、手元にある Orange Pi も含めてもう少し突っ込んでやるために、Python を使えるようになろうかなと考えているところですが、その前にあの「self」って何なんだろう、に対する答えの記事にちょうど当たりました。初回にあたったのは自分的に結構ラッキーだったかなと思って、書き残しておきます。

後付けでメソッドを拡張する

ちなみに、ごく最近学び始めたばっかりなので、python2の事情は知りません。pytohn3だけを対象にしていきます。あちこちにサンプルコードがあるのですが、それが2なのか3なのかよくわからんけど、まあ、目的は PPi や組み込み Linux でロボット制御あたりに使うことなので、どっちでもいいのです。
たぶん、似たようなことは javascript の prototype でも作れるだろうけど、演算子のオーバーロードとかラムダ式の書き方とかが、自分にしっくりしてきる(というか F# に似ている)ので、python のほうが相性がよさそうです。

# 何もないAクラス
class A:
    pass
# calc メソッドを追加する
def calc(self, x):
    return x*x
A.calc = calc

a = A()
print("ans. " + str(a.calc(10)) )

# 後付けで上書きする
def calc2(self, x):
    return x*x*x
A.calc = calc2

a = A()
print("ans. " + str(a.calc(10)) )

何もメソッドやプロパティを持たない A クラスがあって、後付けでメソッドを拡張します。いわゆる拡張メソッドなのですが、最初の「def calc(self, x):」なところは、普通にグローバルな関数宣言ですよね。グローバルなクラスのメソッドのstaticメソッドとも言えます。F# なら、

let calc self x = 
    x*x

とするところです。このグローバル関数の self 引数は余分なのですが、Python の場合、後付けで A.calc メソッドを付けるときに効いてきます。

def calc(self, x):
    return x*x
A.calc = calc

C++ ならば、A クラスの calc 関数ポインタにグローバル関数を割り当てる、というイメージですよね。C++ の場合、型チェックがきついので、実際は A:calc ポインタになってしまい、グローバル関数の calc を割り当てることはできません。std::function テンプレートを使って、うまいことをやります。
C# や F# の場合も関数ポインタにして用意することもできるのですが、固定であるならば拡張メソッドを使ったほうが楽です。

type A() =
    do ()
let calc self x = 
    x*x
type A with
    member x.Calc n = calc x n 

空の A クラスを作っておいて、グローバルな calc メソッドを、A.calc メソッドから呼び出します。引数の self が無駄になっているように見えますが、実は、member x.Calc の x な部分に対応しています。つまり、F# であっても、ちょうど self/this な部分を参照できるような仕組みになるわけです。

似たようなコードを C# で書くともっと明確になります。

class A {}

class Global {
    static int Calc( A a, int x ) {
        return x*x;
    }
}

class AEx {
    static int Calc( this A a, int x ) {
        return Global.Calc( a, x )
    }
}

C# の場合、グローバルな関数を置けないので、Global クラスを作って static メソッドにしておきます。このとき、無駄な A クラスへの参照を置くわけですが、拡張メソッドを AEx.Calc として作るときに、this として A クラスのオブジェクトを置きます。つまり、明示的に this が使われていることが分かります。

こうなると、Python のメソッドは、実は C# で言うところの「拡張メソッド」にあたるのではないか?という類推ができます。

class B:
    def calc(self, x):
        return x*x

Python のクラスのメンバ関数にいちいち置かれている self は、実はグローバル関数の self 引数なのかもしれません。
そう思うと、グローバル関数と、クラスのインスタンスメソッド、クラスに後付けされるメソッドの3つの関数が同じ型を持っていることに納得がいきます。なるほど、あいまいな型のままにメソッドが呼び出されているわけではなくて、むしろ型チェックのために「self」が置かれている、と考えることができます。いや、実動作はわからないのだけど、論理的にはそう考えられるという訳です。

それらの関数が、常に関数ポインタとして働き、入れ替え可能であるという点で、もういちど、A.calc に別のメソッドを割り当てることも可能になります。

def calc2(self, x):
    return x*x*x
A.calc = calc2

実行してみると、クラスに割り当てたインスタンスメソッドがきちんと動作します。

class A:
    pass

# calc メソッドを追加する
def calc(self, x):
    return x*x
A.calc = calc

a = A()
print("ans. " + str(a.calc(10)) )

# 後付けで上書きする
def calc2(self, x):
    return x*x*x
A.calc = calc2

a2 = A()
print("ans. " + str(a2.calc(10)) )
print("ans. " + str(a.calc(10)) )

この順番で実行してみると、

ans. 100
ans. 1000
ans. 1000

のように、最初の a.calc の値と2回目の a.calc の値が異なります。同じオブジェクト(インスタンス)であっても、違う結果が出るところが面白いですよね。バグの温床になりそうですが(苦笑)。

もちろん、javascript の prototype プログラムのように、オブジェクトを対象にしてメソッドを追加することもできます。

a.calc = lambda x: x*x*x*x
print("a  ans. " + str(a.calc(10)) )
print("a2 ans. " + str(a2.calc(10)) )

こんな風にラムダ式で a オブジェクトのほうにだけ calc メソッドを上書きしてやると、a オブジェクトのほうだけ計算結果が変わります。

a  ans. 10000
a2 ans. 1000

実際は、こんな風に無造作に後付けで拡張してしまうとえらいことになってしまいそうですが、オブジェクト内の変数を弄ってクラスの振る舞いを変えるのではなく、メソッドそのものを切り替えてしまうのは面白い発想です。ダックタイピングを、クラスの利用者側から強制できるし、メソッド名から類する動作をユーザー側から決められるということでもありますね。先の例で言えば、利用するときに A クラスにあらかじめ calc メソッドがあればそれを使うし、無ければ利用者側が追加できるというイメージです。また、提供側の calc が気に入らなkれば、利用者側から書き替えてしまう。その変更は、提供側の動作にも影響を与えるわけですが、calc メソッドとしての動作が理屈にあっていれば(契約プログラミング的に)入れ替えることも自由なわけです。
利用側から内部に影響を与えるという話は、ライブラリが堅牢であることの根拠を崩すことになる(ライブラリは手をつけられない、privateな空間であるということ。または、一定の委譲ルールでしか触れないということ)のですが、実際は、利用者がライブラリを使う以前にライブラリが存在していなければならず、ライブラリの制作者未来を見通すことができない(完全にはできないので、処方箋を示すことしかできない)ので、利用者が使うときには「未知」にならざるを得ない。ならば、一定のルールでもって、ライブラリ自体に介入することを、利用者側から許してもいいのではないか?というのが「後置型」の発想のもとで、先の python のメソッドの後付けが利用できるなと思ったところです。アスペクト指向やDIよりも、もっとライブラリ内部に踏み込んでよい、という発想です。
このあたりは、もうちょっと python を弄って試してみたいところですね。

カテゴリー: 開発, F#, python | メソッドの後付けを python で考える はコメントを受け付けていません