34

私が持っているとしましょう

val foo : Seq[Double] = ...
val bar : Seq[Double] = ...

そして、baz(i) = foo(i) + bar(i) の seq を生成したいと考えています。これを行うために私が考えることができる1つの方法は

val baz : Seq[Double] = (foo.toList zip bar.toList) map ((f: Double, b : Double) => f+b)

ただし、これは見苦しく非効率的です。両方の seq をリストに変換し (これは遅延リストで爆発します)、タプルのこの一時リストを作成し、それをマップして GC するだけです。ストリームが怠惰な問題を解決するかもしれませんが、いずれにせよ、これは不必要に醜いように感じます。Lisp では、 map 関数は複数のシーケンスにマップされます。私は書くだろう

(mapcar (lambda (f b) (+ f b)) foo bar)

また、一時リストはどこにも作成されません。Scalaにはmap-over-multiple-lists関数がありますか、それともzipとdestructuringを組み合わせることは本当にこれを行うための「正しい」方法ですか?

4

7 に答える 7

84

Scala 2.8 では:

val baz = (foo, bar).zipped map (_ + _)

また、2 つ以上のオペランドに対しても同じように機能します。つまり、これを次のようにフォローアップできます。

(foo, bar, baz).zipped map (_ * _ * _)
于 2010-06-18T14:12:36.480 に答える
15

必要な関数は と呼ばれますzipWithが、標準ライブラリの一部ではありません。2.8 になります (更新: どうやらそうではありません。コメントを参照してください)。

foo zipWith((f: Double, b : Double) => f+b) bar

この Trac チケットを参照してください。

于 2009-07-21T07:14:24.960 に答える
10

ええと、zip の欠如は、Scala の 2.7 Seq の欠陥です。Scala 2.8 はよく考えられたコレクション設計を持ち、2.7 に存在するその場しのぎの方法を置き換えます (すべてが一度に作成されたわけではなく、統一された設計になっていることに注意してください)。

現在、一時的なコレクションを作成したくない場合は、Scala 2.7 では「射影」、Scala 2.8 では「ビュー」を使用する必要があります。これにより、特定の命令、特に map、flatMap、および filter が厳密ではないコレクション タイプが得られます。Scala 2.7 では、List の射影は Stream です。Scala 2.8 では、Sequence の SequenceView がありますが、Sequence のすぐそこに zipWith があり、それは必要ありません。

前述のように、JVM は一時的なオブジェクトの割り当てを処理するように最適化されており、サーバー モードで実行している場合は、実行時の最適化が驚異的な効果を発揮します。したがって、時期尚早に最適化しないでください。実行される条件でコードをテストします。サーバー モードで実行する予定がない場合は、コードが長時間実行されることが予想されるかどうかを再考し、必要に応じていつ/どこで/を最適化します。

編集

実際に Scala 2.8 で利用できるようになるのは次のとおりです。

(foo,bar).zipped.map(_+_)
于 2009-07-21T11:59:30.560 に答える
4

レイジー リストはリストのコピーではなく、単一のオブジェクトに似ています。遅延 zip 実装の場合、次の項目を要求されるたびに、2 つの入力リストのそれぞれから項目を取得し、それらからタプルを作成します。次に、パターン マッチングでタプルを分割します。あなたのラムダ。

そのため、操作を開始する前に入力リスト全体の完全なコピーを作成する必要はありません。要約すると、JVM で実行されているアプリケーションと非常によく似た割り当てパターンになります。JVM が最適化されて処理できるように、寿命は短いが小さい割り当てが多数あります。

更新:明確にするために、リストではなくストリーム (遅延リスト) を使用する必要があります。Scala のストリームには、レイジーな方法で動作する zip があるため、物事をリストに変換するべきではありません。

理想的には、あなたのアルゴリズムは 2 つの無限ストリームで爆発することなく動作できる必要があります(もちろん、何も実行せずfolding、ストリームを読み取って生成するだけであると仮定します)。

于 2009-07-21T06:37:44.360 に答える
1

同様のタスクに直面したとき、次の pimp をIterablesに追加しました。

implicit class IterableOfIterablePimps[T](collOfColls: Iterable[Iterable[T]]) {
  def mapZipped[V](f: Iterable[T] => V): Iterable[V] = new Iterable[V] {
    override def iterator: Iterator[V] = new Iterator[V] {
      override def next(): V = {
        val v = f(itemsLeft.map(_.head))
        itemsLeft = itemsLeft.map(_.tail)
        v
      }

      override def hasNext: Boolean = itemsLeft.exists(_.nonEmpty)

      private var itemsLeft = collOfColls
    }
  }
}

これがあれば、次のようなことができます:

val collOfColls = List(List(1, 2, 3), List(4, 5, 6), List(7, 8, 9))
collOfColls.mapZipped { group =>
  group // List(1, 4, 7), then List(2, 5, 8), then List(3, 6, 9)
}

とは繰り返し呼び出されるIterableため、ネストされたコレクション型として渡されることを慎重に検討する必要があることに注意してください。したがって、理想的には fastおよび.tailheadIterable[List]tailhead

また、このコードは、同じサイズのネストされたコレクションを想定しています。これが私の使用例でしたが、必要に応じて改善できると思います。

于 2015-08-12T20:40:19.343 に答える
0

更新:この「回答」は実際には質問されている質問に対処していないことが (コメントで) 指摘されています。この答えは、要求されたmin(M, N)ではなく、 N x M要素を生成するとのすべての組み合わせにマップされます。だから、これは間違っていますが、良い情報なので後世に残しました。foobar


これを行う最善の方法は、 とflatMap組み合わせることmapです。コードは言葉よりも雄弁です:

foo flatMap { f => bar map { b => f + b } }

Seq[Double]これにより、期待どおりに単一の が生成されます。このパターンは非常に一般的であるため、Scala にはそれを実装する構文マジックが実際に組み込まれています。

for {
  f <- foo
  b <- bar
} yield f + b

または、代わりに:

for (f <- foo; b <- bar) yield f + b

構文は、実際にこれfor { ... }を行うための最も慣用的な方法です。必要に応じて、引き続きジェネレータ句 (例: ) を追加できますb <- bar。したがって、マップしなければならない s が突然3 つ Seqになった場合、要件に合わせて構文を簡単に拡張できます (言い回しを言えば)。

于 2009-07-21T13:41:41.280 に答える