0Pricing
Scala for Backend Engineering & Functional Programming · レッスン

クロージャ

状態をキャプチャします。

「クロージャ」はCoddyKit上の無料Scala for Backend Engineering & Functional Programmingレッスンです。 これはレッスン4/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはScala for Backend Engineering & Functional Programming学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 Scala for Backend Engineering & Functional Programmingコースには全4レッスンが含まれています。

クロージャとは

クロージャとは、定義された周囲のスコープから変数を取り込む関数です。これらの変数を「閉じ込める」ことで、そのスコープが終了した後も変数にアクセスできます。

object Main {
  def main(args: Array[String]): Unit = {
    val factor = 3
    val multiply = (x: Int) => x * factor
    println(multiply(5))
  }
}

自由変数の取り込み

x => x * factorでは、xはパラメーターですが、factorは外側のスコープから取得される自由変数です。関数はこの変数への参照を保持します。

object Main {
  def main(args: Array[String]): Unit = {
    val greeting = "Hello"
    val greet = (name: String) => s"$greeting, $name!"
    println(greet("Ann"))
    println(greet("Bob"))
  }
}

クロージャはスコープを超えて存続する

関数がクロージャを返すと、定義元の関数がすでに終了していても、取り込まれた変数はクロージャが存続する限り保持されます。

object Main {
  def makeAdder(n: Int): Int => Int = x => x + n
  def main(args: Array[String]): Unit = {
    val add10 = makeAdder(10)
    val add100 = makeAdder(100)
    println(add10(5))
    println(add100(5))
  }
}

各クロージャは独自に取り込む

関数ファクトリを2回呼び出すと、2つの独立したクロージャが生成され、それぞれが独自に取り込んだ値を記憶します。

object Main {
  def prefixer(p: String): String => String = s => p + s
  def main(args: Array[String]): Unit = {
    val withDr = prefixer("Dr. ")
    val withMr = prefixer("Mr. ")
    println(withDr("Smith"))
    println(withMr("Jones"))
  }
}

可変状態の取り込み

クロージャはvarを取り込み、その値を読み取ったり更新したりできます。これにより、カウンターのように、呼び出しの間で状態を保持できます。

object Main {
  def main(args: Array[String]): Unit = {
    var count = 0
    val increment = () => { count += 1; count }
    println(increment())
    println(increment())
    println(increment())
  }
}

カウンターファクトリ

ここまでの考え方を組み合わせると、独自のプライベートなvarを取り込むクロージャを返す関数によって、カプセル化された独立したカウンターを作成できます。

object Main {
  def counter(): () => Int = {
    var n = 0
    () => { n += 1; n }
  }
  def main(args: Array[String]): Unit = {
    val c1 = counter()
    val c2 = counter()
    println(c1())
    println(c1())
    println(c2())
  }
}

クロージャが取り込むのはコピーではなく参照

クロージャが取り込むのはスナップショットではなく変数です。取り込んだvarがクロージャの作成後に変化すると、クロージャからは新しい値が見えます。

object Main {
  def main(args: Array[String]): Unit = {
    var msg = "first"
    val show = () => println(msg)
    show()
    msg = "second"
    show()
  }
}

valの取り込みを優先する

不変のvalを取り込むと、取り込んだ値が変化しないため予期しない動作を避けられ、クロージャが予測どおりに動作します。関数型コードではvalの取り込みを優先してください。

object Main {
  def main(args: Array[String]): Unit = {
    val taxRate = 0.2
    val withTax = (price: Double) => price * (1 + taxRate)
    println(withTax(100))
  }
}

コレクションメソッドとクロージャ

mapやfilterなどに渡すラムダが外部の変数を参照している場合、それはクロージャです。Scalaでは日常的によく使われます。

object Main {
  def main(args: Array[String]): Unit = {
    val threshold = 3
    val nums = List(1, 2, 3, 4, 5)
    val big = nums.filter(_ > threshold)
    println(big)
  }
}

ループでの取り込みに関する注意点

Scalaでは、forループの変数は反復ごとに新しい束縛になるため、クロージャはそれぞれ異なる値を取り込みます。一部の他の言語で見られる典型的なバグを避けられます。

object Main {
  def main(args: Array[String]): Unit = {
    val fns = for (i <- 1 to 3) yield () => i
    println(fns.map(f => f()))
  }
}

クロージャは振る舞いとデータをカプセル化する

クロージャは、振る舞い(関数本体)とデータ(取り込んだ変数)をひとまとめにします。これは、メソッドを1つだけ持つ小さなオブジェクトの軽量な代替手段です。

object Main {
  def discounter(rate: Double): Double => Double = price => price * (1 - rate)
  def main(args: Array[String]): Unit = {
    val sale = discounter(0.25)
    println(sale(80))
    println(sale(200))
  }
}

クイックチェック

クロージャは周囲のスコープから何を取り込みますか。

まとめ

クロージャについて学びました。

  • クロージャは外側のスコープから自由変数を取り込みます
  • 取り込まれた変数は定義元の関数より長く存続します
  • 各クロージャのインスタンスは独自の取り込んだ状態を持ちます
  • クロージャが取り込むのは参照なので、可変な取り込み値は変化する可能性があります
  • mapやfilterに渡すラムダは日常的なクロージャです

よくある質問

「クロージャ」レッスンは無料ですか?

はい。「クロージャ」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、Scala for Backend Engineering & Functional Programmingコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 Scala for Backend Engineering & Functional Programmingコースには全4レッスンが含まれています。

「クロージャ」で何を学びますか?

状態をキャプチャします。 ブラウザで直接実行するハンズオンコードでScala for Backend Engineering & Functional Programmingを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。

Scala for Backend Engineering & Functional Programmingを始めるのに経験は必要ですか?

事前経験は必要ありません。CoddyKitのScala for Backend Engineering & Functional Programmingは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン4/4です。

「クロージャ」レッスンにはどのくらい時間がかかりますか?

ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。

このScala for Backend Engineering & Functional Programmingレッスンでコードを書いて実行できますか?

はい。すべてのScala for Backend Engineering & Functional Programmingレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。

このコースのすべてのレッスン

  1. 値としての関数
  2. カリー化
  3. 合成
  4. クロージャ
← Scala for Backend Engineering & Functional Programmingに戻る