F# 基本構文ハンズオンガイド


このガイドでは、F#の基本的な構文を、**値がどこに流れていくのか**を視覚的に理解できるように解説します。

---

## 1. パイプ演算子 `|>` の値の流れ

### 📚 基本概念：値はどこに行く？

パイプ演算子 `|>` は、左側の値を右側の関数の**最後の引数**として渡します。

```
値 |> 関数
```

これは以下と同じ意味です：
```
関数(値)
```

### 🔄 値の流れの図解

```
入力値 ──|>──→ 関数 ──→ 出力値
```

より複雑な場合：
```
入力値 ──|>──→ 関数1 ──|>──→ 関数2 ──|>──→ 関数3 ──→ 最終出力
       ↓           ↓           ↓
     第1段階     第2段階     第3段階
```

### 🔧 具体的な値の追跡

```fsharp
// 1. 単純な値の流れ
let number = 5
printfn "開始値: %d" number

let step1 = number |> (*) 2
//          ↑        ↑
//       値:5    関数:(* 2) → (* 2 5) → 2 * 5 = 10
printfn "ステップ1: 2 * %d = %d" number step1

// 2. 複数ステップでの値の追跡
let result = 
    10                    // 開始値: 10
    |> (+) 5             // 5 + 10 = 15 (注意: 5が左、10が右の引数)
    |> (*) 2             // 2 * 15 = 30
    |> (-) 100           // 100 - 30 = 70

printfn "計算過程の詳細:"
printfn "10 |> (+) 5   → 5 + 10 = 15"
printfn "15 |> (*) 2   → 2 * 15 = 30" 
printfn "30 |> (-) 100 → 100 - 30 = 70"
printfn "最終結果: %d" result

// 3. 関数の引数位置の重要性
let subtractFrom100 x = 100 - x
let subtract100From x = x - 100

let value = 30
let result1 = value |> subtractFrom100  // 100 - 30 = 70
let result2 = value |> subtract100From  // 30 - 100 = -70

printfn "%d |> subtractFrom100 = %d" value result1
printfn "%d |> subtract100From = %d" value result2
```

### 🔍 リスト処理での値の流れ

```fsharp
// 4. リスト処理での段階的変換
let numbers = [1; 2; 3; 4; 5]
printfn "初期リスト: %A" numbers

let step1 = numbers |> List.map (fun x -> x * 2)
//          ↑                    ↑
//    [1;2;3;4;5]        各要素を2倍する関数
//                              ↓
//                       [2;4;6;8;10]
printfn "2倍後: %A" step1

let step2 = step1 |> List.filter (fun x -> x > 4)
//          ↑                      ↑
//   [2;4;6;8;10]           4より大きい要素のみ
//                              ↓
//                        [6;8;10]
printfn "フィルター後: %A" step2

let step3 = step2 |> List.sum
//          ↑           ↑
//      [6;8;10]    合計を計算
//                      ↓
//                     24
printfn "合計: %d" step3

// 一連の流れを一つのパイプラインで
let finalResult = 
    [1; 2; 3; 4; 5]              // 開始: [1;2;3;4;5]
    |> List.map (fun x -> x * 2)  // 通過: [2;4;6;8;10]
    |> List.filter (fun x -> x > 4) // 通過: [6;8;10]
    |> List.sum                    // 終了: 24

printfn "パイプライン結果: %d" finalResult
```

---

## 2. printf系関数の書式指定子

### 📚 基本概念
F#の `printfn` や `sprintf` などの関数では、`%` を使った書式指定子でデータ型を指定します。

### 🔧 主要な書式指定子と実例

```fsharp
// 基本的な書式指定子の例
let intValue = 42
let floatValue = 3.14159
let stringValue = "F#"
let boolValue = true
let listValue = [1; 2; 3]

// %d : 整数
printfn "整数: %d" intValue
// → "整数: 42"

// %f : 浮動小数点数（デフォルト6桁）
printfn "浮動小数点数: %f" floatValue
// → "浮動小数点数: 3.141590"

// %.2f : 小数点以下2桁の浮動小数点数
printfn "小数点以下2桁: %.2f" floatValue
// → "小数点以下2桁: 3.14"

// %s : 文字列
printfn "文字列: %s" stringValue
// → "文字列: F#"

// %b : ブール値
printfn "ブール値: %b" boolValue
// → "ブール値: true"

// %A : 任意の型（自動的にフォーマット）
printfn "リスト: %A" listValue
// → "リスト: [1; 2; 3]"
```

