ブログ「サイバー少年」

ブログ「サイバー少年」へようこそ!
小学六年生ごろからプログラミングを趣味にしている高校生のブログです。
勉強したことについての記事などを書いています。フリーソフトも制作、公開しています。
(当ブログについて詳しくは「ブログ概要紹介」を参照)

サイバー少年が作ったフリーソフトは「サイバー少年の作品展示場」へ

C#の独自ソートで順序関係を壊す

今回C#の記事ではあるんですが、実質的には特定のプログラミング言語に依存しない話です。
そして数学の順序関係からヒントを得ています。


さて、C#ではIComparer<T>インターフェースを実装するクラスを定義し、Compareメソッドを実装してやることにより、
リストのソートなどで要素同士を比較するときに使うメソッドをカスタマイズできます。

しかしながら、ふと思ったのですが、どのようにこのCompareメソッドを実装してもいいわけではなくて、書き方次第では正常にソートできなくなってしまうのではないかということが、

まあ考えてみれば当たり前のことではあるのですが、ピンときたわけであります。


たとえば、以下のようなコードを書いてみます。


class WrongComparer : IComparer<string> {
  public int Compare(string x, string y) {
    return 1;
  }  }

これは常にxがyより大きいことを意味しています。

ここでstringのリストlistで、list.Sort(new WrongComparer())を実行してみました。


すると例えばlistが { "Red", "Green", "Blue" } だったとき結果は { "Blue", "Green", "Red" } と逆順になりました。

MSDNによるとSortメソッドは要素数が16個より小さい場合は挿入ソートを使用するそうですので、もし一番後ろの"Blue"から入れ替えを始めていたとしたら、

最初は"Blue"が配置されて、次にCompare("Green", "Blue") == 1より"Blue"の後ろに"Green"、次にCompare("Red", "Green") == 1より"Green"の後ろに"Red"という感じで、順番がひっくり返るのもうなづけますね。

続きを読む

tag: C# リスト ソート 数学 順序 関係 クソ記事 クイックソート

C#でscanf的なことをする

既出ですが、C#でscanf的に値を読み込む方法を書いていきたいと思います。

今回はわりと小さい記事です。
久しぶりにC#カテゴリの記事を書きますが、VB.NETでもPowerShellでも、.NETで動くもので共通ですけどね。


そもそもC言語ではscanf関数を使って

int a,b;
scanf("a:%d, b:%d", &a, &b);


みたいにフォーマットされた文字列を読み込んで指定の部分を変数に格納することができます。


しかし、C#ではこういう関数がないので、どうしようかと困ってしまいますね。

単純なカンマ区切りとかだったらString.Splitで特定の場所を取ったりできますが、なんか自然じゃないし、そもそももっと複雑なフォーマットになるとコードも複雑になります。



じゃあどうするかというと、正規表現のキャプチャを使います。
番号でグループにアクセスしてもいいんですが、わかりやすさのため(?<a>[0-9]+)みたいにグループに名前をつけておきます。

string input = Console.ReadLine();
Match m = Regex.Match(input, "^a:(?<a>[0-9]+), b:(?<b>[0-9]+)$");
int a = int.Parse(m.Groups["a"]);
int b = int.Parse(m.Groups["b"]);


(?<a>[0-9]+)などがscanfでいう%dに相当します。


読み込むのが整数じゃなくて小数だった場合は、scanfでは%fと書けば済みますが、
こちらの正規表現では小数のフォーマットを自前で書かないといけないので多少面倒さは残ります。

あと、負数に対応させる場合も自前で書かないといけないですね。


string input = Console.ReadLine();
Match m = Regex.Match(input, @"^a:(?<a>[0-9]+(\.[0-9]+)?), b:(?<b>-?[0-9]+)$");
float a = float.Parse(m.Groups["a"]);
int b = int.Parse(m.Groups["b"]);



しかし書式を自前で書かないといけないということは、それだけ細かいところに手が届くので、カスタマイズしやすいということでもありますね。

さっき調べてみたら、scanfの書式指定もわりと多機能そうだったんですが、正規表現には敵わないでしょうね。

正規表現では繰り返しの表現が可能ですので、頑張れば任意の長さのカンマ区切りのデータなども読めます。


ということで、正規表現で入力を読み込めば便利という記事でした。

読み込む部分を関数に切り分けて、なんちゃってscanf関数を作ってもいいかもしれないですね。

tag: プログラミング C# C言語 文字列 正規表現 パターンマッチ 入力

C#のout引数は型の反変性をサポートしない

C#で

void Method(string arg)Method(object変数) で呼び出すことは不可能ですが、
void Method(object arg)Method(string変数) で呼び出すことは可能です。

後者はアップキャストを行っています。
前者は、argにnew object()とかが入ってきたら困るので、不可能になっています。

次に、 void Method(ref string arg)Method(ref object変数) で呼び出すことは不可能です。

そして、 void Method(ref object arg)Method(ref string変数)で呼び出すことも不可能です。

前者が不可能な理由は先ほどと同じですが、

後者がなぜ先ほどと違うのかというと、参照渡しの場合は呼び出し元の変数を書き換え可能ですから、
Method内で arg = new object(); とか出来るわけですが、そうした場合に、Methodを呼び出した側からしてみればstring変数にnew object()が入っていることになってしまうからです。

続きを読む

tag: C# 引数 out引数 共変性/反変性 ジェネリックス

C#のアクセシビリティの謎仕様

少し前に知って驚いたんですが、クラス「Class」があったとして

class Class
{
  private int _Field;
  public void Method()
  {
    Class cla = new Class()
    cla._Field = 10000;
  }
}


こういうコードは有効になるんですよ。

つまりどういうことかというと、同じクラスなら、
たとえ自分と違うインスタンスであってもprivateなメンバにアクセスできるということです。

同様に、インスタンスメソッドからprivateなスタティックメンバにアクセスすることもできますし、

スタティックメソッドからprivateなインスタンスメンバにアクセスすることも可能です。


この仕様っておかしくないですかね。

クラスが同じであっても、インスタンスが違うのですから扱い的には他人だと思うのです。

アクセスできるとは不気味です。


もしかして、自分インスタンスのクローンを作るメソッドを実装する(Prototypeパターンというらしいです)さいに、

privateなコンストラクタを定義して、引数にコピーするべきフィールドを全て用意して…とするのは可読性が損なわれるわけで、

public Class GetClone()
{
  Class cla1 = new Class(this.FieldA, this.FieldB, this.FieldC);
  // 可読性が悪い
 
  Class cla2 = new Class();
  cla.FieldA = this.FieldA;
  cla.FieldB = this.FieldB;
  cla.FieldC = this.FieldC;
}


これのcla2のようなやり方を実現するために、このような仕様にしたんですかね。

それでも、オブジェクト指向的な概念で言えば、この仕様はおかしいと思うのです。

tag: C# アクセシビリティ

C#で値型の“参照”は扱えないのか!

C言語にはポインタがあります。

ポインタを使えば、

int a; a = 100;
int* p = &a;
------------
*p = 200;
------------
printf("%d", a);


このコードは不思議なことに、100でなく200が出力されてしまうというわけです。

C言語にはこんな素敵な(??)機能がありますが、なんとC#にはありません。

ですが、代わりに参照というものが用意されているのです。
詳しくは書きませんが、参照とは安全なポインタのようなものです。

ここまでは予備知識として書きました。

続きを読む

tag: C# 値型 参照

当ブログをご利用(閲覧等)になる場合は必ず「当ブログの利用規定」をお守りください。