fc2ブログ
ヽ|∵|ゝ(Fantom) の 開発blog? ホーム »C#
カテゴリー「C#」の記事一覧

【C#】配列やListなどの中身(要素)を見る拡張メソッド Dump  


 今回はデバッグに便利な拡張メソッドを作ってみよう。

 C# では静的配列も List などのコレクション系も IEnumerable という interface が実装されている。

 このインターフェイスは、簡単な理解ではイテレータ(ループなどで順次要素を取り出す)機能みたいなものだが、お陰で1つの拡張メソッドを定義しておけば、簡単に型違いの配列や List などを扱える便利なものでもある(Java でも Iterator というものがあるが、プリミティブ型には使えないため、少し不便)。

 またこの IEnumerableLINQ と組み合わせると簡潔に書けることも多いため、覚えておくと色々便利だ。

 ここでは簡単だが、利用頻度も高い拡張メソッドを定義しておこう。



●配列や List (Collections) などの中身(要素)を見る Dump
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;

public static class Extensions //※名前は任意
{
/// <summary>
/// コレクションのダンプ
/// 2021/01/11 Fantom
/// http://fantom1x.blog130.fc2.com/blog-entry-391.html
/// </summary>
public static string Dump<T>(this IEnumerable<T> source)
{
return (source == null) ? "[]"
: ("[" + string.Join(", ", source.Select(e => e.ToString()).ToArray()) + "]");
}

/// <summary>
/// コレクションのダンプ(string.Format 付き)
/// 2021/01/11 Fantom
/// http://fantom1x.blog130.fc2.com/blog-entry-391.html
/// </summary>
public static string Dump<T>(this IEnumerable<T> source, string format)
{
return (source == null) ? "[]"
: ("[" + string.Join(", ", source.Select(e => string.Format(format, e)).ToArray()) + "]");
}
}

●メインコード例
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;

var nums = Enumerable.Range(0, 20).ToArray(); //int[]
var list = new List<string>()
{
"hoge", "fuga", "piyo"
};

Console.WriteLine(nums.Dump());
Console.WriteLine(list.Dump());

//Format 付きテスト
var sqrt = Enumerable.Range(1, 10).Select(e => Math.Sqrt(e)); //IEnumerable<double>
Console.WriteLine(sqrt.Dump("{0:F3}")); //小数点3桁

[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]
[hoge, fuga, piyo]
[1.000, 1.414, 1.732, 2.000, 2.236, 2.449, 2.646, 2.828, 3.000, 3.162]

 string format の引数を持つオーバーロードは float や double 等、浮動小数点の型で使うと便利だろう。

 一覧を囲む文字は "["~"]" で固定してしまってるが、必要ならオーバーロード作るのも良い。

 配列(Array) や IEnumerable<T> をそのまま使えるので、利用範囲は広いと思う。

 ちなみに、文字列配列を結合するのに string.Join を使っているが、LINQEnumerable.Aggregate を使う手もある。しかし、実装速度は劣るようだ。

//string.Join の代わりに Enumerable.Aggregate を使っても良いが、実行速度は遅い
source.Select(e => e.ToString()).Aggregate((s, e) => s + ", " + e)

 どちらか言うと、デバッグにしか使わないと思うが、あまり巨大な配列などではないなら、開発中は常に色々ダンプして、中身をチェックした方が不具合も見つけやすい。

 こういうちょっとしたものが、意外とデバッグを楽にしてくれたりするんだよね(笑)。





(関連記事)
【Unity】【C#】LINQとfor, ListとArray(配列)での実行速度を比較してみる
【C#】連想配列(Dictionary)を値 or キーでソート
【C#】2次元配列(ジャグ配列・多次元配列)のソート
【C#】多次元配列とジャグ配列(2次元配列)のサイズ(長さ)、相互変換など


category: C#

thread: プログラミング

janre: コンピュータ

tag: C#ライブラリ  配列操作 
tb: 0   cm: --

【C#】最小公倍数を求める(ユークリッドの互除法)  


 前回の最大公約数の求め方のついで。

 ユークリッドの互除法が実装できてれば、最小公倍数を求めるのは簡単だ。

 前回の Gcd (Greatest Common Divisor) を用いて、Lcm (Least Common Multiple) を書いてみよう。

