C# 3.0 と let キーワード

そういえば C# 3.0 にも let キーワードがあったなぁと思いだし let 書きたくなる病.

FizzBuzz

例えば id:siokoshou:20070714:p1 で書いた FizzBuzz で無理矢理 let を使ってみる.

var f1 = Enumerable.Range(1, 100).Select(
   n =>
   {
       if (0 == n % 15) return "FizzBuzz";
       if (0 == n % 5) return "Buzz";
       if (0 == n % 3) return "Fizz";
       return n.ToString();
   });

var f2 = from x in Enumerable.Range(1, 100)
         let a = x % 3 == 0 ? "Fizz" : ""
         let b = x % 5 == 0 ? "Buzz" : ""
         let c = (x % 3 != 0 && x % 5 != 0) ? x.ToString() : ""
         select a + b + c;

var check = Enumerable.SequenceEqual(f1, f2);

Console.WriteLine("f1 == f2 : {0}", check);

まあこれぐらいだとあんまり意味なさげ.

今年の1月1日から12月31日までを列挙

let が欲しくなるのは例えばこういうクエリかな.

var cal = from month in Enumerable.Range(1, 12)
          let year = DateTime.Today.Year
          let numDays = DateTime.DaysInMonth(year, month)
          from day in Enumerable.Range(1, numDays)
          select new DateTime(year, month, day);

2000年から2009年までの13日の金曜日は?

ちょっとそれらしい問題にしてみました.

var ft13 = from year in Enumerable.Range(2000, 10)
           let daysInThisYear =
               from month in Enumerable.Range(1, 12)
               let numDays = DateTime.DaysInMonth(year, month)
               from day in Enumerable.Range(1, numDays)
               select new DateTime(year, month, day)
           from date in daysInThisYear
           where date.Day == 13 && date.DayOfWeek == DayOfWeek.Friday
           select date;

foreach (var item in ft13)
{
    Console.WriteLine("{0}/{1}/{2}", item.Year, item.Month, item.Day);
}

まあひとつきの日数が13より少ないことはないので,いったん日にちまで列挙するのは余計なんですが,それがまたわざとらしい.

2000/10/13
2001/4/13
2001/7/13
2002/9/13
2002/12/13
2003/6/13
2004/2/13
2004/8/13
2005/5/13
2006/1/13
2006/10/13
2007/4/13
2007/7/13
2008/6/13
2009/2/13
2009/3/13
2009/11/13

2000年から2009年までの各年の日曜日の数

無理矢理ひとつのクエリで書いてみました.

var mysundays =
    from year in Enumerable.Range(2000, 10)
    let daysInThisYear =
        from month in Enumerable.Range(1, 12)
        let numDays = DateTime.DaysInMonth(year, month)
        from day in Enumerable.Range(1, numDays)
        select new DateTime(year, month, day)
    let sundaysInThisYear =
        from date in daysInThisYear
        where date.DayOfWeek == DayOfWeek.Sunday
        select date
    select new { Year = year, SundayCount = sundaysInThisYear.Count() };

foreach (var item in mysundays)
{
    Console.WriteLine("{0} => {1}", item.Year, item.SundayCount );
}

んー,さすがにこれぐらいになると関数定義してもよさげ.

Func<int, IEnumerable<DateTime>> daysInYear =
    year => from month in Enumerable.Range(1, 12)
            let numDays = DateTime.DaysInMonth(year, month)
            from day in Enumerable.Range(1, numDays)
            select new DateTime(year, month, day);

var mysundays =
    from year in Enumerable.Range(2000, 10)
    let sundays =
        from date in daysInYear(year)
        where date.DayOfWeek == DayOfWeek.Sunday
        select date
    select new { Year = year, SundayCount = sundays.Count() };

foreach (var item in mysundays)
{
    Console.WriteLine("{0} => {1}", item.Year, item.SundayCount );
}

2000 => 53
2001 => 52
2002 => 52
2003 => 52
2004 => 52
2005 => 52
2006 => 53
2007 => 52
2008 => 52
2009 => 52