ユーザーコントロール内で更新した値を親の ViewModel に反映させる方法

MVVM でユーザーコントロールを作るときに微妙にハマるのがユーザーコントロールです。例えば、ユーザーコントロール内に TextBox を貼り付けておいて、外側の ViewModel からユーザーコントロールに値を設定することはできるのですが、逆にユーザーコントロール内の TextBox に入力された値を ViewModel の反映しようとすると難儀します。というか、難儀していたのだけど、解決できたので書き残しておきます。

image

UserControl に対して ViewModel 側からバインドしたい場合は、DependencyProperty を使って依存関係プロパティを作って設定します。表示のための TextBlock とか DataGrid を使ったリスト表示の場合は、これで十分なのであまり問題はないのですが、さて、UserControl 内に貼り付けた TextBox から ViewModel のプロパティに反映させるためにはどうしたものか?と考えてしまうわけです。

色々調べていくと、ElementName を使って TextBox.Text に直接バインドする仕組みが紹介されているのですが、この方法だと UserControl の独立性が低くなって汎用性が低くなります。じゃあ、標準の TextBox やその他のコントロールのがりがりにカスタムで組んでしまえばできそうなものなのですが、これもちょっと大変すぎる。

BindingOperations.GetBindingExpression を使う

.NET Framework の WPF の TextBox コードを直接眺めていくと https://referencesource.microsoft.com/#PresentationFramework/src/Framework/System/Windows/Controls/TextBox.cs,77a66fa4f401a49f? BindingOperations.GetBindingExpression というメソッドがあって、バインド関係の情報を取得することができます。バインド先のプロパティ名を示す Path プロパティや、結びついているオブジェクトを示す Target プロパティなどがあります。

コントロールから ViewModel への値の設定(コントロールからソースへの反映)は、主にコントロールからフォーカスが外れたときになるので LostFocus イベントで設定すると以下のようにシンプルに書けます。

this.text.LostFocus += (_, __) => {
    BindingExpression beb = BindingOperations.GetBindingExpression(this, YourNameProperty);
    if (beb != null)
    {
        beb.Target.SetValue(YourNameProperty, text.Text);
        beb.UpdateSource();
    }
};

バインドをしていないと、BindingOperations.GetBindingExpression メソッドは null を返すのでそれだけチェックをします。

バインド可能なユーザーコントロールを作る

image

2つのテキストボックスと1つの日付コントロールを持つユーザーコントロールを作ります。ユーザーコントロール内で MVVM してしまうと、MainView の DataContext と競合してしまうので、ユーザーコントロール内では x:Name で名前を付けて参照します。


public partial class PersonControl : UserControl
 {
     public PersonControl()
     {
         InitializeComponent();
         this.Loaded += PersonControl_Loaded;
     }

    public static readonly DependencyProperty YourNameProperty =
         DependencyProperty.Register(
             "YourName",
             typeof(string),
             typeof(PersonControl),
             new FrameworkPropertyMetadata(
                 "",
                 FrameworkPropertyMetadataOptions.BindsTwoWayByDefault,
                 new PropertyChangedCallback((o, e) => {
                     var uc = o as PersonControl;
                     if (uc != null)
                     {
                         string v = (string)e.NewValue;
                         uc.text.Text = v;
                     }
                 })));
     public static readonly DependencyProperty YourAddrProperty =
         DependencyProperty.Register(
             "YourAddr",
             typeof(string),
             typeof(PersonControl),
             new FrameworkPropertyMetadata(
                 "",
                 FrameworkPropertyMetadataOptions.BindsTwoWayByDefault,
                 new PropertyChangedCallback((o, e) => {
                     var uc = o as PersonControl;
                     if (uc != null)
                     {
                         string v = (string)e.NewValue;
                         uc.addr.Text = v;
                     }
                 })));
     public static readonly DependencyProperty YourBirthdayProperty =
         DependencyProperty.Register(
             "YourBirthday",
             typeof(DateTime),
             typeof(PersonControl),
             new FrameworkPropertyMetadata(
                 DateTime.Now,
                 FrameworkPropertyMetadataOptions.BindsTwoWayByDefault,
                 new PropertyChangedCallback((o, e) => {
                     var uc = o as PersonControl;
                     if (uc != null)
                     {
                         DateTime v = (DateTime)e.NewValue;
                         uc.date.SelectedDate = v;
                     }
                 })));
         
     public string YourName { get => this.text.Text; set => this.text.Text = value; }
     public string YourAddr { get => this.addr.Text; set => this.addr.Text = value; }
     public DateTime? YourBirthday { get => this.date.SelectedDate; set => this.date.SelectedDate = value; }

    private void PersonControl_Loaded(object sender, RoutedEventArgs e)
     {
         this.text.LostFocus += (_, __) => {
             BindingExpression beb = BindingOperations.GetBindingExpression(this, YourNameProperty);
             if (beb != null)
             {
                 beb.Target.SetValue(YourNameProperty, text.Text);
                 beb.UpdateSource();
             }
         };
         this.addr.LostFocus += (_, __) => {
             BindingExpression beb = BindingOperations.GetBindingExpression(this, YourAddrProperty);
             if (beb != null)
             {
                 beb.Target.SetValue(YourAddrProperty, addr.Text);
                 beb.UpdateSource();
             }
         };
         this.date.LostFocus += (_, __) => {
             BindingExpression beb = BindingOperations.GetBindingExpression(this, YourBirthdayProperty);
             if (beb != null)
             {
                 if (date.SelectedDate != null)
                 {
                     beb.Target.SetValue(YourBirthdayProperty, date.SelectedDate);
                     beb.UpdateSource();
                }
             }
         };
    }
}

3つのコントロールが貼ってありますので多少コードが長くなっていますが、

  • DependencyProperty で、外側から設定できるプロパティを作る
  • YourName 等で、簡易アクセスができるプロパティを作る
  • LostFocus イベントで、内側から外の ViewModel に値を設定する

ということをやっています。ユーザーコントロールは3つのプロパティ

  • YourName
  • YourAddr
  • YourBirthday

を持ちます。

謎だったユーザーコントロール内の TextBox 等の入力コントロール(これ自体はバインド機能がなくてよい)から、外側の ViewModel にデータを渡すためには、

  • BindingOperations.GetBindingExpression でバインド先を探す
  • Target.SetValue で値を更新する
  • UpdateSourceでソース(バインド先のViewModel)に通知する

という手順になります。通知するタイミングは、サンプルの通り LostFocus イベントでもよいし、TextBox 内のテキストが変更されたタイミング、キータイプされたタイミングでも良いでしょう。

MainWindow から使う

image

メインウィンドウにユーザーコントロールといくつかのボタンを貼り付けます。ユーザーコントロールへのバインドは、独自に作ったプロパティにバインドさせます。

<local:PersonControl 
 YourName=&quot;{Binding Name}&quot; 
 YourAddr=&quot;{Binding Addr}&quot; 
 YourBirthday=&quot;{Binding Birthday}&quot; 
 Margin=&quot;4&quot; Grid.Row=&quot;1&quot; Height=&quot;99&quot; VerticalAlignment=&quot;Top&quot;/>

後はふつうの TextBox などと同じように MVVM でバインドするだけですね。これだとユーザーコントロールは一般的なコントロールと変わらないので、


public partial class MainWindow : Window
 {
     public MainWindow()
    {
         InitializeComponent();
         this.Loaded += (_, __) =>
         {
             _vm = new MainViewModel();
             this.DataContext = _vm;
         };
     }
     MainViewModel _vm;

    private void clickSave(object sender, RoutedEventArgs e)
     {
         _vm.Output = $&quot;{_vm.Name}  {_vm.Addr}  {_vm.Birthday.ToString()}&quot;; 
     }

    private void clickLoad(object sender, RoutedEventArgs e)
     {
         _vm.Name = &quot;MASDUA&quot;;
         _vm.Addr = &quot;OSAKA&quot;;
         _vm.Birthday = DateTime.Parse( &quot;2000/1/1&quot;);

    }
 }
 public class MainViewModel : ObservableObject
 {
     private string _name ;
     private string _addr;
     private DateTime? _birthday;
     private string _output;

    public string Name { get => _name; set => SetProperty(ref _name, value, nameof(Name)); }
     public string Addr { get => _addr; set => SetProperty(ref _addr, value, nameof(Addr)); }
     public DateTime? Birthday { get => _birthday; set => SetProperty(ref _birthday, value, nameof(Birthday)); }
     public string Output { get => _output; set => SetProperty(ref _output, value, nameof(Output)); }
 }

実行すると、こんな感じになります。

image

参考にしてみてください。

カテゴリー: C# | ユーザーコントロール内で更新した値を親の ViewModel に反映させる方法 はコメントを受け付けていません

正しい「PDCAサイクル」の知識を得るために