●最小公倍数を求める(ユークリッドの互除法)
/// <summary>
/// 最小公倍数を求める(Least Common Multiple)
/// 2020/11/14 Fantom
/// http://fantom1x.blog130.fc2.com/blog-entry-385.html
/// http://fantom1x.blog130.fc2.com/blog-entry-384.html
/// http://fantom1x.blog130.fc2.com/blog-entry-215.html#lcm
/// </summary>
/// <param name="a">数値1 (>0)</param>
/// <param name="b">数値2 (>0)</param>
/// <returns>最小公倍数</returns>
public static int Lcm(this int a, int b)
{
return a * b / Gcd(a, b); //※Gcd は前回の記事を参照
}

●メインコード例
using System;

int h = 16;
int v = 9;
int m = Lcm(h, v);
Console.WriteLine("m = " + m);

m = 144

 a > 0, b > 0 となっているので注意(特に負の剰余は言語によって結果が違うので注意)。

 Gcd() は前回の記事からコピペしてきて欲しい。

 これも Java からのそのまま移植なので、解説は以前の記事を見て欲しい。ユークリッド互除法最小公倍数の定義に関する説明も Wikipedia で良いだろう。数学的なものはこの辺りの例を見ればわかるだろう。

 定義をそのままコーディングしたようなものなので、他言語でも簡単に移植できると思う。

【Java】最大公約数・最小公倍数を求める(ユークリッドの互除法)
ユークリッドの互除法 (Wikipedia)
最小公倍数 (Wikipedia)
最大公約数と最小公倍数







(関連記事)
【C#】最大公約数を求める/分数の約分をする(ユークリッドの互除法)
【Unity】【C#】画面解像度とアクペクト比(整数)を求める
【Java】最大公約数・最小公倍数を求める(ユークリッドの互除法)
【Java】3つ以上の最大公約数を求める(ユークリッドの互除法②)
【Java】拡張ユークリッドの互除法


category: C#

thread: プログラミング

janre: コンピュータ

tag: アルゴリズム  算術関数  約数 
tb: 0   cm: --

【C#】最大公約数を求める/分数の約分をする(ユークリッドの互除法)  


 ちょっとアプリを作っていて、画面解像度のアスペクト比(整数)を計算させようと思ったのだが、C# ではユークリッドの互除法のライブラリを作ってなかった…。

 というわけで、以前に作った Java のコードをそのまま移植してみた。私はライブラリに関しては、割と古い書き方をするので、ほぼ Java → C# への移植はコピペに近い感じでできる。Java8 なら、LINQ 的なもできるけど、C# とは互換性あまり無いし、実行速度の問題もある。ライブラリは古い書き方で、メインコードは LINQ や Rx など新しい書き方で、なんて使い分けも実はオススメだ(笑)。




■最大公約数を求める(ユークリッドの互除法)[非再帰版]

 ユークリッドの互除法の実装を調べると、非再帰と再帰でのやり方が出てくると思うが、一応両方書いておこう。普通に使うなら、非再帰版で良いと思うが、シンプルに書きたいときなどは再帰版でも良いかもね。

●最大公約数を求める(ユークリッドの互除法)[非再帰版]
/// <summary>
/// 最大公約数を求める(Greatest Common Divisor).
/// ユークリッドの互除法 (非再帰版).
/// 2020/11/12 Fantom (Java から移植)
/// http://fantom1x.blog130.fc2.com/blog-entry-384.html
/// http://fantom1x.blog130.fc2.com/blog-entry-215.html
/// </summary>
/// <param name="a">数値1 (>0)</param>
/// <param name="b">数値2 (>0)</param>
/// <returns>最大公約数 (なし=1 [互いに素])</returns>
public static int Gcd(this int a, int b)
{
//a > b にする(スワップ)
if (a < b)
{
int tmp = a;
a = b;
b = tmp;
}

//ユークリッドの互除法
int r = -1;
while (r != 0)
{
r = a % b;
a = b;
b = r;
}

return a; //b には r = 0 の値が入るため、a を返す
}

●メインコード例
using System;

int a = 1920;
int b = 1080;
int d = Gcd(a, b);
Console.WriteLine("d = " + d); //最大公約数
Console.WriteLine(a + "x" + b + " => " + (a / d) + ":" + (b / d)); //約分に利用