### 🎨 高度な書式指定

```fsharp
// 複数の値を同時に出力
printfn "%s言語の数値: %d, 小数: %.3f" "F#" 100 2.71828
// → "F#言語の数値: 100, 小数: 2.718"

// パディング（桁揃え）の例
let numbers = [1; 12; 123; 1234]
printfn "右揃え5桁の例:"
numbers |> List.iter (fun n -> printfn "%5d円" n)
// →     1円
// →    12円  
// →   123円
// →  1234円

printfn "左揃え5桁の例:"
numbers |> List.iter (fun n -> printfn "%-5d円" n)
// → 1    円
// → 12   円
// → 123  円
// → 1234 円

// 16進数、8進数
let num = 255
printfn "10進数: %d" num      // → "10進数: 255"
printfn "16進数: %x" num      // → "16進数: ff" 
printfn "16進数(大文字): %X" num // → "16進数(大文字): FF"
printfn "8進数: %o" num       // → "8進数: 377"

// パーセント記号そのものを表示
printfn "進捗: %d%%" 75       // → "進捗: 75%"
```

### 🔧 実践的な使用例

```fsharp
// データのフォーマット済み表示
type Person = { Name: string; Age: int; Height: float }

let people = [
    { Name = "太郎"; Age = 25; Height = 175.5 }
    { Name = "花子"; Age = 30; Height = 162.3 }
    { Name = "次郎"; Age = 28; Height = 180.1 }
]

printfn "%-10s | %3s | %6s" "名前" "年齢" "身長"
printfn "-----------|-----|--------"

people
|> List.iter (fun p -> 
    printfn "%-10s | %3d | %6.1fcm" p.Name p.Age p.Height)

// 出力例:
// 名前       | 年齢 |   身長
// -----------|-----|--------
// 太郎       |  25 | 175.5cm
// 花子       |  30 | 162.3cm
// 次郎       |  28 | 180.1cm
```

---

## 3. 関数の型注釈と矢印演算子 `->` の理解

### 📚 基本概念：関数の型の読み方

矢印演算子 `->` は、F#で関数の型を表現するために使用されます。

```
引数の型 -> 戻り値の型
```

複数の引数がある場合：
```
引数1の型 -> 引数2の型 -> 戻り値の型
```

### 🔍 型の読み方の図解

```
int -> int -> int
 ↑     ↑     ↑
第1引数 第2引数 戻り値
```

これは「整数を受け取って、整数を受け取って、整数を返す関数」という意味です。

### 🔧 具体的な型注釈の例

```fsharp
// 1. 明示的な型注釈
let add (x: int) (y: int) : int = x + y
//       ↑        ↑        ↑
//    第1引数   第2引数   戻り値の型
// この関数の型は: int -> int -> int

let greet (name: string) : string = 
    sprintf "こんにちは、%sさん！" name
// この関数の型は: string -> string

printfn "add 5 3 = %d" (add 5 3)
printfn "%s" (greet "太郎")

// 2. 型推論（F#が自動的に型を判断）
let subtract x y = x - y  
// F#が自動的に int -> int -> int と推論

let divide x y = x / y    
// F#が自動的に int -> int -> int と推論

// 型を確認したい時のテクニック
// let checkType = subtract // マウスオーバーで型が見える
```

### 🎯 関数を値として扱う場合の型

