質問
の書籍"のプログラミングスカラ",第23の例のように:
case class Book(title: String, authors: String*)
val books: List[Book] = // list of books, omitted here
// find all authors who have published at least two books
for (b1 <- books; b2 <- books if b1 != b2;
a1 <- b1.authors; a2 <- b2.authors if a1 == a2)
yield a1
著者は、この翻訳:
books flatMap (b1 =>
books filter (b2 => b1 != b2) flatMap (b2 =>
b1.authors flatMap (a1 =>
b2.authors filter (a2 => a1 == a2) map (a2 =>
a1))))
だけに地図やflatmap方法の定義TraversableLike.scala)よく目にすることでしょう、と定義されたループ:
def map[B, That](f: A => B)(implicit bf: CanBuildFrom[Repr, B, That]): That = {
val b = bf(repr)
b.sizeHint(this)
for (x <- this) b += f(x)
b.result
}
def flatMap[B, That](f: A => Traversable[B])(implicit bf: CanBuildFrom[Repr, B, That]): That = {
val b = bf(repr)
for (x <- this) b ++= f(x)
b.result
}
もないといけないと思い、このために継続的に次のように変換されforeachして翻訳されながらの声を構築な表現scalaないために構築でいるものの利回り。
なので、だいたい話だが、なぜScalaこの"翻訳"?筆者の使用例4-発電機は、翻訳の4レベルの入れ子ループは思って本当に恐ろしい性能が books
が大きい。
スカーラを使用するよう努めているこのような"統語砂糖"では、常に参照することがわかっ重用フィルタの図flatmap、そのようにプログラマを忘れようんでネストワンループ内の他、何を達成しているので、コードパッケージをダウンロードするも短縮できます。自分の考えたんですか?
解決
内包表記の場合は、モナド変換のためのシンタックスシュガーであり、、など、場所のあらゆる種類のに有用です。その時、彼らははるかに詳細なスカラ座での同等のHaskellの構造よりも(もちろん、Haskellは1は、Scalaの中のような構造のパフォーマンスについて話すことはできませんので、デフォルトでは非厳密である)。
また、重要な、この構築物は明確に行われているものを維持し、かつ迅速にエスカレートインデントや不要なプライベートメソッドのネストを回避します。
はとしては、最終的な考察に、その皮革複雑かどうか、私はこれを断定ます
for {
b1 <- books
b2 <- books
if b1 != b2
a1 <- b1.authors
a2 <- b2.authors
if a1 == a2
} yield a1
これが行われているかを確認することは非常に簡単で、複雑さは明確である:B ^ 2 * ^ 2(フィルターは複雑さを変更しません)、書籍や著者の数の数について。さて、深いインデントまたは民間の方法のいずれかで、Javaで同じコードを書き、コードの複雑さが何であるかを、簡単に見に、確認するためにしようとします。
だから、私見、これは複雑さを隠し、しかし、逆に、それは明らかにしません。
についてはmap
/ flatMap
彼らは適用されませんので、/ filter
定義あなたが言及し、彼らは、List
または他のクラスにも属しません。基本的には、
for(x <- List(1, 2, 3)) yield x * 2
に変換されます。
List(1, 2, 3) map (x => x * 2)
それは
と同じものではありませんmap(List(1, 2, 3), ((x: Int) => x * 2)))
あなたが渡された定義が呼び出されることになる方法です。。記録のために、map
上List
の実際の実装は、次のとおりです。
def map[B, That](f: A => B)(implicit bf: CanBuildFrom[Repr, B, That]): That = {
val b = bf(repr)
b.sizeHint(this)
for (x <- this) b += f(x)
b.result
}
他のヒント
私は、コードを記述します。私は、プロファイル。私は私の注意を捧げるところだボトルネックがある場合。それは何かにだ場合は説明してきたように私は別の方法で問題を攻撃します。それまでは、私は「砂糖」を愛しますそれは私に物事を書いたり、それについてハード思考の手間が省けます。
6つのループが実際にあります。各フィルタ/ flatMap /マップの一のループ
フィルター - >マップペアが
(イテレータメソッド)コレクションの怠惰なビューを使用することによって一つのループで行うことができ一般的に、TTは、1冊本の著者は、他の著者のリストにある場合に見つけるために、すべての本のペアと、2つのネストされたループを見つけるために、書籍の2つのネストされたループを実行している。
明示的にコーディングする際に、単純なデータ構造を使用して、あなたが同じことをするだろう。
そしてもちろん、ここでの例では、「for」ループ複合体を示すために、最も効率的なコードを書くことではありません。例えば、代わりに、著者の一連の、1がセットを使用して、交差点が非空であれば見つけることができます:
for (b1 <- books; b2 <- books; a <- (b1.authors & b2.authors)) yield a
filter
コールは怠惰であるとの中間的な構造を構築避けるだろうwithFilter
に変更した注。 withFilterですかにフィルターから移動するためのガイドを参照してください。
私はfor
がmap
、flatMap
とwithFilter
に翻訳されている理由(だけでなく、値の定義存在する場合は)モナドの使用を容易にするためであると考えています。
一般的に、私はあなたがやっている計算は4回のループ伴う場合、それはfor
ループを用いて微細であると思います。計算がより効率的に行うことができ、パフォーマンスが重要であるなら、あなたは、より効率的なアルゴリズムを使用する必要があります。
一ォ@IttayDの答えは、アルゴリズムの効率。ではここで注目されるアルゴリズムのオリジナルポスト(書籍) 入れ子ループjoin.実際には、こんなアルゴリズムのための大規模なデータセットは、データベースの使用 ハッシュ計 こちらです。にスカラ、ハッシュ集合うようなもの:
(for (book <- books;
author <- book.authors) yield (book, author)
).groupBy(_._2).filter(_._2.size > 1).keys