約 3,962,568 件
https://w.atwiki.jp/tmiya/pages/50.html
8.4 最下層の型 (Least Types) Scala では、オブジェクトを型でパラメータ化することはできません。そういう訳で、たとえ任意の型の空スタックを表すただ一つの値で充分だったとしても、最初にジェネリッククラス EmptyStack[A] を定義しました。しかし、共変的スタックについては、次のイディオムを使えます。 object EmptyStack extends Stack[Nothing] { ... } ボトム型 Nothing は値を持ちません。したがって型 Stack[Nothing] は、EmptyStack が要素を含まないことを表現します。さらに、Nothing は他のすべての型のサブタイプです。ですから、共変的スタックではどんな型 T に対しても、Stack[Nothing] は Stack[T] のサブタイプです。これによってユーザーコード中で、単一の空スタックオブジェクトを使用できます。たとえば、 val s = EmptyStack.push("abc").push(new AnyRef()) この式について、型の割り当てを詳細に解析しましょう。EmpthStack オブジェクトは型 Stack[Nothing] であり、次のメソッドを持っています。 push[B Nothing](elem B) Stack[B] . 局所的な型推論は、型パラメータ B は EmptyStack の適用において String へとインスタンス化されているに違いない、と決定します。したがって、その適用の結果型(戻り値型)は Stack[String] であり、次のメソッドを持っています。 push[B String](elem B) Stack[B] . 上記の値定義の最後の箇所は、このメソッドの new AnyRef() への適用です。局所的型推論は今回、型パラメータ b は AnyRef へとインスタンス化されているに違いなく、結果型は Stack[AnyRef] である、と決定します。したがって値 s に割り当てられる型は Stack[AnyRef] です。 Nothing はすべての型のサブタイプでした。ほかに、型 Null というものもあり、これは scala.AnyRef と、AnyRefを継承するすべてのクラスのサブタイプです。 Scala の null リテラルはその型の唯一の値です。これによって null はすべての参照型と互換ですが、Int のような値型とは互換ではありません。 完全に改良したスタック定義で、この章を締めくくります。いまやスタックは共変的サブタイプで、push メソッドは一般化され、空スタックは単一のオブジェクトで表現されます。 abstract class Stack[+A] { def push[B A](x B) Stack[B] = new NonEmptyStack(x, this) def isEmpty Boolean def top A def pop Stack[A] } object EmptyStack extends Stack[Nothing] { def isEmpty = true def top = error("EmptyStack.top") def pop = error("EmptyStack.pop") } class NonEmptyStack[+A](elem A, rest Stack[A]) extends Stack[A] { def isEmpty = false def top = elem def pop = rest } Scala ライブラリ内の多くのクラスはジェネリックです。ジェネリックなクラスの系統でよく使われる、タプルと関数を紹介します。他のよく使われるクラスであるリストは、次の章に回します。 前ページ 8 章 目次 次ページ 名前 コメント
https://w.atwiki.jp/tmiya/pages/48.html
8.2 変位指定アノテーション (Variance Annotations) 型パラメータとサブタイプ化の組み合わせは興味深い問題を引き起こします。たとえば、Stack[String] は Stack[AnyRef] のサブタイプであるべきでしょうか? 直感的には OK のように思われます。String のスタックは AnyRef のスタックの特別な場合だからです。より一般的に言うと、もし T が型 S のサブタイプなら、Stack[T] は Stack[S] のサブタイプであるべきです。この性質は 共変 (co-variant)サブタイプ化と呼ばれます。 Scala では、ジェネリック型はデフォルトでは非変(non-variant)サブタイプ化です。それは、前に定義した Stack については、異なる要素型のスタックは決してサブタイプ関係にないということです。しかし、クラス Stack の定義の一行目を次のように変更すれば、共変サブタイプ化を強制できます。 class Stack[+A] { 形式上の型パラメータの前に + を置くことで、このパラメータについてサブタイプ化が共変であることを示します。+ に加えて、反変サブタイプ化を示す接頭辞 - もあります。もし Stack を class Stack [-T] ... と定義すれば、T が型 S のサブタイプなら、Stack[S] は Stack[T] のサブタイプになります (スタックの場合には驚くべきことでしょう!) 。 純粋関数的な世界では、すべての型は共変となることができます。しかし、もしミュータブル(変更可能)なデータを導入すると状況は変わります。Java や .NET の配列について考えてみましょう。そのような配列は、Scala ではジェネリックなクラス Array で表現されます。次はこのクラスの定義の一部です。 class Array[A] { def apply(index Int) A def update(index Int, elem A) } このクラスは、Scala のユーザープログラムから Scala の配列がどのように見えるかを定義します。Scala コンパイラはこの抽象化を、基礎となっているホストシステムの配列へ、それが可能なほとんどの場合、マップします。 実際 Java では配列は共変です。つまり、参照型 T と S について、もし T が S のサブタイプなら、Array[T] は Array[S] のサブタイプです。これは自然なことに思えるかもしれませんが、特別な実行時チェックを必要とする安全上の問題をもたらします。次がその例です。 val x = new Array[String](1) val y Array[Any] = x y(0) = new Rational(1, 2) // これは次の糖衣構文 // y.update(0, new Rational(1, 2)) 最初の行で、新しい文字列配列が生成されます。二行目で、この配列は Array[Any] 型の変数 y に束縛されます。配列が共変だということを仮定すると、これは OK です。なぜなら Array[String] は Array[Any] のサブタイプだからです。最後に、最終行で有理数がこの配列に代入されます。これも OK です。なぜなら型 Rational は、配列 y の要素型 Any のサブタイプだからです。そして結局最後には、文字列配列に有理数が格納され、明らかに型健全性を破ります。 Java はこの問題を、三行目に実行時チェックを導入して、格納された要素と配列が作られた要素型との互換性をテストし、解決します。例で見たようにこの要素型は、必ずしも更新される配列の静的な要素型ではありません。テストが失敗すると ArrayStoreException が発生します。 Scala はその代わりに、この問題を静的に解決します。Scala では、配列は非変サブタイプ化なのでコンパイル時に二行目を許可しないことで、解決します。これは、Scala はどうやって変位指定アノテーションの正しさを検証するのか、という疑問を生じさせます。もし単に配列を共変に宣言したら、どうやって潜在的な問題を見つけるのでしょうか? Scala は変位指定アノテーションの健全性を検証するために、用心深い推定をします。クラスの共変的型パラメータは、クラス内の共変な場所にだけ現れることができます。クラス内の値の型で共変な場所は、クラス内のメソッドの結果型と他の共変的型への型引数です。形式上のメソッドパラメータの型は共変ではありません。したがって次のクラス定義は却下されます。 class Array[+A] { def apply(index Int) A def update(index Int, elem A) ^ 共変的型パラメータ A が 反変的位置に現れる。 } これまでのところは問題ありません。直感的には共変的なクラスの update 手続きを却下する点でコンパイラは正しいです。なぜなら update は状態を変更することができ、したがって共変サブタイプ化の健全性の土台を揺るがすからです。 しかしながら、状態を変更しないメソッドで、型パラメータが反変的な位置に現れるものがあります。例は型 Stack の push です。Scala コンパイラは共変的スタックに対するこのメソッド定義を再び却下します。 class Stack[+A] { def push(x A) Stack[A] = ^ 共変的型パラメータ A が 反変的位置に表れる。 これは残念です。なぜなら配列と違ってスタックは純粋関数的なデータ構造であり、共変サブタイプ化できるべきだからです。しかしこの問題を、下限境界付きの型パラメータをもつ多相的メソッドで解決する方法があります。 前ページ 8 章 目次 次ページ 名前 コメント
https://w.atwiki.jp/houdini16doc/pages/12.html
examples
https://w.atwiki.jp/tmiya/pages/84.html
17.5 Semaphores 17.5 セマフォ A common mechanism for process synchronization is a lock (or semaphore). A lock offers two atomic actions acquire and release. Here s the implementation of a lock in Scala プロセス同期の一般的なメカニズムはロック(あるいはセマフォ)です。ロックは2つのアトミックなアクションを提供します。すなわち、獲得と解放です。次は Scala におけるロックの実装です。 package scala.concurrent class Lock { var available = true def acquire = synchronized { while (!available) wait() available = false } def release = synchronized { available = true notify() } } 名前 コメント
https://w.atwiki.jp/tmiya/pages/82.html
17.3 フューチャー フューチャー は、他のクライアントスレッドにおいて並列に計算される値であり、クライアントスレッドによっていつか使用されます。フューチャーは並行プロセスのリソースを有効に利用するために使います。典型的な使い方は次のようです。 import scala.concurrent.ops._ ... val x = future(someLengthyComputation) anotherLengthyComputation val y = f(x()) + g(x()) future メソッドはオブジェクト scala.concurrent.ops において、次のように定義されています。 def future[A](p = A) Unit = A = { val result = new SyncVar[A] fork { result.set(p) } (() = result.get) } futureメソッドは、実行すべき計算 p をパラメータにとります。計算の型は任意であり、そのことは future の型パラメータ a によって表現されています。future メソッドでは計算結果を表すパラメータを取る、ガード result を定義します。つぎに、新しいスレッドを fork して、答えを計算し終了時に result ガードを起動します。このスレッドに並行して、関数 (訳注 future) は型 A の無名関数を返します。この無名関数は、呼ばれたときに result ガードが起動されるまでウェイトし、そして、一度 result ガードが起動されると結果の引数を返します。同じ時に、関数は同じ引数で result ガードを再起動しますが、関数の future 起動は結果をすぐに返せます。 前ページ 17 章 目次 次ページ 名前 コメント
https://w.atwiki.jp/dotcomtriple/pages/57.html
最終更新日:2009/11/21 23 00 56 解答 設問1:b 設問2:d 解説 設問1: WWWサーバはNAT装置の内側にあり、WWWサーバのグローバルアドレスはNAT装置の2つのIFのアドレスと同一となる。 同一のアドレスに対し複数のホスト名を割り当てる場合、CNAME(Canonical NAME)レコードを使う必要がある。wwwとnat(nat1,nat2)をCNAMEで定義しているのはbだけである。 設問2: 設問1の定義より、WWWサーバのドメインは「www.nat.example.com」である(example.comはORIGINにて定義)。選択肢の中で該当するのはdである。 関連項目 関連問題 2006年二部問15 2005年二部問10 参考文献・WEBページ http //www.atmarkit.co.jp/flinux/rensai/bind02/bind02.html
https://w.atwiki.jp/tmiya/pages/34.html
4.5 ネストした関数 関数型プログラミングスタイルでは、小さなヘルパー関数をたくさん作ることを推奨しています。先の例では、sqrt の実装はヘルパー関数 sqrtIter、improve、isGoodEnough を使っています。これらの関数の名前は sqrt の実装にだけ関係します。ふつう我々は、sqrt のユーザーがこれらの関数に直接アクセスすることを望みません。 そうすることを (そして名前空間の汚染を避けるのを)、ヘルパー関数を呼び出す関数自身の中へ入れることで強制できます。 def sqrt(x Double) = { def sqrtIter(guess Double, x Double) Double = if (isGoodEnough(guess, x)) guess else sqrtIter(improve(guess, x), x) def improve(guess Double, x Double) = (guess + x / guess) / 2 def isGoodEnough(guess Double, x Double) = abs(square(guess) - x) 0.001 sqrtIter(1.0, x) } このプログラムにおいて、中括弧 {...} は ブロック を囲みます。Scala のブロックはそれ自身が式です。各ブロックはその値を定義する結果式で終わります。結果式の前には補助的な定義があってもよく、それらはそのブロック内からしか見えません。 ブロック内の各定義は、後にセミコロンが続かねばならず、それによって後に続く定義や結果式と分離されます。しかし、次の条件の何れかが真でなければ、各行の最後にセミコロンが暗黙のうちに挿入されます。 1. 問題となる行がピリオドのような単語や、式の最後として正しくない中置演算子で終わる場合。 2. あるいは次の行が、式のはじまりとならないような単語で始まる場合。 3. あるいは括弧 (...) または、角括弧 [...] の内側にいる場合 (これらの中に複数の文を入れることはできません)。 したがって次は正しいです。 def f(x Int) = x + 1; f(1) + f(2) def g1(x Int) = x + 1 g(1) + g(2) def g2(x Int) = {x + 1}; /* ; は必須 */ g2(1) + g2(2) def h1(x) = x + y h1(1) * h1(2) def h2(x Int) = ( x // 括弧は必須。 + y // 括弧がないと x の後にセミコロンが挿入される。 ) h2(1) / h2(2) Scala は通常のブロック構造のスコープ規則を用いています。外側のブロックで定義された名前は、そこで再定義されない限り、内側のブロックからも見えます。この規則によって sqrt の例を簡略化できます。ネストさせた関数の追加パラメータとして x を渡す必要はありません。なぜなら外側の関数 sqrt のパラメータは常に見えるからです。次が簡略化されたコードです。 def sqrt(x Double) = { def sqrtIter(guess Double) Double = if (isGoodEnough(guess)) guess else sqrtIter(improve(guess)) def improve(guess Double) = (guess + x / guess) / 2 def isGoodEnough(guess Double) = abs(square(guess) - x) 0.001 sqrtIter(1.0) } 前ページ 4 章 目次 次ページ 「ギュ」は「行」のtypoでしょうか。あと、翻訳お疲れ様です。日本語でアクセスできるまとまったScalaの資料はまだ無いので、宮本さんの作業はたいへんすばらしいものだと思います。 -- みずしま (2008-04-15 21 34 58) ご指摘ありがとうございます>みずしまさん。typo直しました。専門用語など心もとないと自覚があるので、随時ご指摘願えればと思います。 -- tmiya (2008-04-16 21 50 33) 名前 コメント
https://w.atwiki.jp/tmiya/pages/75.html
13.3 イテレータの使用 イテレータの使い方の例をさらに2つあげます。最初は、配列 xs Array[Int]のすべての要素を表示するもので、次のように書けます。 Iterator.fromArray(xs) foreach (x = println(x)) または for 内包表記を使っても書けます。 for (x Iterator.fromArray(xs)) println(x) 二つ目の例として、double の配列のうち、要素がある値より大きいすべてのインデックスを求めることを考えます。インデックスはイテレータとして返されるものとします。これは、次の式で実現できます。 import Iterator._ fromArray(xs) .zip(from(0)) .filter(case (x, i) = x limit) .map(case (x, i) = i) または for 内包表記を使っても実現できます。 import Iterator._ for ((x, i) fromArray(xs) zip from(0); x limit) yield i 前ページ 13 章 目次 次ページ 名前 コメント
https://w.atwiki.jp/tmiya/pages/44.html
7.1 ケースクラスとケースオブジェクト ケースクラス と ケースオブジェクト は普通のクラスやオブジェクトのように定義しますが、定義に修飾子 case が手前に付くことだけが違います。たとえば定義 abstract class Expr case class Number(n Int) extends Expr case class Sum(e1 Expr, e2 Expr) extends Expr は、Number と Sum をケースクラスとして導入します。クラスやオブジェクト定義の前の case 修飾子には、次のような効果があります。 1. ケースクラスは暗黙のうちにコンストラクタ関数を伴い、それはクラスと同じ名前です。この例の場合、2つの関数 def Number(n Int) = new Number(n) def Sum(e1 Expr, e2 Expr) = new Sum(e1, e2) が追加されます。したがって式の木をもう少し簡潔に、次のように構成できます。 Sum(Sum(Number(1), Number(2)), Number(3)) 2. ケースクラスとケースオブジェクトは暗黙のうちにメソッド toString、equals、hashCode を伴い、それらはクラス AnyRef の同名のメソッドをオーバーライドします。これらのメソッド実装では、それぞれのケースクラスのメンバ構造を考慮しています。toString メソッドは式の木が構成された方法を表します。したがって、 Sum(Sum(Number(1), Number(2)), Number(3)) は、この文字列そのままに変換されます。一方、クラス AnyRef のデフォルトの実装は、一番外側のコンストラクタの名前 Sum と数字からなる文字列を返すでしょう。equals メソッドはケースクラスの2つのケースメンバを、もし同じコンストラクタで構築され、かつ、それらの引数がそれぞれ等しいなら、等しいと扱います。これは == と != の実装にも影響しますが、それらは Scala では equals を用いて実装されているからです。したがって Sum(Number(1), Number(2)) == Sum(Number(1), Number(2)) は、true を返します。もし Sum や Number がケースクラスでないなら、同じ式は false を返します。なぜならクラス AnyRef の equals の標準実装では、異なるコンストラクタ呼び出しで生成されたオブジェクトは常に異なる、と扱うからです。hashCode メソッドも他の2つのメソッドと同じ原則に従います。デフォルトの hashCode の実装ではハッシュコードを、オブジェクトのアドレスから計算する代わりに、ケースクラスのコンストラクタ名と、コンストラクタ引数のハッシュコードから計算します。 3. ケースクラスは暗黙のうちに、パラメータなしのアクセサメソッドを伴い、それはコンストラクタ引数を読み出します。例では、Number はアクセサメソッド def n Int を持ち、コンストラクタパラメータ n を返します。一方 Sum は2つのアクセサメソッドを持ちます。 def e1 Expr, e2 Expr したがって、たとえば型 Sum の値 s に対して、左オペランドにアクセスするために s.e1 と書けます。しかし、型 Expr の値 e に対して項 e.e1 は正しくありませんが、それは e1 は Sum で定義されているのであって基底クラス Expr のメンバではないからです。では、静的型が基底クラス Expr である値に対してどうやってコンストラクタを判別してコンストラクタ引数にアクセスすれば良いのでしょうか? これはケースクラスの四番目にして最後の特徴によって解決されます。 4. ケースクラスを使えば、ケースクラスのコンストラクタを参照する パターン を構築できます。 前ページ 7 章 目次 次ページ 名前 コメント
https://w.atwiki.jp/tmiya/pages/83.html
17.4 並列計算 次の例では、関数 par は計算のペアをパラメータとしてとり、計算結果を別のペアで返します。2つの計算は並列実行されます。 この関数はオブジェクト scala.concurrent.ops において、次のように定義されています。 def par[A, B](xp = A, yp = B) (A, B) = { val y = new SyncVar[B] spawn { y set yp } (xp, y.get) } 同じ場所で replicate 関数が定義されていて、多数の計算の複製を並列実行しま す。各複製インスタンスには、それを識別する整数値が渡されます。 def replicate(start Int, end Int)(p Int = Unit) { if (start == end) () else if (start + 1 == end) p(start) else { val mid = (start + end) / 2 spawn { replicate(start, mid)(p) } replicate(mid, end)(p) } } 次の関数は、配列のすべての要素について並列計算を実行するために、replicate を使っています。 def parMap[A,B](f A = B, xs Array[A]) Array[B] = { val results = new Array[B](xs.length) replicate(0, xs.length) { i = results(i) = f(xs(i)) } results } 前ページ 17 章 目次 次ページ 名前 コメント