プログラマーの必要性

こんにちはmtjです。

現代の小さくて安いAIカメラ等を見て思いますが 最近はプログラムレスで面白い機能があるサービス、商品が大量にあります。
その商品自体はプログラムで組まれておりますが 使う側はプログラムがほとんど必要ありません。

昔に比べてプログラムですべて作るより、プログラムで使える面白いサービス、商品にアンテナを貼り
それらを上手に使用する能力のほうが現代では必要ではないかと思います。

特にクラウドサービス類は1から作るとしたらとんでもない労力になります
作り方を知っている、想像できるまではあってもよいのですがそれらシステムを実際に作る必要がある人は一部の人でしょう。
大半の人はそれらを上手に使用する方法を覚えたほうが物になります。

今後の展示会等でもそういった物を覚えていけたら良いなと思いました。

Strategyパターン

たまの休日に一部の復習も兼ねて、”Java言語で学ぶデザインパターン入門”を亀のようなペースで読み進めています。
個人的に一番好きなパターンはStrategyパターンです。
このパターンは、インタフェースを定義してアルゴリズムを交換可能にするもので、
典型的な使いどころには、ゲームにおけるプレイヤーAI(CPUなどとも呼ばれますね)の思考ルーチンが挙げられます。
例えば、囲碁や将棋などの対戦ゲームにおいて、戦略の異なるAIをこのパターンで実装します。
以下のような形ですね。


public interface IPlayerStrategy {
    // 入力されたゲーム状態を元に行動を決定
    PlayerAction DetermineAction(GameState state);
}

public class AggressiveStrategy : IPlayerStrategy {
    public PlayerAction DetermineAction(GameState state){
        // 攻撃的な戦略に基づいて行動決定
    }
}

public class DefensiveStrategy : IPlayerStrategy {
    public PlayerAction DetermineAction(GameState state){
        // 防御的な戦略に基づいて行動決定
    }
}

public Player {
    private readonly IPlayerStrategy _strategy;

    public Player(IPlayerStrategy strategy){
        _strategy = strategy;
    }

    public PlayerAction GetNextAction(GameState state){
        return _strategy.DetermineAction(state)
    }
}

名が体を表す良いパターンだと思います。
個人の趣味ですが、典型例がゲームなのも良いですね。

実務経験はほぼないので、憶測になってしまうのですが、
実務であれば例えば、あるクラスにおいて、インスタンス生成時に決定される同一のフラグによって挙動を切り替える処理が何度も登場するような場合に、
Strategyパターンを適用できるのではないかと思っています。
インタフェースを切り、フラグのオンオフの挙動をそれぞれその具象クラスに切り出すことで、
外部のクラスには影響を及ぼさずに、フラグ毎の処理の見通しを良くできるような気がしています。

経験を積んで、パターンの使いどころを見極められるようになりたいです。

C# のコーディング規則

C# のコーディング規則
Microsoft公式ドキュメントに「C# のコーディング規則」が追加されました。
2021/5/14 の新しい内容です。

この記事のガイドラインは、サンプルおよびドキュメントを開発するために Microsoft によって使用されます。これらは、.NET ランタイムの C# コーディング スタイル ガイドラインから採用されました。それらを使用することも、ニーズに合わせて調整することもできます。 主な目的は、プロジェクト、チーム、組織、または会社のソース コード内での一貫性と読みやすさです。

