【C#】System.Linq.Enumerable.GroupByを絵で表現してみた。
- 2012.04.07 Saturday
- 20:07
そういえば使ったことなかったのでコードを書いてみた。
using System;
using System.Threading.Tasks;
namespace ParallelForDemo20110524_001
{
class Program
{
static void Main(string[] args)
{
Parallel.For(0, 100, Console.WriteLine);
}
}
}
具体的な実行結果は予想できないけど、思った通りに動いてるみたい。
class Program
{
static void Main()
{
var list = new MyList<int> { 1, 2, 3 };
IEnumerator current = list.GetEnumerator();
while (current.MoveNext())
{
Console.WriteLine(current.Current);
}
}
}
public class MyList<T> : IEnumerable<T>
{
private ListNode<T> _first;
private ListNode<T> _last;
public void Add(T value)
{
var node = new ListNode<T> { Value = value };
if (_first == null)
_first = _last = node;
else
_last = _last.Next = node;
}
private IEnumerable<T> GetEnumerable()
{
for (ListNode<T> currentNode = _first; currentNode != null; currentNode = currentNode.Next)
{
yield return currentNode.Value;
}
}
public IEnumerator<T> GetEnumerator()
{
return GetEnumerable().GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerable().GetEnumerator();
}
}
class ListNode<TK>
{
public TK Value { get; set; }
public ListNode<TK> Next { get; set; }
}
上記のコードはhttp://pro.art55.jp/?eid=1303805で紹介したコードを動的なものから静的なものに変換して、さらにIEnumerableを継承させたものです。一部メソッドが消えていたりして微妙に違うところもありますが、大体同じです。
このコードを書き終えて気づきました。IEnumerator インターフェースを継承したクラスを自分で実装していないことに・・・。「あれ?実装しなくてもいけるの?」と、狐につままれた気分です。そもそも元々書いてたコードにはIEnumerableを継承したクラスすらなかった訳ですが・・・・
yield return当たりがなんだかがんばってくれているのだとは思いますが、狐はどこにいるんでしょうか・・・調べる価値がありそうです。
コンパイル後のILをみるとかyield returnを使わずに記述してみるとか
そのへんを探ってみたら答えが出そうな気が済ます(やってませんが・・・)
以前の投稿を探してみるとListを自作しているコードを見つけました。
http://pro.art55.jp/?eid=1175888
using System.Collections;
using System.Collections.Generic;
namespace Art55.Lib
{
public class C<T> : IEnumerable<T>
{
public void Add(T value)
{
ListNode<T> tail = ListItemUtil<T>.GetTail(_head);
if (_head == null)
_head = new ListNode<T>(value);
else
tail.Next = new ListNode<T>(value);
}
public IEnumerator<T> GetEnumerator()
{
return new E<T>(_head);
}
IEnumerator IEnumerable.GetEnumerator()
{
foreach (var item in this)
yield return item;
}
private ListNode<T> _head;
}
internal class E<T> : IEnumerator<T>
{
public E(ListNode<T> item)
{
_top = item;
}
public bool MoveNext()
{
if (_current == null)
Reset();
else
_current =_current.Next;
return _current != null;
}
public void Reset()
{
_current = _top;
}
public T Current
{
get { return _current.Value; }
}
object IEnumerator.Current
{
get { return Current; }
}
public void Dispose()
{
}
private readonly ListNode<T> _top;
private ListNode<T> _current;
}
internal class ListNode<T>
{
public ListNode(T value)
{
Value = value;
}
internal protected ListNode<T> Next { get; set; }
public T Value { get; private set; }
}
static class ListItemUtil<T>
{
public static ListNode<T> GetTail(ListNode<T> head)
{
if (head == null)
return null;
while (head.Next != null)
head = head.Next;
return head;
}
}
}
internal class E<T> : IEnumerator<T>を一生懸命自作しているわけですが、実はいらなかったんですね。
最近よくTupleにお世話になっています。
Tupleの実装自体さほど難しくないとは思うのですが
標準ライブラリーとして、もっと早く組み込まれていればなーって思ったりしてます。
知恵がたりなかった為
キーとなる情報をわざわざ構造体やクラスを作ったり
複数キーを文字列で連結させ、キーがぶつからないかな−ってどきどきしてみたり
アホな事をしてたなーって今更ながらに思ってます。
あいわからずDictionary<T, K>をラップしたDynamicObjecctを使用してプログラミングを
してみたぞ(遊んでみた)という内容を投稿したいと思います。
今日、作ろうと思うのは下記のような構造をもつオブジェクトです。
クラスを定義せずに、プログラミングをしようとした場合に
一番に考えたのが、このようなオブジェクト図(もどき)でした。
上記は4つのノードを追加した状況にある場合のスナップショットです。
これにたいしてクライアント(ProgramクラスのMainの処理)は
今回は2つのメッセージを送りたいと思っています。
1. 値を追加する
2. 列挙する
Mainの処理を書いてみると
static void Main(string[] args)
{
dynamic list = CreateList();
list.Add(1);
list.Add(2);
list.Add(3);
foreach (int value in list.GetEnumerable())
{
Console.WriteLine(value);
}
}
このコードがは私の希望です。
前回までは、下記のように書いてました。
static void Main(string[] args)
{
dynamic list = CreateList();
Add(list, 1);
Add(list, 2);
Add(list, 3);
foreach (int value in GetEnumerable(list))
{
Console.WriteLine(value);
}
}
listオブジェクトにメッセージを送りたいので
なるべくなら
Add(list, 1);
ではなく
list.Add(1);
と記述したいわけです。要するにカリー化したいんです(←よくわからない言葉を使うのは気がひけます)
list.Method(...)と記述するといかにもオブジェクトがもつメソッドを呼び出したように見えます。
是非とも記述したい。
そこで、今回は一工夫いれながらコードを書いてみました。
using System;
using System.Collections;
using System.Collections.Generic;
using System.Dynamic;
namespace Art55.DynamicNamedPropertyObjectDemo001
{
class Program
{
static void Main()
{
dynamic list = CreateList();
list.Add(1);
list.Add(2);
list.Add(3);
foreach (int value in list.GetEnumerable())
{
Console.WriteLine(value);
}
}
static dynamic CreateList()
{
dynamic list = NamedDynamicObject.Create();
list.First = null;
list.Last = null;
list.Add = new Action<object>(value => Add(list, value));
list.GetEnumerable = new Func<IEnumerable>(() => GetEnumerable(list));
return list;
}
static void Add(dynamic list, object value)
{
dynamic node = CreateListNode(value);
if (list.First == null)
list.First = list.Last = node;
else
list.Last = list.Last.Next = node;
}
static IEnumerable GetEnumerable(dynamic list)
{
for (dynamic currentNode = list.First; currentNode != null; currentNode = currentNode.Next)
{
yield return currentNode.Value;
}
}
static dynamic CreateListNode(object value)
{
dynamic node = NamedDynamicObject.Create();
node.Value = value;
node.Next = null;
return node;
}
}
public class NamedDynamicObject : DynamicObject
{
public static dynamic Create()
{
return new NamedDynamicObject();
}
protected NamedDynamicObject()
{
_setting = new Dictionary<string, object>();
}
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
return _setting.TryGetValue(binder.Name, out result);
}
public override bool TrySetMember(SetMemberBinder binder, object value)
{
_setting[binder.Name] = value;
return true;
}
private readonly Dictionary<string, object> _setting;
}
}
CreateList()メソッドでListの能力を全て構築させます。こうすることで、CreateListの戻り値のオブジェクトはすでにListな振る舞いをするオブジェクトとなっているわけです。CreateList()
メソッドが呼ばれたあとの処理は、全てlistにたいしてメッセージを送るだけで、要求が満たされるようになるわけです。こうすることでListオブジェクトが管理しているListNode達の存在や構造を意識することもなくりました。
心残りは
list.Add = new Action<object>((value) => Add(list, value));
こういうのが、もう少しわかりやすくかけないかなーってところです。
以上
---------------------
クラスに書き換えたヴァージョン
class Program
{
static void Main()
{
var list = new List();
list.Add(1);
list.Add(2);
list.Add(3);
foreach (int value in list.GetEnumerable())
{
Console.WriteLine(value);
}
}
}
public class List // : IEnumerable
{
private ListNode _first;
private ListNode _last;
public void Add(object value)
{
dynamic node = new ListNode {Value = value};
if (_first == null)
_first = _last = node;
else
_last = _last.Next = node;
}
public IEnumerable GetEnumerable()
{
for (ListNode currentNode = _first; currentNode != null; currentNode = currentNode.Next)
{
yield return currentNode.Value;
}
}
class ListNode
{
public object Value { get; set; }
public ListNode Next { get; set; }
}
//public IEnumerator GetEnumerator()
//{
// return GetEnumerable().GetEnumerator();
//}
}
昨日に引き続きDynamicObjectを利用して、遊んでみました。
実行時にdynamicを利用してあたかもLinkedListオブジェクトのような
振る舞いを示すオブジェクトを生成するコードを書いてみました。
using System;
using System.Collections;
using System.Collections.Generic;
using System.Dynamic;
using System.Linq;
using System.Text;
namespace DynamicDemo20110511_001
{
class Program
{
static void Main(string[] args)
{
dynamic linkedList = CreateLinkedList();
AddLinkedListNode(linkedList, 1);
AddLinkedListNode(linkedList, 2);
AddLinkedListNode(linkedList, 3);
AddLinkedListNode(linkedList, 4);
Console.WriteLine("前方から");
foreach (dynamic value in GetValuesForward(linkedList))
{
Console.WriteLine(value);
}
Console.WriteLine("後方から");
foreach (dynamic value in GetValuesBack(linkedList))
{
Console.WriteLine(value);
}
}
static IEnumerable GetValuesForward(dynamic linkedList)
{
for (dynamic current = linkedList.First; current != null; current = current.Next)
{
yield return current.Value;
}
}
static IEnumerable GetValuesBack(dynamic linkedList)
{
for (dynamic current = linkedList.Last; current != null; current = current.Previous)
{
yield return current.Value;
}
}
static dynamic CreateLinkedList()
{
dynamic linkedList = NamedDynamicObject.Create();
linkedList.First = null;
linkedList.Last = null;
return linkedList;
}
static void AddLinkedListNode(dynamic linkedList, dynamic value)
{
dynamic newItem = NamedDynamicObject.Create();
newItem.Value = value;
newItem.Previous = linkedList.Last;
newItem.Next = null;
if (linkedList.Last != null)
linkedList.Last.Next = newItem;
linkedList.Last = newItem;
if (linkedList.First == null)
linkedList.First = linkedList.Last;
}
}
public class NamedDynamicObject : DynamicObject
{
public static dynamic Create()
{
return new NamedDynamicObject();
}
protected NamedDynamicObject()
{
_setting = new Dictionary<string, object>();
}
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
return _setting.TryGetValue(binder.Name, out result);
}
public override bool TrySetMember(SetMemberBinder binder, object value)
{
_setting[binder.Name] = value;
return true;
}
private readonly Dictionary<string, object> _setting;
}
}
LinkListクラスとLinkedListNodeクラスの実装をどうやって実装すればいいか
クラスを定義せずに必要となるであろう処理のみを記述することできあがりました。
なかなかおもしろかったです(面白いだけです)
Source and Project
protected NamedDynamicObject()
{
_setting = new Dictionary<string, object>();
}
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
return _setting.TryGetValue(binder.Name, out result);
}
public override bool TrySetMember(SetMemberBinder binder, object value)
{
_setting[binder.Name] = value;
return true;
}
private readonly Dictionary<string, object> _setting;
}
このオブジェクトをdynamicで受けることにより
クラスで定義されていないメンバーへアクセスしているようなコードが記述出来る。
使用例は
class Program
{
static void Main(string[] args)
{
dynamic top = NamedDynamicObject.Create();
top.Value = 1;
top.Next = NamedDynamicObject.Create();
top.Next.Value = 2;
top.Next.Next = NamedDynamicObject.Create();
top.Next.Next.Value = 3;
top.Next.Next.Next = top;
top.Print = new Action<dynamic>(PrintList);
top.Print(top);
}
static void PrintList(dynamic top)
{
dynamic current = top;
for (int i = 0; i < 10; i++)
{
Console.WriteLine(current.Value);
current = current.Next;
}
}
}
特に意味のあるコードではないが、NamedDynamicObjectクラスで定義されていないメンバーに
アクセスして値やインスタンスが設定されている。
デリゲートを利用して、メンバー経由でデリゲートを実行してみた。
(TryInvokeMemberメソッドを利用すればメソッドのオーバーロードも実現できそう)
オブジェクトにたいして全て外部から定義をしてオブジェクトを操作する。
なんだかともて不思議な感じです。
Source and Project
Visual Studio 2010 and .NET Framework 4.0 Training Kit - November Previewが公開されています。
引用元
もり ひろゆきの日々是勉強
http://blogs.wankuma.com/hirom/archive/2008/11/13/161272.aspx