小学生の自由研究に最適…かもしれない中央卸売市場の卸売数量をMySQLで扱う

築地市場から豊洲市場への移転に関して、諸々を調べていた時にちょうどよさそうな大量データをみつけたので、紹介がてら。

中央卸売市場の卸売数量は公開されている

いわゆる築地とか豊洲とかの卸売市場は都が経営をしていて、もろもろの情報は以下で公開されています。

東京都中央卸売市場日報
http://www.shijou-nippo.metro.tokyo.jp/

2004年から記録があるので10年以上のデータを取得できます。が、あまり多くても仕方がないのでひとまず5年間分(2013年1月から2018年11月)までの青果と水産のデータをとってきました。

ページを探っていくと、こんな形でテーブルで表示されています。これをCSV形式でダウンロードができます。

CSV形式とはいえ、こんな風にテーブルからとってきたような形のCSV形式なので(一応カンマ区切りにはなっている)ちょっと扱いにくいのです。

公開されているデータ整形する

このままではデータとして扱いづらいので、データベースに挿入できるように本当のCSV形式に直してしまいます。

open System
open System.IO

// 1行目: 販売結果(青果・全市場) → 棟
// 2行目: 平成30年08月01日(水曜日) → 日付
// "(単位:キロ)" で → 分類
// "品名..." で → 市場
// 以降、1行ごとに 品物, 販売方法, 卸売数量(市場ごと)
// ただし、小計、全分類合計は取り込まない

// 出力フォーマット
// 日付, 棟, 分類, 市場, 品名, 販売方法, 卸売数量

let rec readtable (sr:System.IO.StreamReader) 棟 (日付:DateTime) 分類 (市場:string[]) 品名_ =
    let 数量 = sr.ReadLine().Split(",")
    let 品名 = if 数量.[0] <> &quot;&quot; then 数量.[0] else 品名_
    if 数量.[0] <> &quot;小計&quot; then
        for i in [4..市場.Length-1] do
            let 市場名 = 市場.[i]
            let 卸売数量 = if 数量.[i] = &quot;−&quot; then &quot;0&quot; else 数量.[i]
            let 販売方法 = 数量.[2]
            printfn &quot;%s&quot; (
                String.Format(&quot;{0},{1},{2},{3},{4},{5},{6}&quot;, 
                    日付.ToString(&quot;yyyy/MM/dd&quot;), 
                    棟,
                    分類,
                    市場名,
                    品名,
                    販売方法,
                    卸売数量  ))
        readtable sr 棟 日付 分類 市場 品名
    ()

let readblock (sr:System.IO.StreamReader) 棟 日付 =
    let 分類 = sr.ReadLine().Replace(&quot;(単位:キロ)&quot;,&quot;&quot;)
    let 市場 = sr.ReadLine().Split(&quot;,&quot;)
    if 分類 <> &quot;全分類合計&quot; then
        readtable sr 棟 日付 分類 市場 &quot;&quot;
    ()

let readcsv (sr:System.IO.StreamReader) =

    let 棟 = sr.ReadLine().Replace(&quot;販売結果(&quot;,&quot;&quot;).Replace(&quot;・全市場)&quot;,&quot;&quot;)
    let ci = new System.Globalization.CultureInfo(&quot;ja-JP&quot;)
    let 日付 = DateTime.Parse( 
                sr.ReadLine().Substring(0,11), 
                new System.Globalization.CultureInfo(&quot;ja-JP&quot;), 
                System.Globalization.DateTimeStyles.AssumeLocal)
    sr.ReadLine() |> ignore // 空行
    while sr.EndOfStream = false do
        readblock sr 棟 日付
        // 空行まで読み捨て
        while sr.EndOfStream = false && sr.ReadLine() <> &quot;&quot; do
            () 
    ()

[<EntryPoint>]
let main argv =
    if argv.Length = 0 then
        printfn &quot;ex. tukizi sui_20181105.csv&quot;
    else
        // printfn &quot;%s&quot; argv.[0]
        for fname in argv do
            let sr = new System.IO.StreamReader( fname )
            readcsv sr
            sr.Close()
    0

