20200929のC#に関する記事は4件です。

MacにてVisual Studio Codeを使ったC#構築手順

前の投稿にて、Vimエディタを使ってJava環境を構築しようとして、挫折した。
そのため、プログラミングでvimエディタは合わないのだろうと判断し、違うエディタを選ぶことにした。
IDEにしなかった理由は、少しで動作を軽くしたかったから(だからvimを選びたかった)。

んで、たかがエディタを使うだけであるにも関わらず、コンパイルから実行まで4〜5時間掛かってしまったため、備忘録として残すことにする。

インストールなど。

さすがに省略する。

Visual Studio for Macから取得すれば、そのまま使えたはず。
そして、CSharpの場合は、.Net Core 3.1 SDK 以降が必要になる。

起動

Visual Studio Code.appを叩けばいいだけなので、気にすることはない。

1.VisualStudioCode.jpg

ワークスペースフォルダを追加

IDEであれば、プロジェクト作成の表現を使うことだろう。

2.ワークスペースディレクトリ.jpg

"ようこそ"タブがない場合は、、、どうする?

3.ディレクトリ作成.jpg

ワークスペースに作成したフォルダが(作成したため)表示される。

脱線した話。

個人的には、フォルダーの長音記号は邪道だと思っている。そもそも命名規則違反だし、、、

その言葉が 3 音以上の場合には,語尾に長音符号を付けない。

別途参照:3音ルール-語尾の長音記号の使い分け-について。

Microsoft社が長音記号を付けることにしたのがな・・・。
今から2008年の出来事か。思った以上に昔だった。

ドットネット用の下地作成