```fsharp
// 3. 関数を引数として受け取る関数
let applyTwice (f: int -> int) (x: int) : int = f (f x)
//              ↑              ↑          ↑
//         関数型の引数      整数引数    戻り値
// 全体の型: (int -> int) -> int -> int

let double x = x * 2   // int -> int
let square x = x * x   // int -> int

// applyTwiceの動作例
let result1 = applyTwice double 5
//            ↑        ↑      ↑
//          関数    関数引数  値引数
// 実行過程: double(double(5)) = double(10) = 20

let result2 = applyTwice square 3
// 実行過程: square(square(3)) = square(9) = 81

printfn "5を2回doubleした結果: %d" result1
printfn "3を2回squareした結果: %d" result2

// 4. 関数を返す関数
let makeAdder (n: int) : (int -> int) = 
    fun x -> x + n
//  ↑        ↑
// 引数     戻り値（これも関数）
// 型: int -> (int -> int)

// 使用例と動作過程
let addFive = makeAdder 5
//   ↑           ↑      ↑
// 結果の関数  元の関数  引数5
// addFiveの型は int -> int

let result = addFive 7
//    ↑        ↑     ↑
//  結果12   関数   引数7
// 実行過程: (fun x -> x + 5) 7 = 7 + 5 = 12

printfn "addFive 7 = %d" result
```

### 🔧 パーシャルアプリケーション（部分適用）の仕組み

```fsharp
// 5. パーシャルアプリケーションの詳細
let addThreeNumbers x y z = x + y + z
// 型: int -> int -> int -> int

// F#では実際には以下のように解釈される
// let addThreeNumbers x = fun y -> fun z -> x + y + z

// 段階的に引数を適用
let step1 = addThreeNumbers 5
// step1の型: int -> int -> int
// step1 = fun y -> fun z -> 5 + y + z

let step2 = step1 3
// step2の型: int -> int  
// step2 = fun z -> 5 + 3 + z

let step3 = step2 2
// step3の型: int
// step3 = 5 + 3 + 2 = 10

printfn "段階的適用の結果: %d" step3

// より実用的な例：文字列フォーマッター
let createFormatter prefix suffix content = 
    sprintf "%s%s%s" prefix content suffix
// 型: string -> string -> string -> string

// 部分適用でカスタマイズされた関数を作成
let htmlBold = createFormatter "<b>" "</b>"
// htmlBoldの型: string -> string
// htmlBold = fun content -> sprintf "<b>%s</b>" content

let htmlItalic = createFormatter "<i>" "</i>"
// htmlItalicの型: string -> string

// 使用例
let boldText = htmlBold "太字テキスト"
let italicText = htmlItalic "斜体テキスト"

printfn "太字: %s" boldText      // → "太字: <b>太字テキスト</b>"
printfn "斜体: %s" italicText    // → "斜体: <i>斜体テキスト</i>"
```

### 🎨 Option型での型システムの活用

```fsharp
// 6. Option型を返す関数
let safeDivide (x: float) (y: float) : float option =
    if y <> 0.0 then Some(x / y) else None
// 型: float -> float -> float option

// Option.mapの型と動作
// Option.map : ('a -> 'b) -> 'a option -> 'b option

let processNumber input =
    input
    |> safeDivide 100.0        // float -> float option
    |> Option.map (fun x -> x * 2.0)  // float option -> float option  
    |> Option.map (sprintf "結果: %.1f")  // float option -> string option
    |> Option.defaultValue "計算失敗"     // string option -> string

// 値の流れの詳細
let testInput = 4.0
printfn "入力: %f" testInput

let step1 = safeDivide 100.0 testInput  // Some(25.0)
printfn "100.0 / %f = %A" testInput step1

let step2 = step1 |> Option.map (fun x -> x * 2.0)  // Some(50.0)  
printfn "2倍: %A" step2

let step3 = step2 |> Option.map (sprintf "結果: %.1f")  // Some("結果: 50.0")
printfn "フォーマット: %A" step3

let final = step3 |> Option.defaultValue "計算失敗"  // "結果: 50.0"
printfn "最終: %s" final
```

---

## 4. 組み合わせ実習：値の流れを追跡する実践例

### 🔧 実践プロジェクト：計算パイプラインの詳細追跡