どうも巷で「PDCAを回す」ってのが相当誤解されているようなので、正しい知識としての「PDCAサイクル」の話を書き残しておきます。カッコつきなのは、正しい知識のほうに重きがあるので、PDCA サイクル自体が有用であるか否かはここでは論じません。というか、ノウハウってのは適材適所があるので、うまく開発プロセスの中(会社の独自文化もあるだろうし各々の状況もある)にはまり込めば「PDCA は使えるよ」って形になるけど、はまらなければ「PDCA は使えない」って形になるだけ。うまいところに使って正しい形で PDCA を活用してください(場合によっては活用しないでください)ってのが主旨です。

PDCA とは何か?

そもそも PDCA とは何か?という前提があるので(いろいろなパロディを面白がるためには元ネタの知識が必要なのだ)、念のため書いておきます。

  • Plan(計画)
  • Do(実行)
  • Check(計測、評価、検証)
  • Action(再計画)

ですね。おそらく、「Action」のところが曖昧になってしまい、PDCA 回すといいつつ、PDCPDCPDC になっているパターンが多いので、これは PDCA サイクルとは言いません。「サイクル」と言うからには、計画→実行→評価、をした後に、この評価や検証をもとにして計画を見直す(あるいは新しい計画を盛り込む)というアクションを取る、という意味での「Action」です。

この PDCA って言葉自体は、私が就職したころ(1980年代)から日本にあるので、結構前からあります。本家?アメリカではどういう扱いをしていたのか定かではないのですが、改善が「カイゼン」になる前からあったような気がします。

で、PDCA を「回す」という言い方をするので、この PDCA をくるくると回転させる(ときには高速に回転させると効率がよさそう?)という話もあるでしょうが、いえいえ、そういう意味での「回す」ではなくて、単純に循環しているという意味での「サイクル」です。なので、この Plan, Do, Check, Action という各プロセス(昔は、それぞれの工程のことを「プロセス」と呼びました。今だと、工程の流れや繋がりを「プロセス」と言いますね)を循環させてつなげていく、特に Action を入れることによって、評価されたものを次の計画に反映させるという「循環/サイクル」という意味で、「PDCA を回す」という言い方をします。

PDCA はイテレーション開発のことなのか?

No. 違います。「イテレーション開発」はできあがったものを顧客に見せて、次の開発のステップへ進むというスタイルで、開発プロセスそのものを示しています。PDCA サイクルは、評価から次の再計画へ、という小さなステップを踏む改善のサイクルでしかないので、開発プロセスとはイコールになりません。

具体的に PDCA の活用の仕方をみていきましょう。

  1. 計画を立てる。主にスケジュールや、何をやるとか、何を目標にするとかを決める。
  2. 実行する。計画に沿って実行する。実行するときは途中であれこれ変更しない。だって、計画に沿わないと計画自体の意味がない。
  3. 実行を計測する。どういう風にやったとか、どういうことをやったとか、どのくらいの時間がかかったとかを記録して、評価できるようにする。
  4. 記録をもとにして、次の計画に役立てる。記録は次に役立てるために記録しているのであって、次の計画に何にも関わらないのであれば、記録している時間が無駄。
  5. 最初の計画を、実行して記録した結果をもとに見直す、再計画する、修正する。またはそのままでよいと確認する。

というプロセスになります。なんで、ここに顧客の意見とか、アジャイル的に顧客を巻き込みながら実行時に意見を取り入れて、ということはしません。だって、計画からずれるじゃないですか?計画通りに実行しないと、計画自体が妥当であったかどうかが分からないので、きちんと計画通りに進めて途中で記録を取ります、そして計画通りに進んでいるかどうかをチェックして、次に役立てるわけです。ここで計画を見直します。実行して記録していたものを見たときに、無理な計画であったとか、ぞれは意味がない計画であったとか、無謀だったとかを「計画通りに実行する」ことによって確認するのです。

そう、ある意味では初期のスクラム開発に似ています。スプリントの期間を決めてしまい、最悪「スプリントの2週間が完全に無駄であってもよい」という考えのもとに開発を行います。これはスクラムマスターが外部からの雑音をメンバに伝えないための防護壁となるためです。同時に、開発者はスプリントの最初に建てた計画(この計画は2週間のうちに間違っていることがわかったとしても)の最大限の努力をもってして進めます。現在のスクラム開発はどうかよくわからないのですが、少なくとも初期のスクラム開発はこれでした。

PDCA で何がダメなのか?

PDCA サイクルの適用範囲外の部分を先に話しておきましょう。範囲外のところに適用しようと思っても無駄です。意味がないので、やめましょう、って話ですね。

  • 計画通りに実行しないと意味がありません。なので、計画がゆるゆるだと実行時に勝手に最適化するよう動いてしまいダメです。
  • なんらかの形で記録を取れないとだめです。勤務時間だとかステップ数の進捗度とかなんかの記録を取らないと、計画が無茶だったのか妥当だったのかがわかりません。
  • Action のところに重きがあるので「再計画」ができない計画に適用してはいけません。計画を見直す、計画の妥当性を見極めて改善するのが PDCA サイクルの主旨なのですが、そもそも最初の計画が動かない(短期スケジュールとか人を増やせないとか)という厳しい条件の場合には PDCA サイクルは意味を成しません。

なので、程よく計画プロセスと実行プロセスが分かれており、実行プロセスをチェックするする方法が確立して、それを踏まえたうえで次の再計画に意見できる、という組織でないと PDCA サイクルを使おうとしても無駄です。

PDCA の適用範囲は?

では、PDCA サイクルをうまく活用するためにはどういう条件が必要でしょうか?この「条件」を書いていない本や記事が多くて閉口するのですが、ノウハウというものは必ず適応条件というものが存在するのです。

  • 実行可能な計画を立てる。スケジュールや手順を作る。
  • スケジュールや手順に従って、実行してみる。途中で勝手にやり方を変えたりしない。
  • 実行には記録が伴う。コード量や開発スピードなどの記録を取る。
  • 記録をもとにして、計画が妥当であったのか、不具合があったのかを評価する。時にはよりよいアイデアを出して、より効率的に「実行」できる案を考える。
  • 再計画する。手順やスケジュール、タスクの組み方などが改善されている。

というのが PDCA サイクルの効率的な取り入れ方です。

PDCA サイクルは、そのサイクルが長いか短いかは関係ありません。問題になるのは Action から Plan へサイクルになっていることであって、PDCA を回すスピードは問題ではないのです。なので、PDCA サイクル自体がひと巡りするの1年以上かかるかもしれないし(例えば、決算や年度末への改善案を出せば、年1回しかプロセス改善機会がないですよね)、1日や1時間程度の単純なものかもしれません(筋力トレーニングの計画を立てて、それにそってトレーニングをやる。疲れすぎたり、楽すぎたりすれば、筋力トレーニングの計画を見直して新しいトレーニングをしますよね)。

これらを知っていれば、正しい PDCA サイクルの知識をもってして、取り入れるか取り入れないかの判断ができますよ、というお話でした。

カテゴリー: 開発 | 正しい「PDCAサイクル」の知識を得るために はコメントを受け付けていません

ジェラルド・M・ワインバーグ追悼

随分間が開いてしまったが、書かないとそのまま通り過ぎてしまいそうなので1時間ほど書き留めておこう。

「ライト、ついてますか」で有名なワインバーグではあるけれど、私にとっては「コンサルタントの道具箱」と「プログラミングの心理学」のワインバーグだったりする。一時期、彼の主催するコンサルタントの集まりに行きたいと思ったこともあったが、まあ、それは昔のことではある。

おそらく私がワインバーグ氏を知ってきちんと彼の本を読み込み始めたのは、ワインバーグの各著書がブームを過ぎた頃だと思っている。2000年頃に会社の受託開発での各プロセスに疑問を持った私は、なんらかの形で開発プロセスを改善したいと模索した。アジャイル開発のはしりもあり XP があり TDD もありオブジェクト指向もあり UML もありと様々な道具立てが出てきた頃でもあるのだが、私の興味はもっと人間の動きに近いところの「ピープルウェア」と心理学な分野であった。当時、すでにコンサルタントとして名を聞いていたワインバーグ氏のっ著書で最初に手に取ったのは「コンサルタントの道具箱」だったと思う(本棚には「スーパーエンジニアの道」や「ワインバーグのシステム変革法」などひととおり購入したので、どれが最初だったか覚えてはいない)。現在はコンサルタントではあるが、昔はプログラマだったというワインバーグに興味があった。ただし、あとで知ったところだが(プログラミングの心理学を読んだときだったが)、プログラマとして働いたのは1年半位の話で、そのあとはマネージャやコンサルタントとして働いているので「プログラマ」としてのワインバーグの意見はあまりあてにならない。時代も違うのだが。しかし、コンサルタントとしての視点は有効であろう、ということで「道具箱」なのである。