".NET Core コンソール アプリ"プロジェクトを作成するため、コマンドプロンプト(Control+`記号で表示可能)に、 dotnet new consoleを打ち込む。

4.どっとねっと.jpg

実行したことで、必要なファイルが自動生成される。

C#をVisual Studio Codeに導入

上記のコマンドでC#ファイル(*.cs)も自動生成されているため、これを開く。

5.csファイル.jpg

そうした場合、C#用の拡張有無を聞かれるため、 "Yes" ボタンを押下する。
※一定時間後に、この確認ダイアログは消える。

試しに動かす

Visual Studio Codeに、C#用ソフトウェアが導入されたことを"アプリを拡張する"というそうだ。
これにより、C#ファイルをコンパイル及び実行できる。

ここでもまた、コマンドプロンプト上で、 dotnet run を実行する。

6.どっとねっとを走らせる.jpg

プロンプトに"Hello World!"が表示された。

デバッグ準備

冒頭で作成したqiitaSampleディレクトリ配下に、隠しディレクトリとして.vscodeがある。
この中に、launch.jsonファイルがあり、この中身を書き換えることで、デバッグ実行が可能になる。

7.虫.jpg

変更前:"console": "internalConsole",
変更後:"console": "integratedTerminal",

デバッグ

今回は、プロンプトからではなく、左側にある三角形と虫の絵が付いたボタンを押すことで、デバッグ実行になる。

8.虫潰し.jpg

プルダウンメニューには、".NET Core Launch (console)[ワークスペースフォルダ名(?)]"が選択されていることを確認すること

今回の場合のワークスペースフォルダ名は、"qiitaSample"になる。

デバッグ途中

今回は10行目にブレークポイントを設置した。

9.壊す.jpg

ブレークポイントは、行番号の左側をクリックすることで設置できる。

ステップ実行など。

あとは各々好きにデバッグをすれば良い。

10.虫とのステップ.jpg

処理を最後まで走らせたのが以下画像になる。

11.bash.jpg

以上。

一応手順をまとめたが、本当にこんな手順に躓いたとは思えない。
そのため、環境をいじっていたときに、正しい環境になってしまい、躓いた箇所を再現できずに、スムースに終わってしまったと思われる。
無事に環境構築が出来たのはいいのだが、ちょっと腑に落ちない。
無念ではあるが、結果オーライと言うことでよかった。

■公式ページ
チュートリアル: Visual Studio Code を使用して .NET Core コンソール アプリケーションを作成する
チュートリアル: Visual Studio Code を使用して .NET Core コンソール アプリケーションをデバッグする

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Open棟梁_テンプレートベースのサンプルを実行(DB接続編)

前回からの続きです。

この記事を確認する前に、
以下の記事からご覧ください。

< https://qiita.com/KOtamagokake/items/710c84b3876aaabb9caa >

目的

前回、ログインまで完了しました。
今回の目的は、DBと接続して、格納されているデータを
webページで確認することです。

前提

Open棟梁

DB:postgres
※それ以外は前回の記事を参照

手順

1.postgresの導入
以下のサイト様を参考に、導入。
< https://www.dbonline.jp/postgresql/install/index1.html >

以下は、このあと使用するのでメモしておいてください。
・ホスト名
・DB名
・ユーザID
・パスワード

2.postgresにテーブルを作成
ダウンロードしたopen棟梁には、サンプル用のデータを作成するための
sqlが書かれたファイルが入っています。今回はそれを使用したいと思います。
以下のファイルを開き、postgresにテーブルを作成、データを追加。

< "C:\root\files\resource\Sql\pstgrs\TestTable.txt" >

3.configファイルの修正

下記のconfigファイルを、1.でメモしたpostgresの情報に合わせて修正します。

configファイル.jpg

< "C:\root\programs\CS\Samples\WebApp_sample\WebForms_Sample\WebForms_Sample\Web.config" >

4.unsafeをインストール
①visial studioのソリューションエクスプローラーにて、「参照」の上で右クリックし、「NuGetパッケージの管理」をクリック
②「unsafe」で検索し、インストール

手順は以上となります。

試しにDBからデータを取得

1.プロジェクトを実行し、以下の画面を表示。てきとーにユーザIDとパスワードを入力し、「ログイン」ボタンを押下。(デフォルトだと認証機能なしなのでてきとーで)
01.jpg

2.「ノーマル」を選択

02.jpg

3.以下の画像のように、プルダウンから「Postgre」を選択

03.jpg

4.画面下部にある「一覧取得(dt)」を押下すると、DB内のデータが表示される。
(画像の赤枠内)

05.jpg

おわり

データ取得以外にも、他のボタンを押下すれば、CRUD操作を行えます。
次回は気が向いたら書きたいと思いますー

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ガチャシステムを二分探索で作る

概要

以下のような確率分布があるとして、これに従ってランダムに要素を1つ決定したい。

確率分布.png

実装のソースコードは記事の最後に記載する。運用先としてUnity等が真っ先に思い付いたため、C#での実装とした。
この機能に名前があるのか知らないが、近年ソーシャルゲームで流行しているガチャに似ているため、ガチャシステムと呼ぶ。

考え方

入力として、$N$ 個の要素からなる確率分布の数列 $A_1,A_2,⋯,A_N$ を与える。

double[] odds = { 0.5, 0.3, 0.15, 0.05 };

確率分布 – 1.png

まず、$[0, \sum_{i=1}^{N} A_i]$ の範囲でランダムな実数 $r$ を1つ決定する。

Random rnd = new Random();
double r = rnd.NextDouble();

確率分布 – 2.png

次に、$\sum_{i=1}^{k} A_i \leq r < \sum_{i=1}^{k+1} A_i$ となる整数 $k$ を求める。
簡単に思いつく方法は線形探索だが、今回は高速化を試みて二分探索で実装してみる。 ※二分探索の説明は割愛する。
なお、確率分布の部分和を計算しなければいけないため、実装全体としては $O(N)$ である。$O(\log N)$ ではない。

とりあえず関数のシグネチャを定義。
引数として、先ほどの確率分布 $A_1,A_2,⋯,A_N$ 、そしてランダムに決定した値 $r \; (0 \leq r \leq \sum_{i=1}^{N} A_i)$ を取る。
戻り値は $r$ に対応するインデックス $k$ にすればよい。

public int BinarySearchInOdds(double[] odds, double r) {
    // oddsの確率分布から、rの値に対応するインデックスを返す
}

この部分の実装は、一般的な二分探索とあまり変わらないため説明を割愛する。
考え方としては以上のような形になる。

実装

BinarySearchInOdds() メソッドが、先ほど省略した「確率分布から、rの値に対応するインデックスを返す機能」を実装されているものである。
Draw() メソッドに確率分布を渡すことでお手軽にガチャを引くことができる。
仕様が分かるように Main() を書いたので、実行結果も含めてぜひ見ていってほしい。

using System;
using System.Collections.Generic;
using System.Linq;

public class Gacha {

    // 確率分布の中で、 rに対応する要素のインデックスを返す
    public static int BinarySearchInOdds(double[] odds, double r) {
        if (r < 0) return -1;
        if (odds.Length == 0) return -1;

        // 確率分布の部分和をあらかじめ求めておく
        double[] sum_from_left = new double[odds.Length];
        double[] sum_from_right = new double[odds.Length];
        double odd_total = odds.Sum();
        sum_from_left[0] = 0;
        sum_from_right[0] = odd_total;
        for (int i = 1; i < odds.Length; i++) {
            sum_from_left[i] = sum_from_left[i-1] + odds[i-1];
            sum_from_right[i] = sum_from_right[i-1] - odds[i-1];
        }

        int left = 0;
        int right = odds.Length - 1;
        int mid = left + (right - left) / 2;
        double L_mid = sum_from_left[mid];

        while (right >= left) {
            double R_mid = L_mid + odds[mid];
            if ((L_mid <= r && r < R_mid) || (r == R_mid && mid == odds.Length - 1)) {
                // キーがmid番目の要素の範囲内にある場合
                return mid;
            } else if (r < L_mid) {
                // キーがmid番目の要素の範囲より小さい範囲にある場合
                right = mid - 1;
                mid = left + (right - left) / 2;
                R_mid = L_mid - (odd_total - sum_from_left[mid + 1] - sum_from_right[right + 1]);
                L_mid = R_mid - odds[mid];
            } else {
                // キーがmid番目の要素の範囲より大きい範囲にある場合
                left = mid + 1;
                mid = left + (right - left) / 2;
                if (mid >= odds.Length) break;
                L_mid = R_mid + (odd_total - sum_from_left[left] - sum_from_right[mid]);
                R_mid = L_mid + odds[mid];
            }
        }
        return -1;
    }

    // 確率分布にしたがって、ランダムに決定した要素のインデックスを返す
    public static int Draw(double[] odds) {
        Random rnd = new Random();
        double r = rnd.NextDouble() * odds.Sum();
        return BinarySearchInOdds(odds, r);
    }

    public static void Main() {
        double[] odds1 = { 0.5, 3.0, 0, 2.0, 1.5, 0.1, 0.9 };
        Console.WriteLine("odds1 = [" + string.Join(", ", odds1) + "]");
        Console.WriteLine("r = 0.0  --->  " + BinarySearchInOdds(odds1, 0.0));
        Console.WriteLine("r = 0.1  --->  " + BinarySearchInOdds(odds1, 0.1));
        // rがちょうど境目にある時、大きいほうのインデックスが返ってくる
        Console.WriteLine("r = 0.5  --->  " + BinarySearchInOdds(odds1, 0.5));
        Console.WriteLine("r = 2.0  --->  " + BinarySearchInOdds(odds1, 2.0));
        Console.WriteLine("r = 4.0  --->  " + BinarySearchInOdds(odds1, 4.0));
        Console.WriteLine("r = 6.0  --->  " + BinarySearchInOdds(odds1, 6.0));
        Console.WriteLine("r = 7.08  --->  " + BinarySearchInOdds(odds1, 7.08));
        Console.WriteLine("r = 7.7  --->  " + BinarySearchInOdds(odds1, 7.7));
        // rが確率分布の合計と等しい場合、最後の要素のインデックスが返ってくる
        Console.WriteLine("r = 8.0  --->  " + BinarySearchInOdds(odds1, 8.0));
        // rが範囲外の場合、-1が返ってくる
        Console.WriteLine("r = 10.0  --->  " + BinarySearchInOdds(odds1, 10.0));
        // rが負の場合も同様
        Console.WriteLine("r = -1.0  --->  " + BinarySearchInOdds(odds1, -1.0));
        Console.WriteLine("");

        double[] odds2 = { 1.0 };
        Console.WriteLine("odds2 = [" + string.Join(", ", odds2) + "]");
        // 確率分布の要素が1つでも問題ない
        Console.WriteLine("r = 0.0  --->  " + BinarySearchInOdds(odds2, 0.0));
        Console.WriteLine("r = 0.5  --->  " + BinarySearchInOdds(odds2, 0.5));
        Console.WriteLine("r = 1.0  --->  " + BinarySearchInOdds(odds2, 1.0));
        Console.WriteLine("");

        double[] odds3 = {};
        // 確率分布の要素がない場合、-1が返ってくる
        Console.WriteLine("odds3 = [" + string.Join(", ", odds3) + "]");
        Console.WriteLine("r = 0.0  --->  " + BinarySearchInOdds(odds3, 0.0));
        Console.WriteLine("");

        double[] odds4 = { 0.5, 0.35, 0.15, 0.05 };
        Console.WriteLine("odds4 = [" + string.Join(", ", odds4) + "]");
        // 100万回ガチャを引いて、排出確率を調べてみる
        int[] receivedCounts = new int[4];
        for (int i = 0; i < 1000000; i++) {
            int received = Draw(odds4);
            receivedCounts[received]++;
        }
        for (int i = 0; i < 4; i++) {
            Console.WriteLine("要素" + i + "  " + receivedCounts[i] + " / 1000000  ( " + receivedCounts[i] / 10000 + " %)");
        }
    }

}

実行結果

odds1 = [0.5, 3, 0, 2, 1.5, 0.1, 0.9]
r = 0.0  --->  0
r = 0.1  --->  0
r = 0.5  --->  1
r = 2.0  --->  1
r = 4.0  --->  3
r = 6.0  --->  4
r = 7.08  --->  5
r = 7.7  --->  6
r = 8.0  --->  6
r = 10.0  --->  -1
r = -1.0  --->  -1

odds2 = [1]
r = 0.0  --->  0
r = 0.5  --->  0
r = 1.0  --->  0

odds3 = []
r = 0.0  --->  -1

odds4 = [0.5, 0.35, 0.15, 0.05]
要素0  456796 / 1000000  ( 45 %)
要素1  333225 / 1000000  ( 33 %)
要素2  144646 / 1000000  ( 14 %)
要素3  65333 / 1000000  ( 6 %)

実行速度も計測した。

N = 10^2  ->  2 ms
N = 10^3  ->  2 ms
N = 10^4  ->  2 ms
N = 10^5  ->  4 ms
N = 10^6  ->  18 ms
N = 10^7  ->  173 ms
N = 10^8  ->  1683 ms

$N=10^5$ 程度までは処理時間のオーダーがほぼ変わらない模様。二分探索による高速化が多少活きた...かな?

これでいつでもガチャ引き放題。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

.NET 5 から Windows Runtime API を呼ぶのが凄い楽になってる

.NET 5 Preview 8 かららしいのですが、Windows 10 の API に .NET 5 からアクセスするのが凄く楽になってました。言及のある記事は以下になります。

どれくらい簡単になったかというと、今回のこの記事がすぐ終ってしまうレベルで簡単です。すぐ終わると悲しいので、ちょっとだけ昔と比べてどれくらい簡単なのか?というのも書いておこうと思います。

Windows 10 SDK の特定のフォルダにある DLL や winmd ファイルを手動で参照追加したうえで、配布時に含まれてほしくないファイルはコピーされないように手動で設定する。
そんなに数は多くないのですがめんどくさかったです。

Microsoft.Windows.SDK.Contracts という名前の NuGet パッケージが追加されているので、それを参照することで呼べるようになります。まじ天国。

.NET 5 以降

Target Framework Moniker を設定するだけでよくなります。具体的にはプロジェクトファイルの TargetFramework タグに net5.0-windows10.0.17763.0 のように Windows であることと対象のバージョン番号をつけるだけで良くなります。

やってみましょう。

VS 2019 Preview を起動して WPF プロジェクトをサクッと作ります。

プロジェクトファイルの TargetFramework を net5.0-windows から net5.0-windows10.0.19041.0 にします。以下のような感じですね。

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <OutputType>WinExe</OutputType>
    <TargetFramework>net5.0-windows10.0.19041.0</TargetFramework>
    <UseWPF>true</UseWPF>
  </PropertyGroup>
</Project>

英語のブログのほうにもあるカメラからの写真撮影をやってみましょう。

MainWindow.xaml
// Windows 名前空間で始まるクラスが使える!?
using Windows.Media.Capture;
using Windows.Media.MediaProperties;
using System;
using System.Windows;
using System.Windows.Media.Imaging;
using System.IO;

namespace WpfApp8
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        private async void Button_Click(object sender, RoutedEventArgs e)
        {
            // カメラから画像取り込んで表示
            using var c = new MediaCapture();
            await c.InitializeAsync();

            var format = ImageEncodingProperties.CreatePng();
            using var s = new MemoryStream();
            using var randomAccessStream = s.AsRandomAccessStream();
            await c.CapturePhotoToStreamAsync(format, randomAccessStream);
            await randomAccessStream.FlushAsync();
            s.Position = 0;
            var source = new BitmapImage();
            source.BeginInit();
            source.CacheOption = BitmapCacheOption.OnLoad;
            source.StreamSource = s;
            source.EndInit();

            image.Source = source;
        }
    }
}

動かしてみるとちゃんと動きます。(仮想カメラの画像を取り込んでる感じです)

cap.gif

昔 WPF でカメラ映像を取り込むのが凄く大変だった記憶があるので、こういう OS に紐づく API として提供されているものがさくっと呼べるのは心強いですね。

まとめ

ということで、NuGet パッケージを追加するだけというのでも簡単だったのですが .NET 5 から、さらに拍車をかけて簡単に Windows 10 の API が呼べるようになりました。便利~~~。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む