C# と VB.NET の質問掲示板

ASP.NET、C++/CLI、Java 何でもどうぞ

C# と VB.NET の入門サイト

Re[4]: Dynamic や、LinQ の使い方がわかりません。


(過去ログ 129 を表示中)

[トピック内 5 記事 (1 - 5 表示)]  << 0 >>

■76315 / inTopicNo.1)  Dynamic や、LinQ の使い方がわかりません。
  
□投稿者/ よぽん (46回)-(2015/06/24(Wed) 14:50:15)

分類:[.NET 全般] 

お世話になります。

フォームに NumericUpDown を5つ配置しました。
いずれも Minimum = 1, Maximum= 5 に設定し
ValueChanged を nud_ValueChanged にしています。
また、初期値として、
numericUpDown1.Value = 1,
numericUpDown2.Value = 2,
.....
numericUpDown5.Value = 5
にしています。

処理としては
いずれかの NumericUpDown の値を変更すると
他の NumericUpDown の値も同じ値をとらないように
変更するようにしています。


1,2,3,4,5  2番目のデータを4にすると

1,4,2,4,5  4番目のデータが4で重複するのでひとつ下げる
1,4,3,3,5  すると次に3が重複するので
1,4,2,3,5  3番目のデータを2に変更する。

力ずくで作ったのが以下になります。

bool exeflg = false;

void nud_ValueChanged(object sender, EventArgs e)
{
NumericUpDown nudsender = (NumericUpDown)sender;
NumericUpDown nudwork;

// 処理中は何度も ValueChange が呼び出されるので処理されないようにする。
if(exeflg) return;
exeflg = true;

// 変更された numericupdown 以外のリストを作成
NumericUpDown[] nudarray = { numericUpDown1, numericUpDown2, numericUpDown3, numericUpDown4, numericUpDown5 };
List<NumericUpDown> nudlist = new List<NumericUpDown>();
for(int inum = 0; inum < 5; inum++)
{
if(nudarray[inum] != nudsender) nudlist.Add(nudarray[inum]);
}

// 変更された numericupdown 以外のリストからValueでソートして、番号を付け直す
nudarray = nudlist.ToArray();
for(int inum1 = 0; inum1 < nudarray.Count() - 1; inum1++)
{
for(int inum2 = inum1 + 1; inum2 < nudarray.Count(); inum2++)
{
if(nudarray[inum1].Value > nudarray[inum2].Value)
{
nudwork = nudarray[inum1];
nudarray[inum1] = nudarray[inum2];
nudarray[inum2] = nudwork;
}
}
}
for(int inum = 0; inum < nudarray.Count(); inum++) nudarray[inum].Value = inum + 1;

// 変更した numericupdown を挿入して、番号を付け直す
nudlist = new List<NumericUpDown>(nudarray);
for(int inum = 0; inum < nudlist.Count; inum++)
{
if(nudsender.Value <= nudlist[inum].Value)
{
nudlist.Insert(inum, nudsender);
break;
}
}
for(int inum = 0; inum < nudlist.Count; inum++) nudlist[inum].Value = inum + 1;

exeflg = false;
}

ただしいかどうかわかりませんが、一応動作はしています。
しかし、あまりにもスマートじゃないし、何しているかわかりにくいように思います。

Dynamic を使ったり、 LinQ を使ったりするともっと短いコード(数行)で
できそうな気がしますが、Dyanamic も LinQ も使い方がわからず、
動作させることができませんでした。
どのようなコード(プログラム)の書き方ができますか?

このやり方ならもっと他に方法があるとか・・?

お願いします。




引用返信 編集キー/
■76316 / inTopicNo.2)  Re[1]: Dynamic や、LinQ の使い方がわかりません。
□投稿者/ shu (759回)-(2015/06/24(Wed) 15:12:41)
No76315 (よぽん さん) に返信

> 処理としては
> いずれかの NumericUpDown の値を変更すると
> 他の NumericUpDown の値も同じ値をとらないように
> 変更するようにしています。
>
> 例
> 1,2,3,4,5  2番目のデータを4にすると
> ↓
> 1,4,2,4,5  4番目のデータが4で重複するのでひとつ下げる
> 1,4,3,3,5  すると次に3が重複するので
> 1,4,2,3,5  3番目のデータを2に変更する。
>
> 力ずくで作ったのが以下になります。
>

LINQ関係ない話ですが
元々ちょうふくしていないのなら
i番目の変更をA=>Bとした場合Bの入っているj番目をAに変更するのでは
駄目なのでしょうか?

引用返信 編集キー/
■76317 / inTopicNo.3)  Re[2]: Dynamic や、LinQ の使い方がわかりません。
□投稿者/ ぶなっぷ (27回)-(2015/06/25(Thu) 10:26:42)
ソースコードを短くするのが目的のようですので、思いつく限り挙げてみます。

1)nudarrayの初期化
配列じゃなくて、Listに初期化しちゃうのがお勧め
 → そうすると、LINQで簡単にソートできるから
  List<NumericUpDown> nudlist = new List<NumericUpDown>()
    { numericUpDown1, numericUpDown2, numericUpDown3, numericUpDown4, numericUpDown5 };

1)nudlistのソート
LINQには書き方が複数あります。
まずは、メソッド形式
List.Sort()メソッドとラムダ式の組み合わせでのソート
  nudlist.Sort((X, Y) => X.Value.CompareTo(Y.Value));
もう一つは、&#8226;クエリ式
  var sortnudlsit = from item in nudlist orderby item.Value select item;
