static void Main(string[] args)
{
var list = new[] { 1, 9, 5, 3, 7, 2 };
list.WriteLine();
list.QuickSort().WriteLine();
}
오랜만에 코딩이 들어간 포스팅 이네요.
잘 모르고 있었는데 C#에 Functional한 Feature가 많이 들어 갔더군요.
그래서 간단히 공부겸 테스트겸 가볍게 코드를 만들어 보고, 이를 공유 합니다.
만들어본 코드는 Quicksort이며, Main함수의 내용은 위와 같고,
실행한 결과는 아래와 같습니다.
{ 1, 9, 5, 3, 7, 2 }
{ 1, 2, 3, 5, 7, 9 }
먼저 첫 줄, 별 생각 없이 봐도 integer의 리스트를 만들었다는 것을 알 수 있습니다.
var list = new[] { 1, 9, 5, 3, 7, 2 };
다만, 자세히 보면 type이 전혀 선언되지 않았다는 것을 알 수 있습니다.
C# 3.0에서는 Implicitly typed local variables을 지원해서
javascript스러운 var a = 3 같은 문법을 지원합니다.
자세한 것은 문서를 참조 하시고,,,
list.WriteLine();
list.QuickSort().WriteLine();
두 번째부터 세 번째 줄을 살펴보면 list의 Object의 Method인 WriteLine()과 QuickSort()를 호출한 것을 볼 수 있습니다. 여기서 눈 여겨 볼 것은 QuickSort()와 Writeline()은 원래 있는 Method가 아니라 사용자가 만든 확장 Method라는 겁니다. 여기서 list variable의 Type은 Explicit하게 정의 하지 않았지만 C#에서 Type inference를 통해 Type이 결정되는데 여기에서는 int[]로 결정이 되겠지요. 즉, C#에서도 다른 Dynamic Language같이 기존에 존재하는 Type에도 확장 함수(Extension Methods)를 추가할 수 있다는 것 입니다.
실제 Sort부분의 코드를 보면…
public static IEnumerable<int> QuickSort(this IEnumerable<int> Ts)
{
return Ts.Case(
() => Ts,
(head, tail) =>
QuickSort(tail.Where(t => t < head))
.Concat(new[] { head })
.Concat(QuickSort(tail.Where(t => t >= head)))
);
}
여기서 head는 type이 int고, tail은 type이 int[]라는 것을 잘 봐야 합니다. C# 2.0에도 anonymous method가 있었는데 C# 3.0에서는 lambda expressions로 더욱 강화 되었습니다. Type inference feature가 추가 되면서 일일이 Parameters의 type을 적을 필요가 없게 된 것이 매우 큰 변화라 할 수 있을 것 같습니다. Javascript 등과 비교했을 때도, function(x) { return x; } 라고 지저분하게 적었어야 했는데, 이를 (x) => x 라고 적으면 된다니 코드를 작성하는 입장에서는 매우 반가운 일 입니다.
아무튼 나머지 코드는.
public static void WriteLine<T>(this IEnumerable<T> Ts)
{
Debug.Write("{ " + Ts.Head());
foreach (T t in Ts.Tail())
Debug.Write(", " + Convert.ToString(t));
Debug.WriteLine(" }");
}
public static T Head<T>(this IEnumerable<T> Ts)
{
foreach (T t in Ts)
return t;
throw new ArgumentException();
}
public static IEnumerable<T> Tail<T>(this IEnumerable<T> Ts)
{
bool head = true;
foreach (T t in Ts)
{
if (head) head = false;
else yield return t;
}
}
public static IEnumerable<T> Case<T>(this IEnumerable<T> Ts, Func<IEnumerable<T>> CaseEmpty, Func<T, IEnumerable<T>, IEnumerable<T>> CaseNotEmpty)
{
if (Ts.Count() > 0)
return CaseNotEmpty(Ts.Head(), Ts.Tail());
return CaseEmpty();
}
참조한 곳은..
C# is a functional programming language
------------
Methods를 좀 뒤져 봤는데.
Head() 는 First()로
Tail()은 Skip(1)로 대체할 수 있을 것 같네요.