「道具箱」や「道具立て」というところが私にとって魅力的なのは、そういう暗喩を好んで使うところが好きだったからでもある。直接的なもの言いではなく(駄目とか良いとかではなく)、婉曲な言い回しはアメリカ人にしてはちょっと不思議な感じでもあったし、なんせよ「文学的」であった。言い回しを工夫することによって、相手が理解するように仕向けるという方法は「ライト、ついてますか」にたくさん書かれている。ちなみに、本のタイトルとなっている「ライト、ついてますか?」の意味だけが私にはかなりの間(5年間位?)わからなかった。これ「ライトをつけてください」とか「ライトをつけろ」という直接的な言い回しではなく、「ライトは、点灯しまいますよね?」という婉曲な確認の意味でなんだけど、もともと「ライト、ついてますか?」の婉曲な思考の持ち主だったので、これが婉曲な言い回しかどうかわからなかった、というオチだった。時にして、私の言うことがよくわからない、回りくどい、と言われることが多かったのだが、そもそもが婉曲に言うことに慣れていた(婉曲な言い回しを使うことによって、距離を置くという方法なのだけど)ので、それ自体に気づかなかったということでもある。自分の話なのに、自分のことではないような言い回しをする、ということだ。それは自分としては客観性を示しているつもりなんだけど、他人から見れば主体性のない言い逃れをしているように見えるらしい。

後から考え直したが、他人の婉曲な用法が分からないゆえに、自分独自の婉曲な用法とのずれのため、コミュニケーションが取れていない、という推測もできるし事実としてはそれっぽい。これは私自身では判断がつかない。

それはさておき、道具立てというのは、人の感情の起伏とは離れたところにある客観性を持つ。定規のようなものだ。物理的な計測というものが絶対的な価値を持つように(誰が測っても同じだし、時代が変わっても1mは1mだ)、目の前の出来事を一定の道具を使って比較をすれば、それは主観に引っ張られずに客観的にとらえられる。そして、客観的な事実をとらえたうえで主体的に判断すればよいのだ。2000年頃は絶対的な価値を求めて、TDD の正しさや一連の「ワインバーグのシステム何とか」を読み込んで目の前のプロジェクトからあれこれと読み取ろうとしていたものだが、今は違う。もっと緩やかなものだし、それぞれの多様な方法は多様な方法としてあるだけでいいと思う。だから、成功も失敗もその人あるいはグループあるいは会社に属するもので、誰かがとやかくと強制はできないものだ。だからこそ、誰からも強制される訳ではなく、ほどよく理論武装をしほどよくガードを保つ。正攻法で受けるのではなく、いなす。まあ、それこそが道具立てではあるので、そこは道具(冶具)の使いどころというところだろう。

「コンサルタントの道具箱」や「コンサルタントの秘密」には様々な道具立てが掛かれており、独自な語り口調で数々の「法則」が語られているのであるが、実のところは「孫子の兵法」や「風水」や「タロットカード」を知ることによって、ワインバーグ独自の「法則」をなめることをしなくてよいことが今の私はわかっている。これらのどれも「まんべんなく俯瞰して状況をみること」を示している。目の前の事実から何かを読み取ろうとするとき、人は何かを読み取ろうとするがゆえに、固定の事物に縛られがちになる。だから、ばらっと視野を広くして無理矢理にでも他の事象を合わせてみることが必要なのだ。進化論的に。このあたりのは話は、どんどんと突き詰めてしまうとあらぬ方向に行ってしまいそうなので、そこは「ワインバーグの道具箱」を引用させて貰うのがベターなところだろう。残念なことに「プログラミングの心理学」には、現在の心理学的な正しさ(主に実験心理学の統計的なアプローチ)は全くでてこない。正しいと思われるような「モデル」を組み立てたうえで、都合のよい事象に当てはめてみるという「似非心理学」に近いものあるのだが、それはそれで十分だ。彼の業績がそれによって薄められるということはない。以前、TED で話していたオリバー・サックス(すでに亡くなってしまったが)を見たときに「古さ」を感じたと同じものを、ワインバーグにも感じる。それはそれでよい。私たちは(少なくとも私は)、ワインバーグの語る心理学や数々の道具立てが古臭く見える程度には進歩してきたのだといえる。そして、ワインバーグの業績は、まさしくそれ自体を古くするための礎になっているといえる。だから、彼の言う心理学てなテストや数々の統計ちっくなものや気づきのポイントみたいなものは、そのまま現在に持ってくると古臭くて場合によっては間違っている。けれども、それらをアレンジして語ることはできる。本質を変えてしまって彼の言葉だけを借用してもよいし(ワインバーグを信奉する人ならばその方法は有用だ、若い人にはちんぷんかんぷんだろうが)、逆に本質だけよりわけて言葉を現代風にアレンジしてもよい。

「ワインバーグの道具箱」で私が好きな道具は「ジャムの法則」と「くすぐりの羽根」だ。サティアに信奉してたワンバーグは、実に前向きに物事を進めるのが好きだったようなのだが、ちょっとユーモアに頼りすぎる。でも、カート・ヴォネガット・ジュニアもユーモアが好きだったので、その程度は許するのが良いだろう。厳しい状況にあって悲壮な感じで努力をするよりも、楽観的に斜に構えて努力するが気は楽だ。怒号の中でひたすら平伏するよりも、正義の御旗を振りかざしながら反発するよりも、シニカルな目で客観視をして事実をとらえる方法が私には向いている。だから、ちょっとした想像力とユーモアが必要になる。オブラートに包むためにも。

ぼちぼち1時間になる。ワイバーグを中心にしたコンサルタント集団やカンファレンスはどうなったのだろう。そのあたりはあまり問題ではない。それこそが「イチゴジャムの法則」なのだから。

カテゴリー: 雑談 | ジェラルド・M・ワインバーグ追悼 はコメントを受け付けていません

Scratchから飛び出してSmalltalkを学ぼう(準備編)

先日書いた記事に返事を頂いて、そうか、Scratch 1.4 の中身は Smalltalk(Squeak)なのだからそっちで試してみることができるんだ。ということで、ちょっと Smalltalk を数日動かしてみる。

何処から手を付ければよいのか?

真面目にやるのであれば、教科書とかチュートリアルをやればいいんでしょうが、そもそも Smalltalk を今更まじめに見る気はないし(と思っておりましたよ)、オブジェクト指向のメッセージングのところだけ見ておきたいので、何かよい実例はないのかと思い探してみました。Google で初心者解説回りをさがしてもよいのですが、今だとアドベントカレンダーを見ていくとなんとなく実用がわかります。

Smalltalk Advent Calendar 2017 – Qiita
https://qiita.com/advent-calendar/2017/smalltalk

初心者なところと、難解で細かいいところと雑多な記事があつまっていますが、少なくと、

  • ほどよく、プログラム言語を使っている人が書いている
  • ほどよく、複数の人が書いている

ということで、アドベントカレンダーの各記事と各リンクはプログラム言語を学ぶときのとっかかりとして便利です。

テスト駆動開発でお試しする Pharo Smalltalk・第1回 はじめてのレッド、イエロー、グリーン – Qiita
https://qiita.com/sumim/items/fa41066c57d211814ff9

そんな中で、TDD を使った解説があったので試していきます。

Scratch 1.4 で Smalltalk を使う

ちなみに、Scratch 1.4 で Smalltalk の環境を開くには、以下の記事を参考にしてください。

SmalltalkからScratchをいじる(1) スプライトの大量生成 – Qiita
https://qiita.com/maeda_/items/7a525cae6b3c1ca45773

「画面右上のSCRATCHのロゴの”R”の部分をShiftキーを押しながらクリックするとメニューが開きます。」ってのがスタートです。

  • inspect morph がスクリプトを持つ変数表示と workspace/playground の表示
  • browse morph class でクラスのブラウジングの表示

image

このままでも色々できるはずなのですが、もうちょっときちんとして環境が欲しいので、Pharo ってのを入れます。

Pharo 6.1 を使う

Pharo  https://pharo.org/ ってのは、OSS な Smalltalk な環境で、Windows/macOS/Linux での動作環境が用意されています。先の TDD の解説も Pharo なので、これを使います。

Smalltalk は内部では VM で動いているので、Android シミュレータのようにイメージをダウンロードして実行します。で、最新の公開は 7.0 らしいのですが、stable なのは 6.1 のほうですね。7.0 と 6.1 はメニュー表示が微妙に違っていて、先の TDD の記事と違ってしまうので、6.1 のほうを入れます。

image

間違えて 7.0 のほうを入れて「メニューが違う」「リファクタリングがない」と思って苦労したのは内緒です。↓が 7.0 の画面

image

これが 6.1 の画面。ちょっと違う。

image

まず最初にやってことは、コードエディタの文字が小さいので大きくするところから、背景部分をクリックして「System」→「Setting」を開きます。

image

何処あるのかわからないので、Font で検索して、Default と Code の部分を変更します。大きめにしておくと老眼の私には優しくなります。

image

TDD のチュートリアルを進める

テスト駆動開発でお試しする Pharo Smalltalk・目次 – Qiita
https://qiita.com/sumim/items/3a36c03ab41519077b37

