2015年07月26日

LINQ to Objectサンプル(23): 2重ループを記述する

   このエントリーをはてなブックマークに追加 Clip to Evernote
今回が、このシリーズの最終回です。

まずは、いつものように、ループを使うコードから。
何をやっているかは、コードを読んでください。

■ループを使ったコード
int[,] nums = new int[,] {
     {13, 200,31, 43, 54 },
     {110,20, 330,410,50},
     {100,220,33, 40, 500}
};
var points = new List<Point>();
int ax = -1;
int ay = -1;
for (int x = 0; x <= nums.GetUpperBound(0); x++ ) {
    for (int y = 0; y <= nums.GetUpperBound(1); y++) {
        if (nums[x, y] > 400) {
            points.Add(new Point { X = x, Y = y });
        }
    }
}
foreach (var p in points)
    Console.WriteLine("({0}, {1})", p.X, p.Y);


上のコードの結果です。

(1, 3)
(2, 4)


■LINQを使ったコード
int[,] nums = new int[,] {
     {13, 200,31, 43, 54 },
     {110,20, 330,410,50},
     {100,220,33, 40, 500}
};
var points = GetAllPoints(nums).Where(p => nums[p.X,p.Y] > 400);
foreach (var p in points)
    Console.WriteLine("({0}, {1})", p.X, p.Y);
...
private static IEnumerable<Point> GetAllPoints(int [,] nums) {
    return from x in Enumerable.Range(0, nums.GetUpperBound(0) + 1)
           from y in Enumerable.Range(0, nums.GetUpperBound(1) + 1)
           select new Point { X = x, Y = y };
}


GetAllPoints をメソッドを定義している分、LINQのコードのほうが冗長になっていますね。
しかし、2重ループさせる箇所が何か所も何か所も出てきた場合はどうでしょうか?
2重ループを使ったコードのほうが、冗長になるのではと思います。

GetAllPointsというメソッドを定義したことで、IEnumerable<Point> として扱えるようになります。
つまり、Where以外にも、Select, Take, Aggregateなどなど様々な LINQの拡張メソッドを繋げて処理する ことができるということです。

  

Posted by gushwell at 22:30Comments(0)TrackBack(0)

2015年07月22日

LINQ to Objectサンプル(22): 2種類の配列を対として利用する

   このエントリーをはてなブックマークに追加 Clip to Evernote
2つの配列があり、それぞれの要素は同じインデックスでなんらかの結びつけがあるものとします。 この2つの配列から、別の配列を作り出す例です。

ここでは、日本語の曜日と、英語の曜日表記の2つの配列があり、それを 一つにして、"月(MON)","火(TUE)", ... という配列を作り出しています。


■ループを使ったコード
var list1 = new [] { "月","火","水","木","金","土","日"};
var list2 = new[] { "MON", "TUE", "WED", "THU", "FRI", "SUT", "SUN" };
var list3 = new List<string>();
for (int i = 0; i < list1.Length; i++) {
    list3.Add(string.Format("{0}({1})", list1[i], list2[i]));
}
list3.ForEach(Console.WriteLine);


■LINQを使ったコード
var list1 = new[] { "月", "火", "水", "木", "金", "土", "日" };
var list2 = new[] { "MON", "TUE", "WED", "THU", "FRI", "SUT", "SUN" };
var list3 = list1.Zip(list2, (s1, s2) => string.Format("{0}({1})", s1, s2));
list3.ToList().ForEach(Console.WriteLine);

Zip拡張メソッドを使うことで、実現しています。

  
Posted by gushwell at 22:00Comments(0)TrackBack(0)

2015年07月19日

LINQ to Objectサンプル(21): 条件を満たしている間の値を取り出す

   このエントリーをはてなブックマークに追加 Clip to Evernote
今回は、整数の二乗の桁数が 3 桁 である 整数を求めたいと思います。

例えば、 8 * 8 は、 64 で条件を満たしません。12 * 12 は 144 で条件を満たします。


