2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

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

Last updated at Posted at 2025-06-14

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


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

📚 基本概念:値はどこに行く?

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

値 |> 関数

これは以下と同じ意味です:

関数(値)

🔄 値の流れの図解

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

より複雑な場合:

入力値 ──|>──→ 関数1 ──|>──→ 関数2 ──|>──→ 関数3 ──→ 最終出力
       ↓           ↓           ↓
     第1段階     第2段階     第3段階

🔧 具体的な値の追跡

// 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

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

// 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#の printfnsprintf などの関数では、% を使った書式指定子でデータ型を指定します。

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

// 基本的な書式指定子の例
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]"

🎨 高度な書式指定

// 複数の値を同時に出力
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%"

🔧 実践的な使用例

// データのフォーマット済み表示
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引数 戻り値

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

🔧 具体的な型注釈の例

// 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 // マウスオーバーで型が見える

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

// 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

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

// 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型での型システムの活用

// 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. 組み合わせ実習:値の流れを追跡する実践例

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

// デバッグ用ヘルパー関数
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. 実用的なパターンと値の流れ

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

// 文字列処理の各段階を追跡
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 の使い分けができる
    • パディングや桁数指定ができる
    • 複数の値を組み合わせた書式設定ができる

🔧 実践演習

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

// 最終確認用のコード
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#での値の流れと型システムの基本を理解し、実際のコードで確認することができます。

2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?