とあるように、Microsoftでも使用される内容なので、
コーディング規則はこれをベースにするのが良さそうです。
フィールドはキャメルケースに_プレフィクスを付ける事が明文化されています。

  • private または internal のフィールドに名前を付ける場合は、キャメルケースを使用し、_を使用してプレフィックスを付けます。
  • ステートメント補完をサポートする IDE でこれらの名前付け規則に従う C# コードを編集するときは、「_」と入力すると、オブジェクト スコープのすべてのメンバーが表示されます。
  • なので自動実装ではないプロパティを使用する場合、
    バッキングフィールド名はプロパティ名の先頭を小文字にして_プレフィックスを付けたものになります。

    あと気になった点は、次のような点でしょうか。
    コメントは文章で書くようにという事ですね。

  • private または internal である static フィールドを使用する場合は、s_プレフィックスを使用し、スレッド静的には t_を使用します。
  • コメントのテキストはピリオドで終了します。
  • もちろん最初に書いてあるように、この規則はニーズに合わせて調整することもできますが、
    基本の規則として知っておくと良いと思います。

    PLINQ(Parallel LINQ)の実行速度

    .netのPLINQ(Parallel LINQ)による並列実行の効果がどれほどなのか理解していなかったのでテストしました。

    Select、Where、AverageをLINQとPLINQで実行し、速度を確認します。

    
    var items = Enumerable.Range(1, 100_000_000).ToArray();
    
    var sw1 = Stopwatch.StartNew();
    var results1 = items.Select(x => Math.Pow(x, 0.5)).ToArray();
    Debug.Print($"Select LINQ {sw1.Elapsed}");
    
    var sw2 = Stopwatch.StartNew();
    var results2 = items.AsParallel().Select(x => Math.Pow(x, 0.5)).ToArray();
    Debug.Print($"Select PLINQ {sw2.Elapsed}");
    
    var sw3 = Stopwatch.StartNew();
    var results3 = items.Where(x => Math.Pow(x, 0.5) >= 100).ToArray();
    Debug.Print($"Where LINQ {sw3.Elapsed}");
    
    var sw4 = Stopwatch.StartNew();
    var results4 = items.AsParallel().Where(x => Math.Pow(x, 0.5) >= 100).ToArray();
    Debug.Print($"Where PLINQ {sw4.Elapsed}");
    
    var sw5 = Stopwatch.StartNew();
    var results5 = items.Average(x => Math.Pow(x, 0.5));
    Debug.Print($"Average LINQ {sw5.Elapsed}");
    
    var sw6 = Stopwatch.StartNew();
    var results6 = items.AsParallel().Average(x => Math.Pow(x, 0.5));
    Debug.Print($"Average PLINQ {sw6.Elapsed}");
    

    結果

    
    Select LINQ 00:00:04.9831283
    Select PLINQ 00:00:01.1474103
    
    Where LINQ 00:00:04.7858871
    Where PLINQ 00:00:00.7447430
    
    Average LINQ 00:00:03.8039131
    Average PLINQ 00:00:00.3454058
    

    今回のテストプログラムの場合、4倍~11倍の速度改善になっています。
    .AsParallel()を追加するだけで、これだけの速度改善になるので、処理が遅くて困った場合にはまずPLINQを試すのが良さそうです。

    リモート環境の変化

    こんにちは、mtjです。

    最近リモートでライブ映像がリアルタイムで見れる等色々なものが遠隔でおこなるようになっています。
    ネット環境の進化もありますが一番はクラウド環境でしょう。

    昔であればストリーミングであれば想定人数に耐えれるだけのサーバーを用意してそれ用にネットワークを構築して等ハード面で様々な準備が必要でした。
    現在はボタン1つで1日もせずにサーバーを増やせます。
    足りなくなったら即増やし、不要になったら即破棄できサービスの運用もそういう意味では簡単に作って、破棄というのができるようになり便利になったような気がします。
    今後はそのようなサービスに対してフットワークが軽い人が人気になるのではないのかと感じました
    便利なサービスが出た場合に提案、対応しさらにクラウド環境を便利にしていく そういう時代になっていくのかなと感じました。

    using declaration

    先月研修でC#の勉強をしていたのですが、C#8.0からusingステートメントが、
    以下のようにローカル変数宣言の先頭に追加する形で行えるようになっていたことを知りました。
    (using declarationと呼ぶそうです)

    // after 8.0
    using var sw = new StreamWriter("hoge.txt");

    // before 8.0
    using (var sw = new StreamWriter("hoge.txt"))
    {
    }

    8.0以前はその後の処理を記述する際に中括弧やインデントが必要だったのですが、
    この形式だとネストを減らすことができてありがたいです。

    ただ、Disposeの呼び出しがスコープの末尾なため、
    記述の長いメソッドで利用するとリソースの解放が遅くなり、
    場合によっては支障が出るのではないかと思ったのですが、
    そもそもメソッドの長さが適切であれば問題ないことに気付きました。

    メソッドの長さは適切に、ネストは浅く、シンプルなコードを書くよう心掛けたいです。

    PlantUML を試してみました。

    5月の連休を利用して、UML図の作成にPlantUMLを試してみました。
    VSCode + 拡張機能の流行りの組み合わせです。
    普段は別の専用ソフトを使っているので、
    テキストベースで図を作成するのは、個人的には初めてです。

    練習に社内の研修用ソフトのシーケンス図を作成してみると、
    予想以上に簡単に作成することができました。
    テキストベースなので要素の配置などレイアウトを考える必要もなく、
    シンプルに書きたい事を書くことができる印象です。

    ほんの些細な事でも考える事が少なくなると、
    やりたい事がよりはっきり見えてくるのを実感しました。
    プログラムのソースコードを書くときでも、
    スコープは狭くする、ネストを深くしない、メソッドは小さく分ける…など、
    余計な事を考える余地が少なくなるよう工夫したいと思います。

    モータ制御のエミュレート

    インフォテックの案件ではサーボモータなどをPCで制御することが良くあります。
    今作成しているソフトもモータ制御を行うのですが、モータそのものやPCのモーションボードが無くてもソフトの動作検証が行えるように、エミュレート機能を社内ライブラリに組み込んであります。

    今回はエミュレート機能をさらに強化し、移動速度に合わせてパルス出力数がカウントされたり、パルス出力状態や停止要因が正しく変化するようにしました。
    この改造により、今までより細かく社内での動作検証が行えるようになりました。

    下は3軸の原点復帰動作のエミュレートです。実機無しで状態が変化しています。

    .Net Dictionaryの速度

    先日大量のデータを処理集計するために Dictionaryに格納していたのですが
    10万件を超えたあたりで急に速度が落ちることがありました。

    Dictionaryのキーを任意作成のStructにしておりましたが 別にテストで作成したものでは1万、10万、100万はDictionaryの個数に比例して増加しておりました。
    (1万件が80msecならば 10万件は800msecのような状態)

    今回のデータ処理用に作成したものは10000件で100msec程度だったので 30万で3秒前後だと考えておりましたが実際には7時間でした。
    遅い原因を調べるために.Netのソースを確認し結果的にはStructのHashCodeが重複しやすい物の場合は今回の様な結果になるのではないかと思いました。

    DictionaryのContainKeyの内部で使用している部分

            public bool TryGetValue(TKey key, out TValue value)
            {
                if (key == null) throw new ArgumentNullException("key");
     
                int bucketNo, lockNoUnused;
     
                // We must capture the m_buckets field in a local variable. It is set to a new table on each table resize.
                Tables tables = m_tables;
                IEqualityComparer comparer = tables.m_comparer;
                GetBucketAndLockNo(comparer.GetHashCode(key), out bucketNo, out lockNoUnused, tables.m_buckets.Length, tables.m_locks.Length);
     
                // We can get away w/out a lock here.
                // The Volatile.Read ensures that the load of the fields of 'n' doesn't move before the load from buckets[i].
                Node n = Volatile.Read(ref tables.m_buckets[bucketNo]);
     
                while (n != null)
                {
                    if (comparer.Equals(n.m_key, key))
                    {
                        value = n.m_value;
                        return true;
                    }
                    n = n.m_next;
                }
     
                value = default(TValue);
                return false;
            }
    

    GetBucketAndLockNoはこの通り

            private void GetBucketAndLockNo(
                    int hashcode, out int bucketNo, out int lockNo, int bucketCount, int lockCount)
            {
                bucketNo = (hashcode & 0x7fffffff) % bucketCount;
                lockNo = bucketNo % lockCount;
     
                Assert(bucketNo >= 0 && bucketNo = 0 && lockNo < lockCount);
            }
    

    上記Nodeの取り出しのバケットNoがhashcodeを使用しているのでカスタムのKeyを使用する場合はHashCode十分に分散するか考えて作成していかないといけないのかもしれません。
    遅い原因はチェック中なのでHashCodeの件は間違っているかもしれません進展があれば再び書きたいと思います。

    ちなみにコードはこちらで確認できます。

    読みやすいコード

    コードは理解しやすくなければならない。
    「リーダブルコード」にもそう書かれています。

    他の人が読みやすいかどうかというのはとても重要な事だと思います。
    適切な変数名やメソッド名は理解を助けますし、
    説明変数なども積極的に利用するようにしています。
    書き方ひとつとっても、
    例えば変数を宣言して anotherClass のみ遅延した初期化が必要な場合、

    var myClass = new MyClass();
    var yourClass = new YourClass();
    AnotherClass anotherClass;

    と書くよりは、

    var myClass = new MyClass();
    var yourClass = new YourClass();
    var anotherClass = default(AnotherClass);

    と書く方が、それぞれの変数の位置が揃っていて、
    読みやすいのではないでしょうか。
    気取らず、気の利いた美しいコードを目指したいと思います。