を地味に進めるわけですが、まったく Smalltalk の文法が分からないながらもいくつか面白い発見をしたので、以下、書き残しておきます。Smalltalk 自体は古い言語(メジャーなのは Smalltalk-80 位からだから、40年ぐらい前か)ので、そのあとに出てきた言語も含めてってことですが。

文末に「.」ピリオドを使う

C言語系では文の終わりに「;」セミコロンを使うわけですが(これが嫌われものであり、セミコロンあるなしで、関数言語とか Python とかに移る場合もあるわけで)、Smalltalk は「.」ピリオドで文章を終わらせます。なるほど、英文はピリオドで終わるから、Smalltalk もピリオドで終わるのですね(おそらく)。

image

関数の最後はピリオドが省略できるので、一文だけの関数はピリオドがあったりなかったりします。

戻り値を ^ で表す。

最初、この「^」はなんだ?と思ったのですが、return です。関数型言語だと、常に式を表すので関数の最後の行が価を返すことが決まっているのですが、Smalltalk の場合は、明示的に値を返します。

image

メソッドのパラメータを「:」コロンで区切る

Smalltalk でメソッド(関数)を呼び出すときに、括弧を書きません。引数の指定に「:」コロンを使うわけですが、なんか、この形式みたことがあるな?と思ったら Objective-C でした。

image

というか、Objective-C のルーツが Smalltalk であったことを今知りました。

  • Smalltalk の場合
    bank addXRate: 1 USD / 2 CHF .
  • Objective-C の場合
    [ bank addXRate: 1 USD / 2 CHF ] ;
  • C++ の場合
    bank.addXRate( 1 USD / 2 CHF ) ;

「1 USD / 2 CHF」は、まあ、ひとつの引数とみてください。Objective-C の場合、ひとつ目の引数の場合は名前(セレクタ)を省略することもできるのですが、ここではあえて使うパターンで。