■ループを使ったコード
List<int> list = new List<int>();
int n = 1;
string s = (n * n).ToString();
while (s.Length < 4) {
    if (s.Length == 3)
        list.Add(n);
    n++;
    s = (n * n).ToString();
}
foreach (var num in list) {
    Console.WriteLine("{0} * {0} = {1}", num, num * num);
}


今までのコードと決定的に違うのは、何回ループさせたらよいか分からないという点ですね。

3桁になったら、リストの格納を開始し、4桁になったらループから抜け出しています。


では、LINQを使って書いてみます。ちょっと発想を変えていますが、結果は同じです。
■LINQを使ったコード
var seq = Enumerable.Range(1, int.MaxValue)
                     .SkipWhile(n => (n * n).ToString().Length < 3)
                     .TakeWhile(n => (n * n).ToString().Length == 3);
seq.ToList().ForEach(n => Console.WriteLine("{0} * {0} = {1}", n, n * n));


Enumerable.Rangeメソッドを使うと、指定した範囲の整数を順に列挙することができます。
最大値がわからないので、ここでは、int.MaxValueの数だけ整数を列挙しています。
100*100 は明らかに、4桁をオーバーするので、

Enumerable.Range(1, 100)

としてもかまいませんが、100の根拠が不明瞭ですし、どちらにせよ速度は変わりないです。

SkipWhileメソッドで、二乗が3桁未満の整数をスキップします。
TakeWhileメソッドで、二乗が3桁の間だけ値を取り出しています。
if文を書かずに、こういった処理が書けるのも LINQの楽しいところです。

上の例では、いったん文字列にして桁数を判断していますが、整数のまま処理することもできますね。 以下がそのコードです。


var seq = Enumerable.Range(1, int.MaxValue)
                     .SkipWhile(n => (n * n) < 100)
                     .TakeWhile(n => (n * n) < 1000);
seq.ToList().ForEach(n => Console.WriteLine("{0} {1}", n, n * n));

  
Posted by gushwell at 22:00Comments(0)TrackBack(0)

2015年07月15日

