Chapter 14 Lazy Values

「Chapter 14 Lazy Values」の編集履歴(バックアップ)一覧はこちら

Chapter 14 Lazy Values」(2011/02/24 (木) 09:04:18) の最新版変更点

追加された行は緑色になります。

削除された行は赤色になります。

#co(){ Chapter 14 Lazy Values Lazy values provide a way to delay initialization of a value until the first time it is accessed. This may be useful when dealing with values that might not be needed during execution, and whose computational cost is signifficant. As a first example, let's consider a database of employees, containing for each employee its manager and its team. } * 第 14 章 遅延評価val (lazy value) 遅延評価val (lazy value) は、値の初期化を最初にアクセスされるまで遅延させる方法です。これは実行中に必要とならないかもしれない、計算コストが高い値を扱う場合に有用です。最初の例として、従業員のデータベースを考えましょう。各従業員ごとにマネージャーとチームが決まっています。 case class Employee(id: Int, name: String, managerId: Int) { val manager: Employee = Db.get(managerId) val team: List[Employee] = Db.team(id) } #co(){ The Employee class given above will eagerly initialize all its fields, loading the whole employee table in memory. This is certainly not optimal, and it can be easily improved my making the fields lazy. This way we delay the database access until it is really needed, if it is ever needed. } 上記の Employee クラスは、直ちにそのフィールドをすべて初期化し、従業員テーブル全体をメモリにロードします。これは明らかに最善ではなく、フィールドを lazy にすることで簡単に改善できます。このようにして、データベースアクセスを、本当に必要になるまで、また、初めて必要になるまで遅らせます。 case class Employee(id: Int, name: String, managerId: Int) { lazy val manager: Employee = Db.get(managerId) lazy val team: List[Employee] = Db.team(id) } #co(){ To see what is really happening, we can use this mockup database which shows when records are fetched: } 実際に何が起きているか、いつレコードがフェッチされるか表示するモックアップのデータベースを使って、見てみましょう。 object Db { val table = Map(1 -> (1, "Haruki Murakami", 1), 2 -> (2, "Milan Kundera", 1), 3 -> (3, "Jeffrey Eugenides", 1), 4 -> (4, "Mario Vargas Llosa", 1), 5 -> (5, "Julian Barnes", 2)) def team(id: Int) = { for (rec <- table.values.toList; if rec._3 == id) yield recToEmployee(rec) } def get(id: Int) = recToEmployee(table(id)) private def recToEmployee(rec: (Int, String, Int)) = { println("[db] fetching " + rec._1) Employee(rec._1, rec._2, rec._3) } } #co(){ The output when running a program that retrieves one employee confirms that the database is only accessed when referring the lazy values. } 一人の従業員を取り出すプログラムを実行すると、確かにデータベースは遅延評価Val を参照するときのみアクセスされることが、出力によって確認できます。 #co(){ Another use of lazy values is to resolve the initialization order of applications composed of several modules. Before lazy values were introduced, the same effect was achieved by using object definitions. As a second example, we consider a compiler composed of several modules. We look first at a simple symbol table that defines a class for symbols and two predefined functions. } ほかの 遅延評価val の使い方は、いくつかのモジュールからなるアプリケーションの初期化の順番を解決することです。遅延評価Val が導入される前は、同様のことを object 定義を使用することで実現していました。二つ目の例として、いくつかのモジュールからなるコンパイラを考えてみましょう。最初に、シンボルのためのクラスと2つの事前定義された関数を定義している、単純なシンボルテーブルを見てみます。 class Symbols(val compiler: Compiler) { import compiler.types._ val Add = new Symbol("+", FunType(List(IntType, IntType), IntType)) val Sub = new Symbol("-", FunType(List(IntType, IntType), IntType)) class Symbol(name: String, tpe: Type) { override def toString = name + ": " + tpe } } #co(){ The symbols module is parameterized with a Compiler instance, which provides access to other services, such as the types module. In our example there are only two predefined functions, addition and subtraction, and their definitions depend on the types module. } symbols モジュールは、Compiler インスタンスでパラメータ化されています。Compiler インスタンスは、types モジュールなどのほかのサービスへのアクセスを提供します。この例では、事前定義された関数が2つ (加算と減算) だけあり、それらの定義は types モジュールに依存しています。 class Types(val compiler: Compiler) { import compiler.symtab._ abstract class Type case class FunType(args: List[Type], res: Type) extends Type case class NamedType(sym: Symbol) extends Type case object IntType extends Type } #co(){ In order to hook the two components together a compiler object is created and passed as an argument to the two components. } 2つのコンポーネントをつなぐため、コンパイラオブジェクトを作成して、2つのコンポーネントへ引数として渡します。 class Compiler { val symtab = new Symbols(this) val types = new Types(this) } #co(){ Unfortunately, the straight-forward approach fails at runtime because the symtab module needs the types module. In general, the dependency between modules can be complicated and getting the right initialization order is difficult, or even impossible when there are cycles. The easy fix is to make such fields lazy and let the compiler figure out the right order. } 残念ながら、この実直的なアプローチは実行時に失敗します。symtab モジュールが types モジュールを必要としているからです。一般的に、モジュール間の依存は複雑になりがちで、正しい順番で初期化するのは難しく、循環があるために不可能なことさえあります。簡単な対処は、そのようなフィールドを lazy にして、正しい順番は compiler に任せてしまうことです。 class Compiler { lazy val symtab = new Symbols(this) lazy val types = new Types(this) } #co(){ Now the two modules are initialized on first access, and the compiler may run as expected. } これで、2つのモジュールは最初のアクセスで初期化され、compiler は期待通りに動くでしょう。 #co(){ Syntax The lazy modifier is allowed only on concrete value definitions. All typing rules for value definitions apply for lazy values as well, with one restriction removed: recursive local values are allowed. } ** 構文 lazy 修飾子は、具体的な値定義でのみ指定できます。値定義におけるすべての型付け規則が遅延評価valにも適用されますが、ひとつだけ制限が取り払われています。それは、再帰的なローカル値が許されることです。 #center(){[[前ページ>Example13.3]] [[ 14 章>Chapter 14 Lazy Values]] [[目次>ScalaByExample和訳]] [[次ページ>Chapter 15 Implicit Parameters and Conversions]]} ---- - すべての従業員テーブルをメモリにロードします (?訳微妙) => 「従業員テーブル全体を」の方がいいかもです。 -- pomu0325 (2009-12-29 19:32:07) - (?訳微妙。my は?) => 原文が&quot;by&quot;のtypo と思われます。 -- pomu0325 (2009-12-29 19:32:39) #comment
#co(){ Chapter 14 Lazy Values Lazy values provide a way to delay initialization of a value until the first time it is accessed. This may be useful when dealing with values that might not be needed during execution, and whose computational cost is signifficant. As a first example, let's consider a database of employees, containing for each employee its manager and its team. } #setmenu2(ex-r-menu) * 第 14 章 遅延評価val (lazy value) 遅延評価val (lazy value) は、値の初期化を最初にアクセスされるまで遅延させる方法です。これは実行中に必要とならないかもしれない、計算コストが高い値を扱う場合に有用です。最初の例として、従業員のデータベースを考えましょう。各従業員ごとにマネージャーとチームが決まっています。 case class Employee(id: Int, name: String, managerId: Int) { val manager: Employee = Db.get(managerId) val team: List[Employee] = Db.team(id) } #co(){ The Employee class given above will eagerly initialize all its fields, loading the whole employee table in memory. This is certainly not optimal, and it can be easily improved my making the fields lazy. This way we delay the database access until it is really needed, if it is ever needed. } 上記の Employee クラスは、直ちにそのフィールドをすべて初期化し、従業員テーブル全体をメモリにロードします。これは明らかに最善ではなく、フィールドを lazy にすることで簡単に改善できます。このようにして、データベースアクセスを、本当に必要になるまで、また、初めて必要になるまで遅らせます。 case class Employee(id: Int, name: String, managerId: Int) { lazy val manager: Employee = Db.get(managerId) lazy val team: List[Employee] = Db.team(id) } #co(){ To see what is really happening, we can use this mockup database which shows when records are fetched: } 実際に何が起きているか、いつレコードがフェッチされるか表示するモックアップのデータベースを使って、見てみましょう。 object Db { val table = Map(1 -> (1, "Haruki Murakami", 1), 2 -> (2, "Milan Kundera", 1), 3 -> (3, "Jeffrey Eugenides", 1), 4 -> (4, "Mario Vargas Llosa", 1), 5 -> (5, "Julian Barnes", 2)) def team(id: Int) = { for (rec <- table.values.toList; if rec._3 == id) yield recToEmployee(rec) } def get(id: Int) = recToEmployee(table(id)) private def recToEmployee(rec: (Int, String, Int)) = { println("[db] fetching " + rec._1) Employee(rec._1, rec._2, rec._3) } } #co(){ The output when running a program that retrieves one employee confirms that the database is only accessed when referring the lazy values. } 一人の従業員を取り出すプログラムを実行すると、確かにデータベースは遅延評価Val を参照するときのみアクセスされることが、出力によって確認できます。 #co(){ Another use of lazy values is to resolve the initialization order of applications composed of several modules. Before lazy values were introduced, the same effect was achieved by using object definitions. As a second example, we consider a compiler composed of several modules. We look first at a simple symbol table that defines a class for symbols and two predefined functions. } ほかの 遅延評価val の使い方は、いくつかのモジュールからなるアプリケーションの初期化の順番を解決することです。遅延評価Val が導入される前は、同様のことを object 定義を使用することで実現していました。二つ目の例として、いくつかのモジュールからなるコンパイラを考えてみましょう。最初に、シンボルのためのクラスと2つの事前定義された関数を定義している、単純なシンボルテーブルを見てみます。 class Symbols(val compiler: Compiler) { import compiler.types._ val Add = new Symbol("+", FunType(List(IntType, IntType), IntType)) val Sub = new Symbol("-", FunType(List(IntType, IntType), IntType)) class Symbol(name: String, tpe: Type) { override def toString = name + ": " + tpe } } #co(){ The symbols module is parameterized with a Compiler instance, which provides access to other services, such as the types module. In our example there are only two predefined functions, addition and subtraction, and their definitions depend on the types module. } symbols モジュールは、Compiler インスタンスでパラメータ化されています。Compiler インスタンスは、types モジュールなどのほかのサービスへのアクセスを提供します。この例では、事前定義された関数が2つ (加算と減算) だけあり、それらの定義は types モジュールに依存しています。 class Types(val compiler: Compiler) { import compiler.symtab._ abstract class Type case class FunType(args: List[Type], res: Type) extends Type case class NamedType(sym: Symbol) extends Type case object IntType extends Type } #co(){ In order to hook the two components together a compiler object is created and passed as an argument to the two components. } 2つのコンポーネントをつなぐため、コンパイラオブジェクトを作成して、2つのコンポーネントへ引数として渡します。 class Compiler { val symtab = new Symbols(this) val types = new Types(this) } #co(){ Unfortunately, the straight-forward approach fails at runtime because the symtab module needs the types module. In general, the dependency between modules can be complicated and getting the right initialization order is difficult, or even impossible when there are cycles. The easy fix is to make such fields lazy and let the compiler figure out the right order. } 残念ながら、この実直的なアプローチは実行時に失敗します。symtab モジュールが types モジュールを必要としているからです。一般的に、モジュール間の依存は複雑になりがちで、正しい順番で初期化するのは難しく、循環があるために不可能なことさえあります。簡単な対処は、そのようなフィールドを lazy にして、正しい順番は compiler に任せてしまうことです。 class Compiler { lazy val symtab = new Symbols(this) lazy val types = new Types(this) } #co(){ Now the two modules are initialized on first access, and the compiler may run as expected. } これで、2つのモジュールは最初のアクセスで初期化され、compiler は期待通りに動くでしょう。 #co(){ Syntax The lazy modifier is allowed only on concrete value definitions. All typing rules for value definitions apply for lazy values as well, with one restriction removed: recursive local values are allowed. } #setmenu2(ex-r-menu) ** 構文 lazy 修飾子は、具体的な値定義でのみ指定できます。値定義におけるすべての型付け規則が遅延評価valにも適用されますが、ひとつだけ制限が取り払われています。それは、再帰的なローカル値が許されることです。 #center(){[[前ページ>Example13.3]] [[ 14 章>Chapter 14 Lazy Values]] [[目次>ScalaByExample和訳]] [[次ページ>Chapter 15 Implicit Parameters and Conversions]]} ---- - すべての従業員テーブルをメモリにロードします (?訳微妙) => 「従業員テーブル全体を」の方がいいかもです。 -- pomu0325 (2009-12-29 19:32:07) - (?訳微妙。my は?) => 原文が&quot;by&quot;のtypo と思われます。 -- pomu0325 (2009-12-29 19:32:39) #comment

表示オプション

横に並べて表示:
変化行の前後のみ表示:
ツールボックス

下から選んでください:

新しいページを作成する
ヘルプ / FAQ もご覧ください。