2つのパラメータを持つときは、

  • Smalltalk の場合
    bank exchange: 10 CHF to: #USD .
  • Objective-C の場合
    [ bank exchange: 10 to: #USD ] ;
  • C++ の場合
    bank.exchange( 10, #USD );

こんな感じになりますね。C/C++ だと引数に名前がつかないので、型で判断するのでややこしくなるのですが、Smalltalk の場合は名前が使えます。この部分、C# では名前付きの記法もできるようになったので、似た形で書けます。

ちなみに self は自分自身のオブジェクトを示すので C++ の this と同じ。

  • self assert: ( bank exchange: 10 CHF  to: #USD ) equals: 5 USD
  • this->assert( bank.exchange( 10, #USD ), 5 USD );

この2つは同じ意味になります。

Smalltalk には型がない

実は Smalltalk には型がありません(と言い切っていいのだろうか?)。関数の後ろにある「|」パイプで囲まれたところが、内部変数の宣言です。えらい大雑把ですが、Smalltalk が VM で動いているからこれでいけるみたいです。

image

VB6 の Variant みたいな感じですかね。

  • Dim bank, result

と書くのと同じことになります。ちなみに、下記のように bank 変数に別の型を入れてもエラーになりません。あまり、やらないだろうけど。

image

既存クラスを拡張できる

たぶん、Smalltalk のオブジェクト指向プログラミングの肝になると思うのですが、Smalltalk では既存のクラスにメソッドを追加して拡張できます。Objective-C でいうカテゴリだったり、C# でいう拡張メソッドのような機能ですが、Smalltalk ではすでに定義されているクラスに対してメソッドが追加できます。

image

既存のクラスを弄ってしまうのだから、セキュリティやら隠蔽性やらはどこに行くの?という話なのですが、そこは Smalltalk が VM 上で動いている利点なのでしょう。動作環境が別々なのだから大丈夫…という思想だったのかもしれません。

ともかく、一番根っこになる Object クラスにもメソッドを追加できます。

image

Object クラスの testing のカテゴリ(これは分類なので、何処にあっても一緒(だと思う)には、isColor とかいうメソッドもあるわけで、所謂「神クラス」になります。Objective-C にもカテゴリ名を使って既存クラスを拡張する機能があってので、これと同じ機能を取り入れたものと思われます。

C++ がオブジェクト指向で GUI を作るときに継承を使い、そのあと深い継承が深くなりすぎて C# で委譲が使われることが多くなった、という経緯があるのですが、Smalltalk だと根っこに手を入れるという技があったのですね。まあ、このあたりは C++/Java/C# 等ではできないし、親クラスがいきなり変わってしまっても困るので(メソッド名の重複など)拡張という機能だけに留まるわけですが。

細かく保存し、細かくコンパイルする

Smalltalk では、ブロックという概念がない(らしい)ので、クラスのメソッド単位でコードを書いていきます。メソッドが細かく分かれるので、分かれた単位で保存&コンパイル(Accept)を繰り返します。これ、昔は相当重かったと思われるのですが、今の CPU ならば全然問題ありません。

image

記述するコードは十数行ぐらいなので、これで十分かもしれません。逆に言えば、ひとつのメソッドに数百行も書いてしまっていたら、Smalltalk としてはおかしいかもしれません。

if 文ってのは無い

これも Smalltalk の奇妙な文法だと思うのですが、Smalltalk には if 文がありません(たぶん)。じゃあ、条件分岐はどうなるのか?というと「ifTrue:」と「ifFalse:」というセレクタ(引数みたいなものか)を使います。

image

なので、C++ であれば

if ( a == true ) { b = 10 ; } else { b = 20 ; }

と書くところを、Smalltalk では、

a == true ifTrue: [b := 10] ifFalse: [b := 20] .

と書きます。不思議ですね。この書き方、実は利点があって、if 文(っぽいもの)が式として扱えるので、関数型言語っぽいことができます。

b := a == true ifTrue: 10 ifFalse: 20 .

な感じで、a が true/false かによって、10 か 20 を b に代入ができます。

~~~

そんな訳でひとまず、気づいたところはここまで。あともう少し TDD をぽちぽちと進めるつもり。

カテゴリー: 開発, Smalltalk | Scratchから飛び出してSmalltalkを学ぼう(準備編) はコメントを受け付けていません

System.Net.HttpListener が .NET Core の時だけメモリリークっぽい現象になる件の解決策

FireScratch を C# で作っていた時に発覚したメモリーリックっぽい現象に当たったので、珍しく issue https://github.com/dotnet/corefx/issues/32454 を立てました。

サーバーのほうで、.NET Core か .NET Framework を使って以下のような簡易 HTTP サーバーを作ります。

namespace CheckHttpListener
 {
     class Program
     {
         static void Main(string[] args)
         {
             Console.WriteLine("test server .net framework");
             var listener = new System.Net.HttpListener();
             listener.Prefixes.Add("http://127.0.0.1:5411/");
             listener.Start();
             while (true)
             {
                 var context = listener.GetContext();
                 var res = context.Response;
                 res.StatusCode = 200;
                 var sw = new System.IO.StreamWriter(res.OutputStream);
                 sw.Write(string.Format("text {0}", DateTime.Now.ToString()));
                 sw.Close();
             }
         }
     }
 }

これに対して、テスト用にアクセスするクライアントを作っておきます。

namespace CheckClient
{
    class Program
     {
         static void Main(string[] args)
         {
             HttpClient client = new HttpClient();
             int cnt = 0;
             while ( true )
             {
                 var res = client.GetAsync("http://localhost:5411").Result;
                 var text = res.Content.ReadAsStringAsync().Result;
                 Console.WriteLine($"{cnt} {text}");
                 cnt++;
                 System.Threading.Thread.Sleep(20);
             }
         }
     }
 }

これを動かしておくと、.NET Framework の時にはサーバーのメモリが程よい大きさで止まるのですが、.NET Core のほうは、がんがんとメモリを食い潰してしまって最後にサーバーが倒れます。だいたい1時間ぐらい放置しておくと落ちます。

image

同じコードで、.NET Framework と .NET Core の挙動が異なるので、.NET Core の HttpListener のバグか?とも思ったのですが、どうやら HttpListener.GetContext が HttpListnerContonet が持つ Response オブジェクトの挙動が異なるので、一概にバグとは言えないような感じです。

Response を明示的に Close するか using を使う

解決策としては、Response に対して明示的に Close メソッドを呼び出すか、using を使って暗黙に Response が閉じられるようにします。

var context = listener.GetContext();
var res = context.Response;
 res.StatusCode = 200;
var sw = new System.IO.StreamWriter(res.OutputStream);
 sw.Write(string.Format("text {0}", DateTime.Now.ToString()));
 sw.Close();
 res.Close(); // ★

あるいは、

var context = listener.GetContext();
using (var res = context.Response)  // ★
 {
     res.StatusCode = 200;
     using (var sw = new System.IO.StreamWriter(res.OutputStream))
     {
         sw.Write(string.Format("text {0}", DateTime.Now.ToString()));
     }
}

のように書きます。この Close 処理は .NET Framework でも有効なので、これで同じコードで .NET Core でも .NET Framework でもメモリリークが出ないようになります。

大抵のサンプルは HttpListener をたくさん回さないので、この問題にあたることはないのですが、実際に作ってサーバー化すると数時間後に落ちたりするので注意が必要ですね。これ、Windows + .NET Core だけの現象なのか、それとも Linux + .NET Core でも発生するのかはあとで調べておきます。

カテゴリー: C# | System.Net.HttpListener が .NET Core の時だけメモリリークっぽい現象になる件の解決策 はコメントを受け付けていません

いっせーのせで学ぶ Scratch のブロードキャストメッセージ

Scratch のメッセージングは、普通のオブジェクト指向言語と変わっていて(Scratch 自体はれっきとした「オブジェクト指向言語」、内部は Smalltalk だったわけだし)、メッセージをブロードキャストで投げます。ブロードキャストメッセージとは何か?そもそもブロードキャストってどういう動きをするのか?と言えば、

普通のメッセージは、送り手のクラスがあって、受け手のクラスがある。値を送る(参照でもいいけど)という1対1のイメージですね。いわゆる 受け手.method( 値 ) で、受け手のクラスに送るパターンです。

image

これがブロードキャストになると、1対多の関係になって、送り手.method( 値 ) のイメージになります。

image

この図だと、送り手が複数の受け手に対して送るので、1対1の関係を増やしたような感じになりますが、実際のところはメッセージをブロードキャストで送るってのは、中間的なクラスがあってブロードキャストの場合はそれぞれに送るというスタイルになります。

送り手からは1回のメッセージを送っただけで、複数の受け手にデータが流れるという感じですね。

image

これ、ちょうど UDP の配信と同じ仕組みで、双方向のコネクションをとらない UDP 通信ではブロードキャストが可能になります。TCP とは異なり一方的にデータを流します。

同じことは、映像配信にも言えて、いわゆる昔のテレビの電波だとか、地デジに配信も似た感じになります。ブロードキャストは一方向にしか流せないけど、非常に多い受け手に対して同時配信をするときの良い仕組みです。

物理的にブロードキャストを実現するならば、電気信号を分配してそのまま増幅させればよいし、光通信も光を分割して後から増幅すればいくらでも同じデータを増やせます。まあ、完全に同じではないことは量子的な解析になるわけですが、普通の範囲では分割してデータを流すことが可能です。

Scratch のブロードキャスト

Scratch には、メッセージを送るためのブロックがあって、

  • 受け手が、メッセージを受け取ったときのイベント処理
  • 送り手が、メッセージを送り、処理を待たない
  • 送り手が、メッセージを送り、処理を待つ

image

という3つのブロックがあります。「送って待つ」ブロックは、いわゆる処理を受け取るパターンになるのですが、実はこのメッセージは相手が複数あってもよい(ブロードキャストだから)、「送って待つ」というのは、ブロードキャストを送ったすべての相手の処理を待つという、all wait のパターンになります。なかなか複雑で、おもしろいですよね。

このメッセージングが複数持てるということは、メッセージ先の「受け手」はスレッドで動いているのでは?という想像ができます。おそらくScratch はラウンドロビン形式で動いているはずなので、メッセージの先(各スクリプト)が OS レベルでのスレッドで動いているとは思えませんが、少なくとも「スレッドもどき」で同時実行できるということになります。

マルチスレッドなので無限ループでブロッキングしない

猫弾幕3 https://scratch.mit.edu/projects/249352885/ のネズミのスクリプトを見るとわかるのですが、「旗をクリックされたとき」の処理が3つあります。これもそれぞれ同時実行しています。

image

image

左上のブロックで「ずっと」を使っていて無限ループになっているのは「怖い」感じがするのは C言語を想定するからで(実際、怖い感じがしますw)、Scratch の場合はこのような無限ループにしても適度に OS に処理を戻してくれるので問題ありません。ループはだいたい 20 msec 位で動いています。

猫弾幕3では、2つの変数(残りの数、秒数)同時に別々のループでチェックしています。ふつうにプログラミングをするならば、ひとつのループの中で2つの変数を2つの if 文を使ってチェックするところですが、Scratch の場合はこんな風にループ自体を分けてしまうことができます。2つのループが同時に実行されるために、2つの変数が同時にチェックされる、という仕組みですね。意外とこの仕組みは重宝します。

親指の数をどう勘定するか

さて、いっせーのせに戻ると、この上がっている親指の数をどうやって数えようか?と悩みます。

image

ひとつの方法としては、8つの右手と左手についてサムアップしているかどうかをひとつずつチェックします。

image

幸いにして、右手と左手はそれぞれ別のスプライトとなっているので、別々に勘定ができます。

image

まあ、これでもいいんですけどね。リストに入れて効率化してもいいですけど、さっきのブロードキャストメッセージを活用してみましょう。

「いっせーのせ」メッセージを送って待つことにします。ブロードキャストメッセージで送るので、メッセージが送る先の数は「送り手」にはわかりません。どこに送るかもわかりません。どれだけ多くの相手がいるかわからないけど、すべて相手が応答を返すまで待つのは確かなことです。

image

さて、受け取った右手や左手のスプライトは、「いっせーのせ」メッセージを受け取ったら、サムアップをしている(コスチューム番号が1になっている)場合は、合計を1だけ足して返します。これは、どの右手、左手のスプライトも同じです。

image

さて、「いっせーのせ」メッセージを送った送り手は、最終的に何を受け取るでしょうか?そう、サムアップしているが「合計」に入っているというわけです。ちょうど、ブロードキャストにメッセージを配信している形と同じになりますね。

image

この右手と左手のスプライトは、どの順番で実行されるかわかりません(実際は、スプライトの番号順になるだろうけど)。順番は問わないし、受け手となるスプライトの数も関係ありません。

例えば、人数を増やしたいと思ったとき先の「親指の数を数える」の定義では、if 文を追加していかないといけませんが、ブロードキャストメッセージを利用したパターンでは追加したスクリプトに「いっせーのせ」メッセージを受け取ったときの処理を追加しておくだけです。このあたりはオブジェクト指向のインターフェースという形になりますが、そのあたりも含めて Scratch は非常にオブジェクト指向言語として綺麗に動きます。

サンプル

UDPのブロードキャストはどうするのか?

参考までに、UDP通信のブロードキャストの例を晒しておきます。C#だとこんな感じ。ネットワーク内(ネットワークマスク内)にあるPCに一斉通信をすることができます。Wi-Fiは通らないので有線LANのみ有効な技ですが重宝します。

 public async Task<bool> Connect()
        {
            // ブロードキャスト送信
            var client = new UdpClient();
            client.EnableBroadcast = true;
            int n = await client.SendAsync(
                CMD_REQUEST_RESPONSE, CMD_REQUEST_RESPONSE.Length,
                new IPEndPoint(IPAddress.Parse(BROADCAST_ADDR), BROADCAST_PORT));
            var recv = await client.ReceiveAsync();
            _ep = recv.RemoteEndPoint;
            client.Dispose();

            return true;
        }
カテゴリー: Scratch | いっせーのせで学ぶ Scratch のブロードキャストメッセージ はコメントを受け付けていません

WPFで Web Speech API を使ってみよう!

クレウスさんの、

なところから、Web Speech API がブラウザ上で使えることが分かったので試してみるの巻です。

Web Speech APIの実装 – Speech Synthesis API | CodeGrid https://app.codegrid.net/entry/2016-web-speech-api-1

ブラウザ上で Web Speech API を使って文章の読み上げができる、って話は聞いてことはあったのですが、これ OS の機能をブラウザから呼び出している訳ではなくて、ブラウザの内部機能として実装されている、という話だったのですね。なので、Google Chrome で読み上げると Google の音声で喋るし、Edge で動かすと Microsoft の音声出力になります。おそらく Safari とかも違う音声になっているはず。

Javascript は簡単

読み上げするだけなら非常に簡単で、

var text = “hello world.”;
speechSynthesis.speak(new SpeechSynthesisUtterance(text));

と Javascript で書くだけです。text の部分は、文章を選択した場所でもよいし、何かを喋らせたい文章を書くもよし。声質も変えられるので、自前であれこれできます。

じゃあ、.NET から使ってみよう

ブラウザ上で音声を出せることは分かったけど、では、.NET からどう扱えばいいのか?ってことを考えると悩みどころなのですが、まあ、定番の WebBrowser か WebView を使えばなんとかなるでしょう?と考えました。

が、試してみると、意外と難関があることが判明。

  • WinForms や WPF で使われている WebBrowser は内部で IE を使っているので、Web Speech API の対象にならない。

なので、確か WPF などで Edge エンジンを使えるようになったような気がしたので、調べなおしてみると、

WPFやWindowsフォームでEdgeのWebViewを使うには?[Windows 10 1803以降]:.NET TIPS – @IT
http://www.atmarkit.co.jp/ait/articles/1807/04/news017.html

に、ありますね。

どうやら、NuGet で「Windows Community Toolkit v3.0」を落としてきて、その中にある WebView を使えばよいそうです。Microsoft.Toolkit.Win32.Controls を NuGet でダウンロードします。ちなみに、このコントロールは .NET Framework 4.6.2 以上なので、別途 .NET Framework をダウンロードする必要があります。開発者向けのをダウンロードしないといけないので、https://docs.microsoft.com/ja-jp/dotnet/framework/install/guide-for-developers から新しいものを入れてください。

image

ツールボックスに「WebView」コントロールが増えるので、これをウィンドウに貼り付けます。

image

読み上げるための TextBox と Button も貼り付けておきます。

image

グレーの WebView は、呼び出し用にあるだけなので見えなくても大丈夫なはず。大抵の例は、ブラウザ上で Web Speech API を動かすわけですが、ここでは WPF に貼り付けてある TextBox を読み上げるようにします。

 

InvokeScript で Javascript を実行する

ブラウザ内にある Javascript を実行するのに、WebView には InvokeScript というメソッドがあります。WebBrowser のときには、WebBrowser.Document.InvokeScript だったのですが、WebView では Document がなくなっていて、直接 InvokeScript メソッドが追加されていますね。Document がなくなったのでブラウザの DOM を直接見ることはできなくなったのですが、まあ、InvokeScript があればトリッキーなことをして DOM を探ることもできるので問題はありません。

以下、は動作コードですが、いつか試行錯誤の痕跡を残しておきます。

public partial class MainWindow : Window
 {
     public MainWindow()
     {
         InitializeComponent();
         this.Loaded += MainWindow_Loaded;
     }
     private void MainWindow_Loaded(object sender, RoutedEventArgs e)
     {
         // web.IsJavaScriptEnabled = true;
         // web.Navigate(&quot;http://moonmile.net/speech.html&quot;);
         // 非推奨だがローカルファイルから読み込む
        web.NavigateToLocal(&quot;speech.html&quot;);
     }

    private void clickGo(object sender, RoutedEventArgs e)
     {
         var text = @&quot;
<body>
<script>
function hello(text){
  speechSynthesis.speak(new SpeechSynthesisUtterance(text));
}
</script>
</body>
 &quot;;
         // 直接文字列を入れるとダメ
        // web.NavigateToString(text);
         this.web.InvokeScript(&quot;hello&quot;, new string[] { text1.Text } );
     }
 }

NavigateToString メソッドを使って、直接 HTML を入れることはできるのですが、なぜか InvokeScript メソッドを呼び出したときにアプリケーションごと落ちてしまいます。仕方がないので、Navigate メソッドで外部サイトから読み込むか、NavigateToLocal メソッドでローカルファイル(exeと同じフォルダ)から読み込みます。NavigateToLocal は非推奨ってことになっていますが、まあ、そのまま使えます。InvokeScript メソッドの呼び出しで落ちるのは、何かの初期化が足りていないんでしょう。

InvokeScript メソッドは、HTML 内にある Javascript の関数を直接呼び出せます。パラメータも渡すことができるので、これを TextBox から取り込みます。

ローカルファイルにある speech.html の内容は、以下のように hello 関数にひとつの引数を入れたものだけです。

<body>
<script>
function hello(text){
  speechSynthesis.speak(new SpeechSynthesisUtterance(text));
}
</script>
</body>

後は、ボタンを押して InvokeScript を呼び出せばふつうに Edge で喋ってくれます。

これは Unity で使えるのか?

さて、肝心のこの仕組みは Unity で使えるんでしょうか?って話なんですが、たぶん、ダメだと思うんですよね。UWP だと使えるんでしたっけ?

と思って、UWP で使ってみると…あっさりいけますね。UWP のほうはもともと内部的に Edge が使われているので大丈夫そうです。NavigateToString メソッドも普通に使えます。InvokeScript はダメで、InvokeScriptAsync だとうまく動きます。

public sealed partial class MainPage : Page
 {
     public MainPage()
     {
         this.InitializeComponent();
         this.Loaded += MainPage_Loaded;
     }

    private void MainPage_Loaded(object sender, RoutedEventArgs e)
     {
         // this.web.Navigate(new Uri( &quot;http://moonmile.net/speech.html&quot;));
         var text = @&quot;
<body>
<script>
function hello(text){
  speechSynthesis.speak(new SpeechSynthesisUtterance(text));
}
</script>
</body>
 &quot;;
         this.web.NavigateToString(text);
     }

    private async void clickGo(object sender, RoutedEventArgs e)
     {
         await web.InvokeScriptAsync(&quot;hello&quot;, new string[] { text1.Text });
     }
 }

ってことは、Unity でもいけるのかも。

~~~
追記 2018/10/01

Unity はやってないのでわからないのですが、Xamarin.Forms と Xamarin.Android の WebView で試してみました。結果は「ダメ」です。Android 上の Chrome では動くのですが、WebView の内部で使っている Chrome(Chroniumらしい)が対応していないらしく、AspeechSynthesis を認識できません。ちなみに音声を出すだけならば、TextToSpeech があるので、それを使えば ok. iOS の WebView の場合は AspeechSynthesis が使えるようです。

カテゴリー: C# | WPFで Web Speech API を使ってみよう! はコメントを受け付けていません

Firebaseを使い、Scratchで相互通信する

Google の Firebase には Realtime Database というものがあって、複数のアプリケーション(デスクトップPC、スマホ、M5Stack などなど)を使って同期ができます。って記事が、あちこちにあるので、ならば、Scratch でもできるのでは?と思って作ってみたのがこれ。

image

1画面にはなっていますが、デスクトップPCからノートPCにリモートデスクトップ表示しているところです。別のPCでスクラッチを起動しておいて(ひとつのPCでは複数起動できないので)、片方のオレンジ猫を動かすと、もうひとつのオレンジ猫が動きます。

直接通信させることもできるのですが、Google の Firebase を経由させます。

image

Scratch なところは、デスクトップアプリ(WPFとか)でもよいし、スマホアプリを Xamarin で作ることもできます。便利かどうか別として、まあ、こんなことができるということで。

Firebase の基本的なところは、

C#でFirebaseを使ってみよう!(1) FirebaseとEmail-Password認証 – こっちみないで(´・ω・`) http://kmycode.hatenablog.jp/entry/2017/02/09/205655

Firebase Realtime Database のデータ保存、取得、ストリーミング受信実験( ESP32 , M5Stack ) | mgo-tec電子工作
https://www.mgo-tec.com/blog-entry-firebase-realtime-database-sever-sent-events-esp32-m5stack.html

なところを参考にしています。Firebase は API KEY を取得してアクセスするのですが、そのままだと誰でもアクセスできてしまうので、一応ユーザー名(メールアドレス)とパスワードでガードを掛けます。

C# や F# から Firebase を扱うときは、NuGet で次の2つを追加します。

  • FirebaseAuthentication.net
  • FirebaseDatabase.net

内部で Rx が使われているらしく、System.Reactive や System.Reactive.Linq などが同時にインストールされます。

先の記事では、C# でサンプルが書かれているのですが、FireScratch の場合は都合上 F# で書いています。まあ、以前作った NetScrattino が F# だったので、それを踏襲したかっただけなんですけどね。

Firebase 自体は NoSql なので、JSON 形式でごっそりとデータを置きます。

image

こんな風に、/scratch/firecat というパス(フォルダーのようなものか)の下に、データが置かれます。謎な文字は識別子みたいなものですね。プロパティとして、From, To, Text, X, Y を置くためには、下記のようなクラスを作っておきます。

type Data() =
	let mutable _from : string = &quot;&quot;
	let mutable _to : string = &quot;&quot;
	let mutable _x : int = 0
	let mutable _y : int = 0
	let mutable _text = &quot;&quot;

	member x.From with get() = _from and set(v) = _from &<- v
	member x.To with get() = _to and set(v) = _to <- v
	member x.X with get() = _x and set(v) = _x <- v
	member x.Y with get() = _y and set(v) = _y <- v
	member x.Text with get() = _text and set(v) = _text <- v

C# だとこんな感じ

public class Data
{
	public string Text { get; set; }
	public string From { get; set; }
	public string To { get; set; }
	public int X { get; set; }
	public int Y { get; set; }
}

このデータクラスを、Firebase に対してアップロードします。F# で作る場合は、こんな風に、各種の関数を作っておくと便利です。詳しい中身は先の記事の C# コードを読んだほうがよいでしょう。

// ログイン
let singIn() =
     let auth = new FirebaseAuthProvider( new FirebaseConfig( apikey ))
     authLink <- auth.SignInWithEmailAndPasswordAsync( email, passwd ).Result
     printfn &quot;サインインに成功しました&quot;

// クエリを取得
let GetDatabaseQuery( path ) =
     let opt = new FirebaseOptions()
     opt.AuthTokenAsyncFactory <- fun () -> Task.FromResult( authLink.FirebaseToken )
     let client = new FirebaseClient( databaseURL, opt )
     client.Child( path )

// データをアップロード
let upload( data : Data ) =
     let query = GetDatabaseQuery( DatabasePath )
     query.PostAsync( data ) |> ignore
     ()
 // テキストを保存
let uploadText( text: string ) = upload( new Data( Text = text ))


 // テキストを取得
let downloadText() =
     let query = GetDatabaseQuery( DatabasePath )
     let results = query.OnceAsync<Data>().Result
     let items = results.Select( fun o -> o.Object )
     items.First().Text

// リアルタイムデータの監視
let startWatchingRealtime() =
     realtimeDatabaseWatcher <- 
         GetDatabaseQuery(DatabasePath)
             .AsObservable<Data>()
             .Subscribe( fun ev -> 
                 if ev <> null then
                     let text = ev.Object.Text
                     match ev.EventType with
                         | FirebaseEventType.InsertOrUpdate -> fireData <- ev.Object | FirebaseEventType.Delete -> ()
                         | _ -> ()
             )
     ()

これを、Scratch から呼び出せるように HTTP サーバーを作っておきます。スクラッチからは、/say/me/you/hello のようなスラッシュで区切られてデータが送られてくるので、これをパースして、Firebase に保存します。

もうひとつの PC では、リアルタイム監視(startWatchingRealtimeで登録)をしているので、これを fireData に保存しておいて、スクラッチのポーリング(/poll)に送られるようにします。

// Scratchから受信するためのHTTPサーバー
let Server( port ) =

    // firebase にログイン
     singIn()
     startWatchingRealtime()

    let listener = new System.Net.HttpListener()
     listener.Prefixes.Add(&quot;http://127.0.0.1:&quot;+(port |> string)+&quot;/&quot; )
     listener.Start()
     while true do
         let context = listener.GetContext()
         let res = context.Response
         let mutable data = &quot;&quot;
         let path = context.Request.Url.PathAndQuery
         match path with 
             | &quot;/poll&quot; -> 
                 data <- data + String.Format(&quot;text {0}\n&quot;, fireData.Text )
                 data <- data + String.Format(&quot;from {0}\n&quot;, fireData.From )
                 data <- data + String.Format(&quot;to {0}\n&quot;, fireData.To )
                 data <- data + String.Format(&quot;x {0}\n&quot;, fireData.X )
                 data <- data + String.Format(&quot;y {0}\n&quot;, fireData.Y ) // printfn &quot;%s&quot; path // printfn &quot;%s&quot; debug | &quot;/reset_all&quot; ->
                 printfn &quot;/reset_all&quot;
                 data <- &quot;ok&quot; | _ ->
                 let pa = path.Split([|'/'|])
                 match pa.[1] with   
                 | &quot;say&quot; ->
                     let me = pa.[2]
                     let you = pa.[3]
                     let text = pa.[4]
                     printfn &quot;say %s %s %s&quot; me you text
                     upload( Data( From = me, To = you, Text = text ))
                 | &quot;sayall&quot; ->
                     let text = pa.[2]
                     printfn &quot;sayall %s&quot; text
                     uploadText( text )
                 | &quot;movex&quot; ->
                     let me = pa.[2]
                     let x = pa.[3] |> int
                     printfn &quot;movex %s %d&quot; me x
                     upload( Data( From = me, X = x ))
                 | &quot;movey&quot; ->
                     let me = pa.[2]
                     let y = pa.[3] |> int
                     printfn &quot;movey %s %d&quot; me y
                     upload( Data( From = me, Y = y ))
                 | &quot;movexy&quot; ->
                     let me = pa.[2]
                     let x = pa.[3] |> int
                     let y = pa.[4] |> int
                     printfn &quot;movexy %s %d %d&quot; me x y 
                     upload( Data( From = me, X = x, Y = y ))
                 | _ ->
                     data <- &quot;&quot;
                 printfn &quot;%s&quot; path
         res.StatusCode <- 200
         let sw = new System.IO.StreamWriter( res.OutputStream )
         sw.Write( data )
         sw.Close()
     ()

スクラッチから送られてくるコマンド(say, sayall, movex など)は自分で定義をします。スクラッチのメニューでシフトキーを押しながら「ファイル」を選択すると「実験的なHTTP拡張の読み込み」が出てくるので、ここで作成した firescratch.json を読み込ませます。

image

{
   "extensionName": "Firebase Scratch",
   "extensionPort": 5411,
   "url": "https://github.com/moonmile/firescratch",
   "blockSpecs": [
     [ " ", "%s と言う", "sayall", "hello world." ],
     [ " ", "%s が %s さんへ %s と言う", "say", "me", "you", "hello" ],
     [ " ", "%s を X座標 %d にする", "movex", "me", 0 ],
     [ " ", "%s を Y座標 %d にする", "movey", "me", 0 ],
     [ " ", "%s を X座標 %d、 Y座標 %d にする", "movexy", "me", 0, 0 ],

    [ "-" ],
     [ "r", "自分", "from" ],
     [ "r", "相手", "to" ],
     [ "r", "X座標", "x" ],
     [ "r", "Y座標", "y" ],
     [ "r", "メッセージ", "text" ],
     [ "-" ]
   ],
   "menus": {
     "OnOffValues": ["ON", "OFF"]
   }
 }

サーバーを立ち上げて、コマンド待ち状態になると「その他」のところがグリーンになります。

image

これを2つのPCで起動させて、スクラッチ同士で通信させたのが、https://twitter.com/moonmile/status/1044440471823478784 にある動画になります。

相互通信にしたいところだけど、ひとまず一方向だけのスクリプトを

送信元のスクリプト

送信先のスクリプト

サンプルコード

https://github.com/moonmile/FireScratch

カテゴリー: Scratch | Firebaseを使い、Scratchで相互通信する はコメントを受け付けていません

夏休みの宿題の読書感想文を星新一ショートショートで書いてみるテスト

中学生もすなる読書感想文というものを星新一ショートショートで記してみるなり、ということで、奥さんに「書けるわけない」言われたので原稿用紙5枚(2000字)ぐらい書いてみるテスト。この位だったら30分位でさっと書けないとアカンよ。

絵本 星新一ショートショート | 星 新一
https://www.amazon.co.jp//dp/4048544519

~~~

星新一といえばショートショートSF作家として有名だが、ショートショート以外のものでは「人民は弱し 官吏は強し」という自伝っぽい本がある。星新一の父親が経営していた星製薬の話なのだが、色々官僚に虐められたという話だ。小学校の頃にSF文庫を読み、中学になってからはアシモフやらディックやら翻訳モノを読んでいた自分にとって、筒井康隆と星新一はちょっと特殊な日本のSF作家という存在で、真面目な印象を受ける星新一とおちゃらけた筒井康隆の文体は対象を為していた訳だが、実像のほうは星新一ほうが波乱な人で筒井康隆のほうが常識人というのが面白いところでもある。作家が内面にあるものを「小説」として表層化するときに、どのようにねじれていくのか(中二病的な言い方をすれば、「コンプレックス」なんだけど)の体現者二人という感じで見ていた。そんな中で、ショートショートという軽めであり、一見思想的なものを含まない星新一のもうひとつの顔として「人民は~」をよみ、再び星新一のショートショートに戻ると、ちょっと違った皮肉が文章の中に込められていることが分かる。同時代的には小松左京、横田順弥、半村良がいるのだけど(ええと、「幻魔大戦」の平井和正はちょっと特殊だし、「グインサーガ」の栗本薫も特殊ではあるので)、あまり作家像がみえないところに星新一のショートショートがある。そういう作家性のないところは、日経の星新一賞にAIをぶつけてみようというアイデアにも表れている。文体というものが作家性の大切な要素ではあるものの、ショートショートという中ではアイデア重視とそぎ落とされた文体の中で、要素だけが残された面白さに焦点が合わされているところにある。加えて言えば、星新一のショートショートの挿絵が一貫して和田誠だったところに奇妙な一致を見ることができる。短い文章は短歌や俳句のようなものではあるけれど、それよりも長く、しかし長編よりは短く、作家性を思わせるところが少ない判明、まさしく「星新一の描く」ショートショートである、と匂わせるような断然とした雰囲気が其処にはある。だから、実は、絵本になったショートショートは奇異ではありつつ、一見して半身を失った星新一ショートショートのような気もしないでもないのだが、いや、それはどうなのだろうか。まだ見知らぬ小説、まだ見たことのない挿絵に対して初めて面するとき、昔の星新一&和田誠ペアのショートショートを知らないのであれば、絵本となったショートショートは、実は本来の星新一のショートショートを真面目に眺める機会なのかもしれない。
実はクレイアニメのような図柄、そして短い物語の組み合わせは、数々の教育テレビ(Eテレ)の10分アニメを思わせる。ニョッキだとかポルタだとかそういうショートアニメにはちょっとしたストーリーが含まれていてちょっとした感動(子供向きだから?)が含められている。実は、これらのショートアニメは10年前以上に作れたものが多く、今のEテレのものを見ると解るけど、サイズが4対3になっている。新作が作られてないのか、それても旧作を発信することでコストを抑えているのかどうかは分からないが、これも初見であればそれが旧作であろうと新作であろうとあまり関係ない。始めてみる子供(大人でも)からすれば、それは興味を引く面白いお話である。それと同じく、絵本として再構成された(文章も再構成されているような気がする)星新一のショートショートは、大人向けのちょぴりとした皮肉(政治的な皮肉とかブラックジョーク)がある。禁じ手がいくつかあって、性行為と殺人と時事風俗は扱わなかったはずだが、「おせっかいな神々」には禁じ手を破っているらしい。ちょっと覚えていないけど。とりあえず、その手の良く分からないブラックジョークは絵本のほうでは省かれている。子供向けだし、内情が分からないとおもしろくないジョークを書き連ねてたって子供には面白くない。いや、大人でも面白くないのである。
だから、いろいろなバックグラウンドを知らなくても面白いSFショートショートが成立するというのがはなかなか珍しいことでもあり、うまいぐらいに3本用意したなとうならせるところがある。そう、ネタバレをすれば毛の生えたタコの話は、前知識がなくても奇妙で面白いし、ちょっぴり怖い感じがする。それは心理学的に怖いのか、ヒトが進化した中で必ず奇妙なものとして見えるのか、そこまでは突き詰めないけど、そういう作風が絵本の中からも出て来るのが星新一ショートショートの普遍性というものだろう。

~~~

以上、これで30分ちょっと。40字50行で2,000文字です。

カテゴリー: 書籍 | 夏休みの宿題の読書感想文を星新一ショートショートで書いてみるテスト はコメントを受け付けていません

Surface RT を引っ張り出して性能を確認してみる

Surface Go が流行りそうですが、手元にある Surface RT を引っ張り出して電源を入れてみる。

Surface RT を購入して初日の所感を | Moonmile Solutions Blog にある通り、2013年3月に購入。直前に初代 iPad を購入しているので、散財といえば散財なのですが、いわゆる「Windows ストアアプリを自前で入れられる」ってのがキーポイントです。今でこそ、Xamarin が使えるようになって iPhone/iPad アプリが簡単に作れるようになっていますが、当時は Objective-C で作らないといけないので、「私にとっては」結構な苦痛だったんですよね。まあ、その分、iPad アプリの商機というものがあった訳ですが…iPad 専用の絵本とかが流行った頃の話です。

中身は ARM なので、通常の Intel x86系の実行ファイルは動きません。しかし、arm でコンパイル&ビルドして実行ファイルを入れれば動作する…ハズなのですが、当時 Surface RT は「安全」のために arm の実行ファイルを直接入れて実行させることを嫌ったのです。脱獄すれば実行はできるものの、通常の状態で arm のバイナリが動かないのは、かなりマイナスでしたね。実際マイナスなので、Surface RT は Microsoft イベントにとっては暗黒扱いになっているようです。

先の記事にも書いてある通り、業務用に使うのであれば Intel CPU が積んである Surface Pro を買ったほうがいいんですよ。Surface RT は Office が付いているとはいえ完全に廉価版扱いで、iPad の値段よりも安かった。おそらく原価割れではないかと思うのですが、定かではありません。

Windows Update が走る?

確か、最後の Windows Update(Windows 8.1のアップデート)をして、電源を落としたはずなのですが、何故か更新プログラムを延々と構成し始めます。

30%を構成しましたの後、リセットがかかって、再び30%構成したあとにリセットが掛かる、という無限ループに陥ってしまったのか?という状態なのですが…なんとか、6時間後位に起動しました。一時は工場出荷にリセットなのか?とも思って回復 USB を用意したりしたのですが、まあなんとか。

Excel 2013 が動くよ

Surface Go でも話題になっていますが、Surface RT は Office 2013 がバンドルされています。これ、MS 社員も「Office がバンドルされているから安い」と言っていた覚えがあるのですが…まあ、タブレットで Office は使わないですよね。メールとかで貰ったときに便利なのかもしれませんが。

当時は、Office 365 は無かったし、Google のスプレッドシートが出たばっかりで動作も鈍かったので、arm のデスクトップ上に Office を載せたのはそれなりに意味があったのですが。これ、Excel VBA が使えなくてですね、キーボードがないと使えないし、やっぱり閲覧オンリーでしかないんですよ。

VLC で動画が見れるよ

動画(mp4)を見るのに、VLC を使うわけですが、当時はストアアプリ版というのがなくてですね。標準装備の「ビデオ」しか使えませんでした。標準のビデオのほうは、コーディックが少ないののと、ストリーミングに対応していないので、いまいちだったんですよね。

今はストアアプリ版(Windows 8.1にもあった)の VLC があるので、ストリーミングを含めて NAS 経由で動画を表示できます。ただし、Surface RT の頃の Wi-Fi が 2.4GHz のほうにしか対応していなくて、帯域が足りなくて少しブロックノイズが発生します。実写のほうはそうでもないのですが、アニメ絵は mp4 の圧縮に不利なのでブロックノイズが走りがちです。

自前の Windows 8.1 アプリを入れる

大抵の人は、Windows 8 から Windows 10 にバージョンアップしてしまったでしょうが、Surface RT は Windows 10 のラインナップから外されてしまって 8.1 までしか動きません。ちなみに、

うちの家PCは、奥さんの希望により 8.1 で止まっています。まあ、普通に使っている分には 8.1 も 10 も変わらないんですよ。むしろ、10 にアップデートするときに中にあるソフトウェアが動かなくなる&個人データが一部消される、というリスクがあって 8.1 のままです(8 から 8.1 のとき結構消されたのよ)。

Windows 10 の UWP アプリ(ストアアプリ)は、別途ウィンドウを開けるようになったので、あまり覚えている人はいないかもしれませんが、8.1 の頃はこんな風に全画面オンリーでした。でもって、分割表示するため真ん中にバーがでるんですよ。

これ、フラットデザインとして UI デザインとしてどうなの?って気もしないでもないのですが、改めて使ってみると「タブレットならば、この全画面が使いやすい」ですね。デスクトップ PC の場合、全画面を占領される(正確にはモニタ1枚)のは、ちょっと…な気もしますが、Surface RT のように比較的狭い画面ではせいぜい左右に2分割するしか余裕はありません。

こんな風に、右画面で動画を流しながら、左画面で Redmine の障害報告を読んだりできます。当時、ツイッターを流し見しながら仕事をしていた人も多いはずです(今でもそうだけど)。当時、iPad はシングルタスクで画面を表示していたので、この Surface RT の並列性は結構な優位点だったハズなのですが、活かされなかったですね。

因みに、Windows 8.1 のストアアプリは、Visual Studio 2015 でビルドができます。これを arm 版でビルドをして Surface RT で動かすわけですが、リモートデバッグツールを Surface RT に入れないといけないんですよ。なのに、最新である Visual Studio 2015  Update 3 のリモートデバッグツールは、x64とx86しかなくて arm 版がありません。たぶん、入れ忘れだと思うんですけどね。仕方がないので、無印の Visual Studio 2015 環境を使ってリモートで Surface RT にアプリを送り込んでいます。

Scratch はちょっと重たい

Surface RT 当時、Scratch がどの程度だったのかは覚えていないのですが、今 Scratch を動かしてみると結構重たい感じです。arm & デスクトップ側のアプリ導入禁止により、ブラウザでのみ Scratch が動きます。ストアアプリ版の IE(Edgeとは違う)の Javascript が不足するのでは?と思ったのですが、そういえば普通に Flash が使われているので、元気に動作しています。

ただし、キーが打てないのと、ブロックの動かし方がタップでは微妙なので、Scratch プログラミングを Surface RT 上でできるか?というと難しいですね。普通に、無線マウスと無線キーボードを付けたほうが利用しやすいでしょう。

当時、iPad から Flash が排除される、Android のブラウザでは艦これが動かない、ということで唯一?艦これが動くタブレット端末だったハズですが、どうだったかな。Surface RT で「艦これ」始めた – だるろぐ を見る限り、遅くて駄目だったらしい。

野球ゲームでは重たい Scratch ですが、マリオフラッピーではするすると動きます。なので、プログラムによるのでしょう。

5年前の機種なのに、これだけ元気に動くのは結構優秀な機種ですよね。初代 iPad は液晶が駄目になり、2台目を買い、その2台目もバッテリーが切れて、Apple で3台目に変えて貰っています(1万円で)。

ノートPC も Windows 98 やら Windows XP が元気に10年以上も動いていたりするので、結構 Windows 機種は長持ちするほうなんですよ。まあ、Linux 機の場合は、30年前以上から使っている vi や emacs が今でも動くので、ソフトウェア互換は抜群です。そういう意味では、Slackware から Linux を使っている世代としては、Ubuntu も大差なく使えます(ターミナルレベルではw)。じゃあ、mac はどうなのかというと、以前からある apple script を地道に継承していたりして、そういう姿勢は各種独特です。

そういうところを踏まえて、Surface Go がどの層を狙っているのかあるいは狙っていないのか、が興味深いところですね :)

カテゴリー: 雑談 | Surface RT を引っ張り出して性能を確認してみる はコメントを受け付けていません