sortnudarrayの結果は IEnumerable<NumericUpDown> になります。
ソート結果を、List<NumericUpDown> にしたければ、
  var sortnudlsit = (from item in nudlist orderby item.Value select item).ToList();
ただし、処理が遅くなるので、ソート後に
  foreach(var nud in sortnudlsit)
  {
  }
のようにforeachループしかしないなら、いちいちList化しない。
IEnumerable<NumericUpDown> のままでも foreachループ は有効です。

細かくコードを追っていないので、とりあえず、このくらい。

引用返信 編集キー/
■76320 / inTopicNo.4)  Re[3]: Dynamic や、LinQ の使い方がわかりません。
□投稿者/ ぶなっぷ (28回)-(2015/06/25(Thu) 11:39:11)
試してないですが、ソートに関してはクエリ式の方なら、配列のままでもいけるはず。
あと、はじめの頃はラムダ式がイメージできないでしょうから、簡単なサンプル。

たとえば、リストの中から、最も大きな数字を選び出すメソッド。
まずは、メソッドではなく、処理を考えます。
質問内容からして以下のコードは理解できると思います。
    var NumList = new List<int>()
    {
        15, 28, 9, 53, 26, 37, 24, 62, 19, 54 
    };
    int MaxNum = -1;
    foreach (var Num in NumList)
    {
        if (Num > MaxNum) MaxNum = Num;
    }
    Trace.WriteLine(string.Format("最大の数字は{0}です", MaxNum));
この最大値を求める処理をメソッド化します。
Listクラスの派生クラスを作って、MyList.Max()を実装します。
 ※ List.Max()ってのがすでにいるので無意味なんですが、あくまでサンプルって
    ことで(^^;
    public class MyList : List<int>
    {
        int Max()
        {
            int MaxNum = this[0];
            foreach (var Num in this)
            {
                if (Num > MaxNum) MaxNum = Num;
            }
            return MaxNum;
        }
    }
実行するときは以下のような感じです。
    var NumList = new MyList()
    {
        15, 28, 9, 53, 26, 37, 24, 62, 19, 54 
    };
    Trace.WriteLine(string.Format("最大の数字は{0}です", NumList.Max()));
問題ないですね。


同様に、最小値を求める処理を実装すると以下のような感じです。
    public class MyList : List<int>
    {
        int Min()
        {
            int MinNum = this[0];
            foreach (var Num in this)
            {
                if (Num < MinNum) MinNum = Num;
            }
            return MinNum;
        }
    }

違いはif()の中の条件式だけでしょ。こういうのを共通化するのをやってみます。
if()の処理だけ関数化します。
    public class MyList : List<int>
    {
        int Find()
        {
            int FindNum = this[0];
            foreach (var Num in this)
            {
                FindNum = FindMethod(FindNum, Num);
            }
            return FindNum;
        }

        int FindMethod(int X, int Y) { return (X > Y) ? X : Y; }
    }

そして、FindMethod()をdelegate化します。
    public delegate int FindDelegate (int X, int Y);

    public class MyList : List<int>
    {
        public int Find(FindDelegate Find)
        {
            int FindNum = this[0];
            foreach (var Num in this)
            {
                FindNum = Find(FindNum, Num);
            }
            return FindNum;
        }
    }

delegate化したので、関数は外部から与えます。
    int MaxFind(int X, int Y) { return (X > Y) ? X : Y; }
これで、最大値を求めるなら以下のように呼べます。
    Trace.WriteLine(string.Format("最大の数字は{0}です", NumList.Find(MaxFind)));
同様に最小値を求めるなら以下のような感じです。
    Trace.WriteLine(string.Format("最小の数字は{0}です", NumList.Find(MinFind)));
分かりますか、NumList.Find()に渡す関数を差し替えるだけで動作が変わるでしょ。

でも、差し替える関数 MaxFind(), MinFind() はものすごく簡単な処理しかしてない。
だから、いちいち関数書くのも面倒。
そこで出てくるのがラムダ式です。
    Trace.WriteLine(string.Format("最大の数字は{0}です",
        NumList.Find((X, Y) => (X > Y) ? X : Y))
    );
このように書くと、関数を定義して、NumList.Find()に渡したことになるわけです。

  int MaxFind(int X, int Y) { return (X > Y) ? X : Y; }
を
  (X, Y) => (X > Y) ? X : Y
とすると同じ意味になるわけです。

長く説明しましたが、
ソート処理も、内部処理的にはデータ型が何であっても大して処理に違いが無いわけです。
唯一違うのがデータ型によって、ある要素とある要素の大小比較処理が違うだけです。

だから、ある要素とある要素の大小比較処理だけを外出し関数としてdelegate化してあるわ
けです。
  public void List<T>.Sort(Comparison<T> comparison)
このComparisonに渡す関数をラムダ式で書いてやればそれでいい。

ラムダ式について詳しくは解説書を読んでください。

引用返信 編集キー/
■76322 / inTopicNo.5)  Re[4]: Dynamic や、LinQ の使い方がわかりません。
□投稿者/ ぶなっぷ (29回)-(2015/06/25(Thu) 12:56:38)
ま、分かるかと思いますが、MInFind()の実装を載せるの忘れたので、載せておきます。

    int MinFind(int X, int Y) { return (X < Y) ? X : Y; }

引用返信 編集キー/


トピック内ページ移動 / << 0 >>

このトピックに書きこむ

過去ログには書き込み不可

管理者用

- Child Tree -