Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Foreach

💡 お知らせ: このドキュメントはAIによって翻訳されています。表現に違和感がある場合は、原文(英語)を参照するか、翻訳にご協力ください。

Flix は、コレクションを命令的に反復するための、伝統的な foreach 構文をサポートしています。

foreach 構文は通常、1つ以上のコレクションを反復し、それぞれの要素に対してエフェクトを伴う操作を実行したいときに使います。

たとえば、次のプログラム:

def main(): Unit \ IO = 
    let fruits = List#{"Apple", "Pear", "Mango"};
    foreach (fruit <- fruits) 
        println(fruit)

は、文字列 ApplePearMango を出力します。

複数のコレクションを反復することもできます:

def main(): Unit \ IO = 
    let fruits = List#{"Apple", "Pear", "Mango"};
    let creams = List#{"Vanilla", "Stracciatella"};
    foreach (fruit <- fruits) 
        foreach (cream <- creams)
            println("Would you like some ${fruit} with ${cream} icecream?")

同じループは、次のようにも書けます:

def main(): Unit \ IO = 
    let fruits = List#{"Apple", "Pear", "Mango"};
    let creams = List#{"Vanilla", "Stracciatella"};
    foreach (fruit <- fruits; cream <- creams) 
        println("Would you like some ${fruit} with ${cream} icecream?")

フィルタ付きのループを書くこともできます。たとえば:

def main(): Unit \ IO = 
    let fruits = List#{"Apple", "Pear", "Mango"};
    let creams = List#{"Vanilla", "Stracciatella"};
    foreach (fruit <- fruits; if isExcotic(fruit); cream <- creams) 
        println("Would you like some ${fruit} with ${cream} icecream?")

def isExcotic(fruit: String): Bool = match fruit {
    case "Mango" => true
    case _       => false
}

見やすさのための波括弧の追加(省略可能)

foreach 式は、波括弧を追加することで見やすさを向上できる場合があります:

def main(): Unit \ IO = 
    let fruits = List#{"Apple", "Pear", "Mango"};
    let creams = List#{"Vanilla", "Stracciatella"};
    foreach (fruit <- fruits) {
        foreach (cream <- creams) {
            println("Would you like some ${fruit} with ${cream} icecream?")
        }
    }

波括弧は foreach ループの意味には一切影響しません。純粋にスタイル上のものです。

ForEach トレイト

foreach 構文は、ForEach トレイトを実装している任意のコレクション型を反復するために使えます。具体的には、ForEach トレイトは1つのシグネチャを定義しています:

///
/// forEach 操作をサポートするデータ構造のためのトレイト。
///
trait ForEach[t] {

    ///
    /// データ構造内の要素の型。
    ///
    type Elm: Type

    ///
    /// `forEach` のエフェクト。
    ///
    type Aef: Eff = {}

    ///
    /// データ構造内の各要素に `f` を適用します。
    ///
    pub def forEach(f: ForEach.Elm[t] -> Unit \ ef, t: t): Unit \ (ef + ForEach.Aef[t])

}

注意: Flix は、foreach の本体となる式が Unit 型であることを期待します。

ForEach コンビネータ

ForEach モジュールは、コレクションの反復方法を変換する4つのコンビネータ withIndexwithFilterwithMapwithZip を提供しています。各コンビネータはコレクションをラップし、foreach 構文で直接使える新しい ForEach 互換の値を返します。

インデックス付きの反復

withIndex コンビネータは、各要素を0始まりのインデックスと組にします:

use ForEach.withIndex;
def main(): Unit \ IO =
    let langs = List#{"Flix", "Haskell", "Scala"};
    foreach ((i, lang) <- withIndex(langs)) {
        println("${i}: ${lang}")
    }

これは次を出力します:

0: Flix
1: Haskell
2: Scala

要素のフィルタリング

withFilter コンビネータは、述語を満たさない要素をスキップします:

use ForEach.withFilter;
def main(): Unit \ IO =
    let numbers = List#{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
    foreach (x <- withFilter(x -> x `Int32.modulo` 2 == 0, numbers)) {
        println("${x}")
    }

これは偶数のみ、すなわち 246810 を出力します。

要素のマッピング

withMap コンビネータは、各要素が生成(yield)される前に変換を適用します:

use ForEach.withMap;
def main(): Unit \ IO =
    let numbers = List#{1, 2, 3, 4, 5};
    foreach (x <- withMap(x -> x * 10, numbers)) {
        println("${x}")
    }

これは 1020304050 を出力します。

2つのコレクションの zip

withZip コンビネータは、2つのコレクションを要素ごとに zip し、組を生成します。反復は短い方のコレクションが尽きた時点で停止します:

use ForEach.withZip;
def main(): Unit \ IO =
    let names = List#{"Alice", "Bob", "Carol"};
    let ages = List#{30, 25, 40};
    foreach ((name, age) <- withZip(names, ages)) {
        println("${name} is ${age} years old")
    }

注意: withZip は、両方のコレクションが(ForEach だけでなく)Iterable トレイトを実装していることを必要とします。