おすすめ F# 本

なんか某所でまた F# が盛り上がっていますが,最近読んでいる F# 本が激しく良いのでご紹介.

Foundations of F# (Expert's Voice in .NET)

Foundations of F# (Expert's Voice in .NET)


何より素晴らしいのが,この手の技術書の中でも破格に読みやすい英語で書かれていることです.むしろ英語文章の教材にでもしたいぐらい.
おそらくこの本が一番向いているのは,.NET や C# をそこそこ知っている人が,関数型言語の入門書として最初に読んでみる,というケースでしょう.その用途だと『ふつうのHaskellプログラミング』よりもこっちの F# 本をおすすめします.
内容も豊富です.そして解説の順序がすばらしいので,分量が多いからといってそれほど苦にはなりません.細かい章立ては ここ で本文抜粋の PDF ファイルが公開されています.

CHAPTER 1 Introduction
CHAPTER 2 How to Obtain, Install, and Use F#
CHAPTER 3 Functional Programming
CHAPTER 4 Imperative Programming
CHAPTER 5 Object-Oriented Programming
CHAPTER 6 Organizing, Annotating, and Quoting Code
CHAPTER 7 The F# Libraries
CHAPTER 8 User Interfaces
CHAPTER 9 Data Access
CHAPTER 10 Distributed Applications
CHAPTER 11 Language-Oriented Programming
CHAPTER 12 The F# Tool Suite and .NET Programming Tools
CHAPTER 13 Compatibility and Advanced Interoperation

だいたい5章ぐらいまでで文法の基礎紹介がテンポ良く続き,第6章でメタプログラミングの顔見せ,第7章でライブラリの解説と続きます.その後は興味のあるところをつまみ食いしていくとよさげ.WinForms や,WPFWCF,LINQ との連携についても触れられています.
個人的には第11章がツボで,Quoting を使ったメタプログラミング (C# 3.0 の Expression Tree 生成機能のように,F# の式も Quote によって木構造をもったデータに変換できます) なんていう話が登場します.他にも簡易計算言語を作るために,文法から Parser を自動生成し,DynamicMethod を使って IL を出力したりという話も.
後は12章がツールの紹介,13章が相互運用で,たとえば F# で定義した Union を C# からどうやって使うかといった話が続きます.
とりあえず第1章がサンプルとして公開 されていますので,これだけ読んでみるというのもありでしょう.
また,微妙にそこそこ日本でも売れているのか Amazon.co.jp にはまだ在庫があるようです.いまボタンを押せば 2 名様限定ですぐに手に入る模様.
本の購入者向けの追加サービスとして $10 で PDF 版が販売されているので,読了後もプログラミング時のオンラインヘルプ代わりに使えます.
http://www.apress.com/promo/tendollars/
とまあこれから F# を始めようという人には是非おすすめの一冊でした.てかこれ,日本語訳に向けてあちこち動いてみてもいい本ですな.

avoid or solve?

バグは直すものか、治すものか。

ふとそんなことが思い浮かびました(^^ゞ

たぶん「直す」ですかねえ、あくまで物ですし。

これを読んで何となく思ったり.いや,オチはないんですが.
世の中,同じプログラマを名乗っていても,「よし,これで問題は回避されたぞ」という人と,「よし,これで問題は解けた」という人がいて,彼らの間にはなんだか大きい谷があるみたいなんですよね.
まあ違うのは「問題」の方なんでしょうが,なんとなく前向きか後ろ向きかも違いそうな気がします.
オチなし.
avoid が連発される関数型言語の本とか,斬新な鬱展開で話題になるやもしれませぬ.


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