d = 120
1920x1080 => 16:9

 a > 0, b > 0 となっているので注意(特に負の剰余は言語によって結果が違うので注意)。

 Java からのそのまま移植なので、解説は以前の記事を見て欲しい。ユークリッド互除法に関する説明も Wikipedia で良いだろう(アニメーションがとてもわかり易い)。数学的なものはこの辺りの例を見ればわかるだろう。

【Java】最大公約数・最小公倍数を求める(ユークリッドの互除法)
ユークリッドの互除法 (Wikipedia)
最大公約数と最小公倍数



■最大公約数を求める(ユークリッドの互除法)[再帰版]

●最大公約数を求める(ユークリッドの互除法)[再帰版]
/// <summary>
/// 最大公約数を求める(Greatest Common Divisor).
/// [ユークリッドの互除法] (再帰版).
/// 2020/11/12 Fantom (Java から移植)
/// http://fantom1x.blog130.fc2.com/blog-entry-384.html
/// http://fantom1x.blog130.fc2.com/blog-entry-215.html
/// </summary>
/// <param name="a">数値1 (>0) (a >= b)</param>
/// <param name="b">数値2 (>0)</param>
/// <returns>最大公約数 (なし=1 [互いに素])</returns>
public static int GcdRec(int a, int b)
{
return (b == 0) ? a : GcdRec(b, a % b);
}

●メインコード例
using System;

int a = 1920;
int b = 1080;
int d = (a >= b) ? GcdRec(a, b) : GcdRec(b, a); //a >= b にする
Console.WriteLine("d = " + d); //最大公約数
Console.WriteLine(a + "x" + b + " => " + (a / d) + ":" + (b / d)); //約分に利用

d = 120
1920x1080 => 16:9

 a > 0, b > 0 となっているので注意(特に負の剰余は言語によって結果が違うので注意)。
 また、再帰版は、a >= b を関数内でチェックしてないので少し注意だ。

 また、シンプルなので再帰的な書き方は好まれる傾向があるが、再帰というのは自身のメソッドを呼ぶたびにスタックを積むため、メモリ的には優しくない。特にスマホみたいなメモリが小さいものには要注意だ。Gcd ではそれほど深くはならないかもだが、フィボナッチ数などで大きな数になると、かなりの深さになることもある。もし使わなくても書けるなら、使わない方が良い(フィボナッチ数も動的計画法を使うと良い)。



■分数の約分をする

 Gcd の使用例で約分しているが、それを簡単にまとめたものを作ってみよう。引数 a, b を分数の分子/分母に見立てただけのものである。これが分数の約分計算と同等になる。

●分数の約分をする
/// <summary>
/// a / b の約分をする
/// 2020/11/12 Fantom
/// http://fantom1x.blog130.fc2.com/blog-entry-384.html
/// </summary>
/// <param name="a">分子 (>0)</param>
/// <param name="b">分母 (>0)</param>
/// <returns></returns>
public static (int, int) Reduce(this int a, int b)
{
int d = Gcd(a, b); //最大公約数
return (a / d, b / d);
}

●メインコード例
using System;

int width = 1920;
int height = 1080;
var aspect = Reduce(width, height);
Console.WriteLine($"{width}x{height} => {aspect.Item1}:{aspect.Item2}");

1920x1080 => 16:9

 a > 0, b > 0 となっているので注意(特に負の剰余は言語によって結果が違うので注意)。

 .NET 4.x の書き方となっているので、.NET 3.5 以前なら、

public static int[] Reduce(this int a, int b)
{
int d = Gcd(a, b); //最大公約数
return new int[]{ a / d, b / d };
}

にして、[0]分子/[1]分母 にすれば良いだろう。

 これを使えば、アスペクト比(整数)の計算も簡単だ(笑)。

 ちなみに、.NET 4.x の方の書き方の戻値に (int, int) を使っているが、これはタプルと言い、C#7.3 以降からは値型の ValueTuple がデフォルトとなり、メモリパフォーマンスも良くなったようだ(以前は参照型で、パフォーマンスも悪かったらしい)。また、ここでは .Item1, .Item2,... のアクセサを使っているが、名前を付けることもできる。

 Unity の場合、2018.3 以降ではデフォルトで使えるようになっているので、int[] よりタプルの方が良いかも知れない(int[] は参照型なので、無駄にヒープを使うため)。詳しい内容は以下に参考資料を載せておくので、確認してみるのも良いだろう。