コードがF#になっているのはいつものことですね。この手の整形をやるのはC#よりもF#で書いたほうがやりやすかったりします。一番内側の readtable 関数内で再帰を使って品名の部分を読み込んでいます。もっと頑張れば for ループを無くせるはずなのでしょうが、ひとまずこれ動いたのでokということで。

何ができるのか?

データ量はざっと200万件近くあります。200万件あると検索が重たくなって大変なのでは?と思っていたのですが、MySQL に突っ込めば全然平気ですね。ひとつのテーブルしか扱わないというのもあるのですが(面倒なので敢えて正規化していません)、一千万件ぐらいあると検索に時間が掛かるのかもしれませんが、200万件位だったら sum を使って集計しても全然あっという間に終わります。

実は200万件の実データを集めるのは結構大変なのです。調査するにしてもデータベースの勉強をするにしても、これだけのデータを扱えるのは結構貴重ではないかな?と思うのです。

5年間分のデータは https://1drv.ms/u/s!AmXmBbuizQkXgpUYeqUVYd7WhAl4uw からダウンロードしてください。MySQLに入れてからエクスポートしたデータと、先のツールで成形したCSV形式のデータが入っています。

テーブル構造以下の通りです。面倒だったので列名は日本語のままw

CREATE TABLE `tt` (
  `日付` datetime NOT NULL,
  `棟` varchar(45) NOT NULL,
  `分類` varchar(45) NOT NULL,
  `市場` varchar(45) NOT NULL,
  `品名` varchar(45) NOT NULL,
  `販売方法` varchar(45) NOT NULL,
  `卸売数量` double NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

去年のさんまの卸売数量を調べてみよう

例えば、去年1年間の「さんま」の取扱量をみてみましょう。

select 日付, sum(卸売数量) from TT 
where 品名 = 'さんま'
  and 日付 between '2017/01/01' and '2017/12/31'
group by 日付
order by 日付
;

こんなクエリを書いて日付でソートすれば、日単位の卸売数量がわかります。

結果をExcelに貼り付けてグラフを追加すれば、ほら自由研究のできあがり。

ちなみにフォントは「全児童フォント(フェルトペン)」 http://tanukifont.com/zenjido/ を使っています。お試しあれ。

カテゴリー: 開発 | 小学生の自由研究に最適…かもしれない中央卸売市場の卸売数量をMySQLで扱う はコメントを受け付けていません

プロジェクトマネジメントに科学的根拠を付加する仮説

PMBOK に科学的根拠がない、というのは「知識体系」として網羅はしているけれど、そこに数学的な根拠とか確率の話がでてこないからだ。何をもってして「プロジェクトの成功」を決めるのかは実はそれぞれなんだけど、プロジェクトの最初に QCD(機能、コスト、納期)を決めておいて、

  • プロジェクト終了時に要件や機能を満たしている
  • 予算/コストをオーバーしていない。
  • 納期が守られている

ということをプロジェクトの「成功」と仮定してよいだろう。実際のプロジェクトは時間とともに変化することが多いから、プロジェクトの最初に決めた「要件」がプロジェクト終了時に満たされていたとしても、時間の差からニーズにあっているかどうかは分からない。これは建築業界でもあることで、計画時に綿密に設計をしたとしても建築物自体ができあがるのはタイムラグがあるから出来上がったときには時代のニーズから外れている(あるいは流行おくれになる)というパターンも多い。公共物を建てるときはどうしてもその矛盾に立ち向かわないといけないし、ニーズ自体が進行中に代わっていくことに追随するためのアジャイル開発手法もあれば計画に沿うかたちで進める計画駆動もあれば(特に建築物は途中で変更が効かないことが多い)それは業界や最終的な利用者(ステークホルダー)に合わせて変化するしかない。

プロジェクト完成時に顧客のニーズを完全に解決することはできない

極端な話、プロジェクトというのは時間の概念から離れることはできないので、計画時の利用者ニーズと完成時の利用者ニーズは変わる可能性がある。車のように購入してすぐに使う、既製服のように吊るしを買う、という場合には買ってから使うまでのタイムラグが短いから差はあまりでないのだけれど(それでも、使ってみたら「思ってたのと違った」パターンがあるのだけど)、プロジェクトの場合は一回性のものでもあるから、ぴったりとはいかない。逆にいえば、「完全に解決する」ことは不可能なので、「おおむね解決できる」という矛盾に立ち向かわないといけない。

プロジェクト開始時の要件や機能(プロジェクト途中の変更も含めて)の規定は、顧客との契約と同時に、作る側としては開発規模を確認/確保するために必須な作業であったりする。が、契約という行為がプロジェクト開始前にある以上ずれが必ずでる。

  • 顧客視点からすれば、プロジェクト開始前に契約して予算が分かることが望ましい
  • 開発視点でいえば、プロジェクト開始時(要件、機能の定義前)に規模はわからない

という話もあり、先のプロジェクトの QCD を決めようとしても矛盾だらけになる。それでも何とかするのがプロジェクトマネジメントの役目だったりする。逆にえば、この「なんとかする知識体系」が PMBOK の根底にはある。

極端な話をすれば、プロジェクトの予算や納期は、プロジェクト終了時点で明確になる。

image

イメージを描くとこんな感じになる。プロジェクト開始時には予算/コストは、上下大幅なずれがあり分布している、当然のことながらプロジェクト終了時には、コストは確定しており納期も確定している。

この上下のブレは、もしプロジェクトが複数あったらという確率の話であるが、実際はプロジェクトは一回こっきりなので、この予算や納期をどうやって予測するのか?というのが PMBOK のプロジェクト計画になる

プロジェクトの予算や納期は分布している

個人にとっては1回こっきりのプロジェクトなのだが、会社にとって複数のプロジェクトが動いていれば、赤字もあり黒字もある、納期遅れもあれ納期より早いものもある。コストが増えたものあれば予算内でおさまったものもある。つまり、複数のプロジェクトでみれば、コストと納期は分布していると言える。

image

プロジェクトが納期/コスト通りに終わる確率は、正規分布なのかカイ二乗分布なのか色々憶測があると思うが「分布」があることが変わりない。少なくとも目標値の前後に広がりがある。

実際のところ納期よりも前にプロジェクトが終わることは稀(これには理由がある)なので、納期遅れが頻発するので適当な指標が必要になるが、大雑把いえば、次の図のように「本来の納期」から遅れが生じたとしても8割の確率で納期遅れが発生しない時点を、プロジェクトの開始時に「納期」として提示すればよい。

image

この部分を80%ラインに置くのか、95%ラインに置くのかは色々だが。納期遅れがシビアな場合は95%が望ましいし、会社の赤黒だけを考えれば8割方プロジェクトが成功するならばまずまずの成績と言える。

この「本来の納期」と80%ラインである顧客に提示する「納期」との差は「保険」と呼ばれ、プロジェクト計画時のマージンとなる。よく言われる「計画時の1.5倍すると大丈夫」というのがそれになる。この確率の話は、CCPM には書いてあるんだけど、PMBOK には出てこない。逆に言えば、どんなに WBS をしっかり作ったとしても納期やコストは分布(ひろがり)を持つので、納期ぴったりに終わる確率は極めて少なく、納期前に終わる確率は50%となる。

じゃあ、なんでプロジェクトは納期通りに終わるのか?というと、納品日に合わせて人が努力をしているからだ、と言える。遅れ気味になったら努力をして残業をして納品日にあわせようとするし、逆に早すぎる場合は待ちの時間を入れて納品日にあわせようとする(早めに納品したりしない)。極端な話、努力の余地がないサイコロを振って出た目だけ進むプロジェクト(実際に CCPM の解説として使われる)の場合は、納品日が前後する。当たり前だ。調節の余地がないからだ。

なので、プロジェクトは実際のところコストや納期に関しては分布を持つので、ぴったりとした値にはならない。というのが、科学的な根拠である。

プロジェクトの QCD はどのように分布するのか?

Q(要件や機能)は分布するのか?というと、まあアジャイル開発の例もあるし、先に書いた時間とともに顧客のニーズ自体も変化するので、実は QCD そのものが分布し、広がりを持つ。

さて、ここの80%成功ラインだが、最初のグラフを参照すると「プロジェクト終了時」には納期が決定している(納品しているから当たり前だ)。当然、コストも一意に決まる。

image

これを確率のグラフと重ね合わせると、時間とともに青線の分布が、オレンジ色の線の分布と変わってくる。80%ラインも時間とともに中央に寄って来る。つまり「本来の納期」に収束するように動く。

これが何を意味するかと言うと、プロジェクトが進行する(時間が経つ)に従って、未確定なものが確定となるので不確定部分は減り、分布が狭くなることを意味している。当たり前といえば当たり前。前に進めば進んだだけゴールに近づいている、ということを示している(なんだかよくわからない例えかもしれないが)。プロジェクト開始時点では未確定であったものが、プロジェクトを進めることにより確定に代わるので、分布が絞れるという形になる。

これを逆に発想すると、プロジェクト開始時に未確定な部分に対して「仮に確定事項」を与えてやれば、分布は絞られる、という話になる。そう、事後分布の話と同じだ。PMBOK とか品質システムで言えば、事前にリスクを抽出しておいてそのリスクが発生したときいどうなるのか?対処はどうするのか?ということを考えておくことにあたる。

そこで、もう少し話を進めると、この未確定事項(リスク管理でもいいし QCD の各項目でもよい)が決定したとしてシミュレーションをすれば、プロジェクトの成功確率を逆算できるのではないか?というが仮説である。また、プロジェクトが進行するうちに未確定事項やリスク項目が減ってきたら、それを確定として再びシミュレーションすれば、プロジェクトの成功率を再計算することが可能になるだろう。

このあたりの発想は、もともと CCPM にあるのだけど、ベイズの定理をあてはめて後はシミュレーションによって求めようというのが自説。実際、プロジェクトシミュレーションの話は、ワインバーグ氏やデマルコ氏のシステムダイナミクスの話にも出てくるし、TOC でも出てくるので、古くからある考え方だと言えるだろう。この続きは、また後日。

カテゴリー: 開発, OpenCCPM | プロジェクトマネジメントに科学的根拠を付加する仮説 はコメントを受け付けていません

PHP7 上に WordPress を入れたときは php-xmlrpc を有効にする

タイトル通りですが、半日ほどハマったのでメモ書き

WordPress から XML-RPC を使って投稿するとエラーになる

以前、作っていた WpPost https://github.com/moonmile/WpPost を markdown に対応しようかと思って、新しく建てた openccpm.com につなげようとすると、エラーになりました。このブログ moonmile.net/blog は XML-RPC で投稿できているので大丈夫なんですが、なぜか新しく立てたほうにはできない。

試しに Open Live Writer を使ってつなげようとするも、アカウント作成のときや URL を指定するときに「parse error. not well formed」と出て「-32700」なエラーコードが返されます。どうやら、送っている XML のフォーマットがダメということなんですが、どういう風にダメなのか分からず。

試しに WordPress の英語版を入れたりバージョンを変えたりしてみたのですが、XML-RPC 経由で投稿できない、なぜ?最近の WordPress は XML-RPC が初期値で有効になっているはずなのに。

PHP7 の初期値では XML-RPC が有効になっていない

WordPress 側のコード(class-IXR-message.php や class-IXR-server.php)にログを入れつつ確認していったところ原因が分かりました。

apache2 + PHP7 を入れているときに、PHP 拡張の XML-RPC が初期値では有効になっていません。このために、IXR_Message::parse() でエラーになっているらしいですね。xml_parser_create が呼び出せないようです。

openccpm.com は Ubuntu 上で動いているので、

sudo apt-get install php-xml php-xmlrpc

でインストールした後に、/etc/php/7.0/apache2/php.ini を開いて

extension=php_xmlrpc.dll

のように XML-RPC を有効にします。

すると、以下のように mt.supportedMethods で有効なメソッド一覧などが取れるようになります。

カテゴリー: 開発 | PHP7 上に WordPress を入れたときは php-xmlrpc を有効にする はコメントを受け付けていません

ユーザーコントロール内で更新した値を親の 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(
             &quot;YourName&quot;,
             typeof(string),
             typeof(PersonControl),
             new FrameworkPropertyMetadata(
                 &quot;&quot;,
                 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(
             &quot;YourAddr&quot;,
             typeof(string),
             typeof(PersonControl),
             new FrameworkPropertyMetadata(
                 &quot;&quot;,
                 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(
             &quot;YourBirthday&quot;,
             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 を使ってみよう! はコメントを受け付けていません