Ken Wakita (https://is-prg1b.github.io/lecture/)
2017.11.14
Future {
処理内容
計算結果: T
} : Future[T]
Future オブジェクト:どこかで計算を実行し,いずれ計算が終わった暁には,その答えをくれるオブジェクト.
計算結果の型が T
のとき,Future オブジェクトの型は Future[T]
Future オブジェクトの foreach
メソッドを介して,実行結果を入手する.
f()
Future { g() }
h()
f()
を実行する.
g()
を計算する Future オブジェクトを生成する.
計算資源に余裕がある場合は(つまり,暇なプロセッサがあるとき)Future オブジェクトはすぐに g()
の計算を開始する.
(Future オブジェクトの計算を待たずに)h()
の計算を始める.
計算資源に余裕があり,g()
とh()
の計算が重い場合はこれらの二つの計算は並列実行される.
(* project lx12; run future*)
def future(): Unit = {
val s = "Hello"
val f: Future[String] = Future { List(s, " future!").reduce((s1, s2) => s1 + s2) }
println(f"$s + ...: ${Await.result(f, Duration.Inf)}")
}
Future f
は文字列のリストを連結する.
println
メソッド中の Await.result(f, 最大待ち時間)
が Future との同期と値の授受を実施する.
Await.result(f, ...)
の計算を待つ
Await.result(..., Duration.Inf)
計算が終るまで無限に待ち続ける
Await.result(...)
の結果は f
の計算結果
ふたつの Future (f1
と f2
) の並列実行を考える.
最終的には f1
と f2
の計算結果を用いて計算したい.たとえば,分割統治法で大きな問題を複数の部分問題に分割したときに,それぞれの部分問題を Future として独立に並列計算し,それらの結果を合成して最終結果を得たい.
for { v1 <- f1; ... } yield ...
) project lx12; run add
def add() {
val f1: Future[Int] = Future { 1 }
val f2: Future[Int] = Future { 2 }
val sum: Future[Int] = for {
v1 <- f1
v2 <- f2
} yield (v1 + v2)
println(f"1 + 2 = ${Await.result(sum, Duration.Inf)}")
}
f1
, f2
が Future のとき for { v1 <- f1; v2 <- f2 } yield ...
によって,複数の Future との同期と値の受理を記述できる.
yield ...
によって,f1
と f2
の結果を合成した Future の計算を表現する....
が合成した Future の計算の内容.
def add_zip() {
val f1: Future[Int] = Future { 1 }
val f2: Future[Int] = Future { 2 }
val (v1, v2) = Await.result(f1.zip(f2), Duration.Inf)
println(f"1 + 2 = ${v1 + v2}")
}
Future[T]::zip[U](that: Future[U]): Future[(T, U)]
f1:Future[T]
, f2:Future[U]
のとき,f1.zip(f2)
は Future[(T, U)]
を返す.
つまり,ふたつの Future の計算結果(それぞれの型は T, U)から,それらの組(型は (T, U))を計算するFutureを返す.
Await.result(f1.zip(f2), ...)
は zip
で合成した Future の計算を待って,f1
, f2
の結果の組を待つ.
Promise[T]
は Future[T]
型の Future オブジェクトの計算結果を保持するオブジェクト.
Future にくっついている覗き穴のようなもの
Future 計算が完了していないときは,Promise の値は未了 (p.isComplete == false
)
Future 計算が完了していたら Promise の値は Success[T]
Future 計算が途中でコケていたら Promise の値は Failure[T]
Promise p
が割り当てられている Future は p.future
Future が Promise に値を通知する方法は p success 計算結果
// project lx12; run promise
def promise(): Unit = {
val s = "Hello"
val p = Promise[String]()
Future { p success List(s, " future!").reduce((s1, s2) => s1 + s2) }
println(f"Value from promise: ${Await.result(p.future, Duration.Inf)}")
}
Promise p
を作成
Future を作成し,計算 (List(...).reduce(...)
) の結果を p success ...
でプロミスに通知
Await.result(p.future, ...)
により,Promise p
に結びついた Future から値を取得
def add_promise() {
def zip(p1: Promise[Int], p2: Promise[Int]): Future[Int] = {
for {
v1 <- p1.future
v2 <- p2.future
} yield (v1 + v2)
}
val p1 = Promise[Int]()
Future { p1 success 1 }
val p2 = Promise[Int]()
Future { p2 success 2 }
println(f"1 + 2 = ${Await.result(zip(p1, p2), Duration.Inf)}")
}
object recursive extends Fibonacci {
def fib(n: Int): Int = {
if (n <= 1) 1
else fib(n-2) + fib(n-1)
}
}
object recursive extends Fibonacci {
def fib(n: Int): Int = {
if (n <= 1) 1
else fib(n-2) + fib(n-1)
}
}
n
についての比較
n <= 1
ならば 1 を返す.
fib(n - 2)
を計算するfib(n - 2)
の計算結果を受け取り,覚えておく.(v1
ということにしようか)fib(n - 1)
を計算するfib(n - 1)
の計算結果を受け取り,覚えておく.(v2
ということにしようか)v1
と v2
の和を計算し,覚えておく.(sum
ということにしようか)sum
を返す.n
についての比較.
n <= 1
ならば 1 を返す.(誰に?)
そうでなければ,
fib(n - 2)
を計算する
fib(n - 2)
の計算結果を受け取り,覚えておく.(v1
ということにしようか)
fib(n - 1)
を計算する
fib(n - 1)
の計算結果を受け取り,覚えておく.(v2
ということにしようか)
v1
と v2
の和を計算し,覚えておく.(sum
ということにしようか)
sum
を返す.(誰に?)
「誰に = 計算結果を渡す相手」を明示
計算結果を渡す相手を Promise で表現
// promise1, promise2 はそれぞれ fib(n-1), fib(n-2) を計算している Future たちと繋がる Promise
// これらの結果を収集したのちに promise_sum で待っている Future に合計値を伝える。
def sum(promise1: Promise[Int], promise2: Promise[Int], promise_sum: Promise[Int]) = {
val (v1, v2) = Await.result(promise1.future.zip(promise2.future), Duration.Inf)
promise_sum success (v1 + v2)
}
// Fibonacci(n)を計算した結果を p に伝える
def fib(n: Int, p: Promise[Int]) {
if (n <= 1) p success 1
else {
// fib(n-1), fib(n-2)を計算するFutureとPromise
val promise1 = Promise[Int]() // fib(n-1) の結果を納める Promise
val promise2 = Promise[Int]() // fib(n-2) の結果を納める Promise
Future { fib(n-1, promise1) }
Future { fib(n-2, promise2) }
// fibを計算するFutureたちから結果を受け取りその和をpで待つFutureに伝えるFuture
sum(promise1, promise2, p)
}
}