LINQ to Objectサンプル(20): 配列の全ての要素に対し、ある変換処理をする(2

   このエントリーをはてなブックマークに追加 Clip to Evernote
前回の続きです。

今回は、テキストファイルから読み込んだ各行に行番号を振った結果を リストに格納し、それをコンソールに出力する例です。


■ループを使ったコード
var list = new List<string>();
int lno = 1;
foreach (var line in File.ReadLines("sample.txt")) {
    list.Add(string.Format("{0,4}: {1}",lno++,line));
}
list.ForEach(line => Console.WriteLine(line));

ここでは、FileクラスのReadLinesメソッドをっています。
このReadLinesメソッドは、自分で、ファイルのopen、read, close のコードを書かなくてよいので便利です。


■LINQを使ったコード
var lines = File.ReadLines("sample.txt")
                .Select((s, n) => string.Format("{0,4}: {1}", n+1,s));
lines.ToList().ForEach(file => Console.WriteLine(file));


Select拡張メソッドには、インデックスを扱えるメソッドも利用されています。このSelectメソッドを使い、 各行の先頭に行番号を付加しています。

  
Posted by gushwell at 22:30Comments(0)TrackBack(0)

2015年07月12日

LINQ to Objectサンプル(19): 配列の全ての要素に対し、ある変換処理をする(1)

   このエントリーをはてなブックマークに追加 Clip to Evernote
配列の全ての要素に対しある変換処理をする場合にもLINQが使えます。
ここでは、文字列配列の各要素をすべて小文字にし、結果をリストにする例を示します。


■ループを使ったコード
string[] words = new string[] {
    "Microsoft","Apple","Google","Oracle","Adobe","IBM","Facebook" };
List<string> list = new List<string>();
foreach (var name in words) {
    list.Add(name.ToLower());
}
list.ForEach(Console.WriteLine);


■LINQを使ったコード
string[] words = new string[] {
    "Microsoft","Apple","Google","Oracle","Adobe","IBM","Facebook" };
var list = words.Select(name => name.ToLower());
list.ToList().ForEach(Console.WriteLine);


Select拡張メソッドで、小文字に変換しています。

今回示したコードでは、ToLowerを呼び出すだけの簡単な処理でしたが、 もっと複雑な変換処理もLINQで書くことが出来ます。

  
Posted by gushwell at 22:30Comments(0)TrackBack(0)

2015年07月08日

LINQ to Objectサンプル(18): 2つの配列を連結する

   このエントリーをはてなブックマークに追加 Clip to Evernote
今回は、2つのフォルダにあるファイルのフルパス名一覧を作成する例を示します。

■ループを使ったコード
string[] files1 = Directory.GetFiles("C:\\");
string[] files2 = Directory.GetFiles("D:\\");
// files1,files2を連結したfiles3を作成
List<string> files3 = new List<string>(files1);
foreach (var file in files2)
    files3.Add(file);
// 連結した結果を表示
files3.ForEach(file => Console.WriteLine(file));


■LINQを使ったコード
string[] files1 = Directory.GetFiles("C:\\");
string[] files2 = Directory.GetFiles("D:\\");
// files1,files2を連結したfiles3を作成
var files3 = files1.Concat(files2);
// 連結した結果を表示
files3.ToList().ForEach(file => Console.WriteLine(file));

Concatメソッドを使うと、2つのシーケンスを連結することができます。ここでは配列を例に取りましたが、List<T>などでも利用できます。

実をいうと、LINQを使わなくてもAddRangeメソッドを使えば、ループ無しで同一のコードを書くことができます。なので、そのコードも。

string[] files1 = Directory.GetFiles("C:\\");
string[] files2 = Directory.GetFiles("D:\\");
// files1,files2を連結したfiles3を作成
List<string> files3 = new List<string>(files1);
files3.AddRange(files2);
// 連結した結果を表示
files3.ToList().ForEach(file => Console.WriteLine(file));

  
Posted by gushwell at 22:30Comments(0)TrackBack(0)

2015年07月05日

LINQ to Objectサンプル(17): 指定したフォルダ内から条件に一致したファイル名を列挙する

   このエントリーをはてなブックマークに追加 Clip to Evernote
指定したディレクトリ内にあるファイルで、拡張子が”exe”であるファイル名の一覧を作成する例です。

■ループを使った例
string[] files = System.IO.Directory.GetFiles("C:\\Windows");
List<string> list = new List<string>();
foreach (var file in files) {
    if (Path.GetExtension(file) == ".exe")
        list.Add(Path.GetFileName(file));
}
list.ForEach(file => Console.WriteLine(file));


■LINQを使った例
var list = System.IO.Directory.GetFiles("C:\\Windows")
                    .Where(file => Path.GetExtension(file) == ".exe")
                    .Select(file => Path.GetFileName(file));
list.ToList().ForEach(file => Console.WriteLine(file));

GetFilesメソッドは、string[] を返します。 配列は、IEnumerable<T>を実装していますので、LINQが利用できるという訳ですね。

この例でも(今までのほとんどの例も)、LINQの子ーどについては、ToList()を呼び出す必要はないのですが、ループを使ったコードと、まったく同一の内容とするためにToList()を呼び出しています。

  
Posted by gushwell at 22:30Comments(0)TrackBack(0)

2015年07月01日

LINQ to Objectサンプル(16): 配列に連続した値を設定する

   このエントリーをはてなブックマークに追加 Clip to Evernote
配列に 1,2,3…20 と連続した数値を設定する例です。

■ループを使ったコード
int[] array = new int[20];
for (int i = 0; i < 20; i++) {
    array[i] = i + 1;
}
Array.ForEach(array, Console.WriteLine);


■LINQを使ったコード
var array = Enumerable.Range(1, 20).ToArray();
Array.ForEach(array, Console.WriteLine);

Enumerable.Rangeを使うと、連続した数値を生成できます。これをToArray()メソッドで配列に変換しています。
ToList()を使えばリストに変換できます。

ちなみに、Rangeメソッドの第2引数は、生成する数 count です。max値ではないので注意してください。
  
Posted by gushwell at 23:00Comments(0)TrackBack(0)