プログラミングについてのWiki。

C#で学ぶF#入門で使用する資料を書いていきます。

時間割

13:20 - 13:30F#の特徴
13:30 - 13:40IDEの使い方(C#)
13:40 - 13:50IDEの使い方(F#)
14:00 - 14:10ハローワールド
14:10 - 14:20条件式
14:20 - 14:30関数
14:30 - 14:50クラス(C#・F#共通)
15:00 - 15:10参照
15:10 - 15:30クロージャ
15:30 - 15:50ループ
16:00 - 16:20クラス(F#特有)
16:20 - 16:50判別共用体
17:00 - 17:30四則演算パーサ

コンセプト

  • F#の構文は見慣れないものだと思うので、C#と比較しながら構文に慣れることを中心にする。
  • 関数型特有の概念の説明には重点を置かないが、その導入になるようには意識する。
  • 一気に関数型に飛ばないで、ベターC#として慣れていくイメージ。
背景

F#が分かりにくいと感じる原因は、主に以下の2種類だと思う。
  1. 構文の異質さ(C系言語などと比較して)
  2. 関数型の考え方

今回は前者の壁に的を絞る。両者は完全に分離しているわけではないので、後者の領域も多少は言及する。個人的には構文が簡潔で短く書けるのが良いと思う。
バーチャート

独断と偏見の Java〜C#〜Scala〜F#〜OCaml〜Haskell バーチャートで今回の範囲(C#〜Scale間)を示す。

ハッシュタグ


ハッシュタグは #cs2fs です。
  • cs: C#
  • 2: to
  • fs: F#

ハローワールド

C#
using System;

class Test
{
    static void Main()
    {
        Console.WriteLine("Hello, World!");
    }
}
  • C#はMainメソッドが必須で、クラスで包む必要がある。
F#
printfn "Hello, World!"
  • F#は関数もクラスもなく、いきなり書き始めることができる。
F# Interactive
printfn "Hello, World!";;
  • F#のテストにfsiは必須
  • セミコロンに注意
  • F#のソースではセミコロンの必要ないlight構文がデフォルト
  • Visual Studio上で[Alt]+[Enter]の方法も説明。
    • 一行テストだと手軽で良いが、複数行だと選択が必要で面倒。

変数

C#
using System;

class Test
{
    static void Main()
    {
        var a = 1;
        Console.WriteLine("{0}", a);
    }
}
  • C#ではMainやクラスで包む必要がある。
F#
let a = 1
printfn "%d" a
  • F#はMainもクラスもなく、いきなり書ける。

代入と束縛

  • C#の可変で代入
  • F#は定数で束縛
  • 多重定義や値変更について書く
  • mutableや参照についても
  • 参照とbox化のことなど

関数

C#
using System;

class Test
{
    static void inc(int x)
    {
        return x + 1;
    }

    static void add(int x, int y)
    {
        return x + y;
    }

    static void Main()
    {
        Console.WriteLine("{0}", inc(1));
        Console.WriteLine("{0}", add(1, 2));
    }
}
F#
let inc = fun x -> x + 1
printfn "%d" (inc 1)
  • 関数の定義も変数の束縛と同列に行う。
    • cf. let a = 1
  • 関数単体で取り出した使い方はF# Interactiveで
F# Interactive
let inc = fun x -> x + 1;;
inc 1;;
(inc 1);;
inc(1);;
(fun x -> x + 1) 1;;
  • printfnを使わなくても良いので便利
  • 関数の呼び方は色々書けるけど、半ば遊び(要説明)
  • 最後の例は関数を束縛せずにインラインで使っている
F#
let add = fun x -> fun y -> x + y
printfn "%d" (add 1 2)
  • 2引数は高階関数でクロージャを含む
    • cf. fun x -> (fun y -> x + y)
F# Interactive
let add = fun x -> fun y -> x + y;;
add 1;;
  • これだけでカリー化の話をするのは説明不足過ぎ?
F#
let inc x = x + 1
let add x y = x + y
  • シンタックスシュガー
  • あまり深く追求しないで、よく分からなければこちらを使うのを推奨して良いかも

タプル

  • 複数の値を返すのが簡単。
  • Int.TryParse()のようなものが自動変換される。

前方参照

  • 進行方向と前方参照
  • ローカル変数の定義と同じ考え

for

  • ワンライナー: for i = 1 to 5 do printfn "%d" i
  • seq{1..2..5}のようなテクニック
    • {1..2..5}との違い(遅延評価)

戻り値

  • 暗黙的に捨てると警告される
  • C言語の (void)foo(); に倣って ignore <| foo() が良いのではないかと思う。
    • パイプライン演算子の説明のタイミングは?

クラス

  • protectedがない以外は、C#と同じことが書ける。
  • F#で推奨されないことは、書けるけど面倒にしてある印象。(あくまで推測)
  • レコード型と匿名クラスの比較。
  • valが面倒で、letはprivateだけとか、memberは前方参照OKとか。

引数の書き方


2通りの書き方ができる。

let test1 a b = a + b
let test2(a, b) = a + b
  • 厳密にはこの2つは異なるが、F#的には前者が推奨。
  • 前者はカリー化、後者はタプルと関係がある。深入りしないでさらっと流す。

引数の型推論


型情報が自己完結していないと外部(呼び出し側)から影響を受ける。

let test a b = a + b  // int -> int -> int
printfn "%d" (test 1 2)

let test a b = a + b  // string -> string -> string
printfn "%s" (test "a" "b")

let test a b = a + b  // int -> int -> int
printfn "%d" (test 1 2)
printfn "%s" (test "a" "b")  // エラー

let test a b = a + b  // string -> string -> string
printfn "%s" (test "a" "b")
printfn "%d" (test 1 2)  // エラー

予期しない型推論に引きずられる可能性があるため、引数には型情報を付けるのが無難。戻り値は型推論に任せても特に問題はない。

let test1 (a:int) (b:int) = a + b
let test2 (a:string) (b:string) = a + b

フィールド


valフィールドで初期化必須なのはC#の構造体に近い。別にvalを使ったから構造体になるわけではないが。

条件式

  • C#のif文よりも三項演算子に近い。unitの扱いについては要説明。
  • インデントで書くことも、一行に書くこともできる。柔軟な構文。

型推論

  • 変数名の上にマウスポインタを持って行って確認するのがコツ。

F# Tutorial


Wiki内検索

Menu

ここは自由に編集できるエリアです。

管理人/副管理人のみ編集できます