```fsharp
// デバッグ用ヘルパー関数
let trace label value =
    printfn "[TRACE] %s: %A" label value
    value  // 重要：値をそのまま返すのでパイプが継続される

// 計算関数群
let add x y = 
    let result = x + y
    printfn "  %d + %d = %d" x y result
    result

let multiply x y = 
    let result = x * y
    printfn "  %d × %d = %d" x y result
    result

let safeDivide x y = 
    if y <> 0 then 
        let result = x / y
        printfn "  %d ÷ %d = %d" x y result
        Some result
    else 
        printfn "  %d ÷ %d = エラー（ゼロ除算）" x y
        None

// 複雑な計算パイプライン
let complexCalculation input =
    printfn "\n=== 計算開始（入力: %d）===" input
    
    input
    |> trace "開始値"
    |> add 10
    |> trace "10を加算後"
    |> multiply 2  
    |> trace "2倍後"
    |> fun x -> x - 5
    |> trace "5を減算後"
    |> fun x -> safeDivide x 3
    |> trace "3で除算後"

// 実行例
let testValue = 5
match complexCalculation testValue with
| Some result -> printfn "✓ 最終結果: %d" result
| None -> printfn "✗ 計算エラー"

// バッチ処理での値の流れ
let testValues = [1; 5; 10; 15]
printfn "\n=== バッチ処理 ==="

testValues
|> trace "入力リスト"
|> List.map complexCalculation
|> trace "計算結果リスト"  
|> List.choose id  // Option型からSome値のみ抽出
|> trace "成功した結果のみ"
|> List.sum
|> trace "合計"
|> printfn "全体の合計: %d"
```

---

## 5. 実用的なパターンと値の流れ

### 🔄 文字列処理パイプライン

```fsharp
// 文字列処理の各段階を追跡
let processText input =
    printfn "\n=== 文字列処理（入力: \"%s\"）===" input
    
    input
    |> trace "原文"
    |> fun s -> s.Trim()
    |> trace "空白削除後"
    |> fun s -> s.ToLower()  
    |> trace "小文字変換後"
    |> fun s -> s.Replace(" ", "_")
    |> trace "スペース置換後"
    |> fun s -> sprintf "processed_%s" s
    |> trace "接頭辞追加後"

let result = processText "  Hello World  "
printfn "最終結果: %s" result

// リスト処理での変換追跡
let processNumbers numbers =
    printfn "\n=== 数値リスト処理 ==="
    
    numbers
    |> trace "元のリスト"
    |> List.filter (fun x -> x > 0)
    |> trace "正数のみ"
    |> List.map (fun x -> x * x)
    |> trace "二乗後"
    |> List.filter (fun x -> x < 100)
    |> trace "100未満のみ"
    |> List.sort
    |> trace "ソート後"

let numbers = [-3; -1; 2; 5; 8; 12; 15]
let processed = processNumbers numbers
printfn "処理完了: %A" processed
```

---

## 🎯 学習確認チェックリスト

### ✅ 理解度チェック

以下の概念を理解できているか確認してください：

1. **パイプ演算子の値の流れ**
   - [ ] `値 |> 関数` で値がどの引数位置に入るか分かる
   - [ ] 複数のパイプで値がどう変換されていくか追跡できる
   - [ ] パーシャルアプリケーションとパイプの関係が分かる

2. **型システムの読み方**  
   - [ ] `int -> int -> int` のような型注釈を読める
   - [ ] 関数の引数と戻り値の型が分かる
   - [ ] Option型などの複合型の型注釈が分かる

3. **書式指定子の使い分け**
   - [ ] %d, %f, %s, %A の使い分けができる
   - [ ] パディングや桁数指定ができる
   - [ ] 複数の値を組み合わせた書式設定ができる

### 🔧 実践演習

以下のコードを実行して、各段階での値の変化を確認してください：

```fsharp
// 最終確認用のコード
let finalChallenge input =
    printfn "=== 最終チャレンジ（入力: %d）===" input
    
    input
    |> fun x -> 
        printfn "段階1: %d → %d" x (x * 2)
        x * 2
    |> fun x -> 
        let result = x + 10
        printfn "段階2: %d + 10 → %d" x result  
        result
    |> fun x ->
        if x > 50 then
            printfn "段階3: %d > 50なので半分にする → %d" x (x / 2)
            Some(x / 2)
        else
            printfn "段階3: %d <= 50なので処理終了" x
            None
    |> function
        | Some value -> sprintf "成功: %d" value
        | None -> "処理なし"
    |> printfn "最終結果: %s"

// テスト実行
[10; 25; 40] |> List.iter finalChallenge
```

このガイドを通して、F#での値の流れと型システムの基本を理解し、実際のコードで確認することができます。