(参考)
タプル記法 (ValueTuple)
タプル型 (C# リファレンス)
C# のメモリ管理
Unity と C# のバージョン



■3つ以上の最大公約数を求める(ユークリッドの互除法②)

 もう1つ、ついでに3つ以上の数の最大公約数を求めるユークリッドの互除法も作っておこう。テスト用のコードには2つの Gcd との計算比較も書いておくので、それをまとめたものということもわかるだろう。

●3つ以上の最大公約数を求める(ユークリッドの互除法②)
/// <summary>
/// 3つ以上の最大公約数を求める[gcd(a, b, c,...)].
/// ユークリッドの互除法の繰り返しで求める.
/// 2020/11/12 Fantom (Java から移植)
/// http://fantom1x.blog130.fc2.com/blog-entry-384.html
/// http://fantom1x.blog130.fc2.com/blog-entry-220.html
/// </summary>
/// <param name="param">数値1, 2, 3,... (a, b, c,...) (>0) [引数は3個以上]</param>
/// <returns>最大公約数(なし=1[互いに素])</returns>
public static int Gcd(params int[] param) {
int len = param.Length;
int g = Gcd(param[0], param[1]);
for (int i = 1; i < len - 1; i++)
{
g = Gcd(g, param[i + 1]);
}
return g;
}

●メインコード例
using System;

int a = 12;
int b = 36;
int c = 60;
int d = 96;

int g1 = Gcd(a, b);
int g2 = Gcd(g1, c);
int g3 = Gcd(g2, d);
Console.WriteLine("g3 = " + g3);

int g = Gcd(a, b, c, d);
Console.WriteLine("g = " + g);
Console.WriteLine(g == g3);

g3 = 12
g = 12
true

 a > 0, b > 0 となっているので注意(特に負の剰余は言語によって結果が違うので注意)。

 これも Java コードそのままなので、解説は以前の記事を参照して欲しい。

【Java】3つ以上の最大公約数を求める(ユークリッドの互除法②)
The case of more than two numbers (Wikipedia)



 私は以前、アルゴリズム系の問題をよく Java で解いていたが、ちょっとしたとき、便利なアルゴリズムなんかもあったりするので、興味あったら勉強してみるのも良いかもね。アルゴリズム自体を完全理解しなくても(物によっては数学的に難しいものもある)、使い方だけ覚えておけば、色々なものに応用できることもある。アルゴリズム自体は特に言語に関係ないしね。1冊くらい何か読んでおくのはオススメ。







(関連記事)
【Unity】【C#】画面解像度とアクペクト比(整数)を求める
【C#】最小公倍数を求める(ユークリッドの互除法)
【Java】最大公約数・最小公倍数を求める(ユークリッドの互除法)
【Java】3つ以上の最大公約数を求める(ユークリッドの互除法②)
【Java】拡張ユークリッドの互除法


category: C#

thread: プログラミング

janre: コンピュータ

tag: アルゴリズム  算術関数  約数 
tb: 0   cm: --

【C#】クラスのフィールド名を文字列の配列で取得する  


 ただのメモ。クラスや構造体をデータのやり取りに使う際、フィールド名を一覧で取得したいときがある。JSONスキーマ的なものとかね。そんな時に役立つ簡単なサンプルコード。

●フィールド名を取得するクラス例
using System.Linq;
using System.Reflection;

public class Sample //※名前は任意(typeof で指定する)
{
//取得するフィールド
public const string c_value = "const value";
public static readonly string s_value = "static value";
public string pub_value = "public value";
private string pri_value = "private value";

//フィールド名を string[] で返す
public string[] GetFieldNames()
{
var bindingFlags = BindingFlags.Public //┐どちらか一方は指定する
| BindingFlags.NonPublic //┘
| BindingFlags.Instance //┐どちらか一方は指定する
| BindingFlags.Static //┘
| BindingFlags.DeclaredOnly; //このクラスのみ(継承などは含めない)

var t = typeof(Sample); //取得するクラス名
var fields = t.GetFields(bindingFlags);
return fields.Select(e => e.Name).ToArray();
}
}

●メインコード例
using System;

var sample = new Sample();
var names = sample.GetFieldNames();
Console.WriteLine($"Length = {names.Length}");
foreach (var item in names)
{
Console.WriteLine(item);
}

Length = 4
c_value
s_value
pub_value
pri_value

 BindingFlags の指定で、public や private, インスタンスや static などを選択することができる(少なくともこれらの属性は指定した方が良い)。また継承してるクラスがある場合、BindingFlags.DeclaredOnly でスーパークラスのフィールドを除外することもできる。

 ここではフィールド名が欲しかったので Type.GetFields() を使っているが、メソッドやプロパティ名を取得したいなら、Type.GetMembers() を同様に使えば良い。

 リフレクション(Reflection)関連は使いこなせれば色々と便利だが、ランタイム時の負荷はそれなりにかかるそうで、実行速度を優先するなら、先に文字列化したものをデータ化しておくなど、工夫した方が良いだろう。使い所は適切に(笑)。

BindingFlags 列挙型
Type.GetFields()
Type.GetMembers()
リフレクション





(関連記事)
【C#】【Unity】enum 型と string, int 型の相互変換など


category: C#

thread: プログラミング

janre: コンピュータ

tag: C#リファレンス 
tb: 0   cm: --

【C#】GUID の生成と書式  


 GUID の生成はググればすぐに出てくるのだが、生成されたオブジェクトは Guid 型なので、普段使うには文字型に変換して使うことが多い。

Guid.NewGuid メソッド

 幸いなことに文字列変換の Guid.ToString() には、いくつかのオーバーロードが定義されているので、利用方法は簡単だ。ただ「あれ?何を指定するんだっけ…?」とよく調べるので、メモ代わりに具体例を書いておくことにした(解説はマニュアル参照)。

Guid.ToString(String)

●テストコード
using System;

Console.WriteLine("(): " + Guid.NewGuid().ToString());
Console.WriteLine("N : " + Guid.NewGuid().ToString("N"));
Console.WriteLine("D : " + Guid.NewGuid().ToString("D"));
Console.WriteLine("B : " + Guid.NewGuid().ToString("B"));
Console.WriteLine("P : " + Guid.NewGuid().ToString("P"));
Console.WriteLine("X : " + Guid.NewGuid().ToString("X"));

(): 9379a57a-eb51-431e-bf78-e2bdb96bf249
N : b11b80a4ebd54e19916d3de68cf6e548
D : f5f54a7d-b46b-4d99-a70c-dff3216f1766
B : {cc3e87ff-83a5-4eac-b465-e4b967173db7}
P : (8cc1037b-db1b-4bb0-969a-6af901719548)
X : {0xe49cb895,0xe333,0x4704,{0x81,0x25,0xa2,0x70,0x79,0x4e,0xf6,0xae}}

 GUID はハッシュ値と違い、毎回変わるので、上の例はあくまでもその実行時の結果なので注意。

 "()":引数無しと "D" は同じようだ。普段使うのは引数無し(or "D") か "N" (ハイフン無し)を使うことが多いだろう。

 他は括弧囲み "()" だったり、中括弧 "{}" だったり、16進 "0x" だったりとなるだけだ。

 GUID はセッションID や識別ID などによく使われる。内容的には以下の記事にあるように『GUID値とは、世界で重複することがない、ユニーク(=唯一)な128bit(16byte)のランダムな数値である。GUID値は必ずしも「絶対に同じID値が生成されないこと」を保証するものではないが、その数値の範囲が2の128乗、つまり「およそ3.4×(10の38乗)」(=340億の100兆倍の100兆倍)もあるので、現実的に同じIDが生成される可能性はきわめて低い。』というものだ。例えば、ブラウザやアプリなどで初めに生成→保存し、利用者を識別したりするのにも使われる(広告とか)。簡易的なユニークID として利用するにはもってこいだろう。

 ちなみに、2128 = 340282366920938463463374607431768211456
(340澗2823溝6692穣938𥝱4634垓6337京4607兆4317億6821万1456) だそうだ(笑)。

.NET TIPS GUID値を生成するには?





(関連記事)
【C#】MD5, SHA1, SHA256, SHA384, SHA512 等のハッシュ値を生成する
【Unity】【C#】文字列の暗号化・復号化を行う


category: C#

thread: プログラミング

janre: コンピュータ

tag: C#リファレンス 
tb: 0   cm: --


プロフィール

Social

検索フォーム

全記事一覧

カテゴリ

ユーザータグ

最新記事

リンク

PR

▲ Pagetop