8

私はScalaが初めてで、コレクションをフィルタリングしてマップする最良の方法を見つけようとしています。これは私の問題を説明するおもちゃの例です。

アプローチ 1:リストを 2 回繰り返し、各繰り返しで同じ値を計算しているため、これはかなり悪い方法です。

val N = 5
val nums = 0 until 10
val sqNumsLargerThanN = nums filter { x: Int => (x * x) > N } map { x: Int => (x * x).toString }

アプローチ 2:これはわずかに優れていますが、それでも(x * x)2 回計算する必要があります。

val N = 5
val nums = 0 until 10
val sqNumsLargerThanN = nums collect { case x: Int if (x * x) > N => (x * x).toString }

では、コレクションを 2 回反復せずにこれを計算し、同じ計算を繰り返さないようにすることは可能ですか?

4

9 に答える 9

7

を使用できますfoldRight

nums.foldRight(List.empty[Int]) {
  case (i, is) =>
    val s = i * i
    if (s > N) s :: is else is
  }

AfoldLeftも同様の目標を達成しますが、結果のリストは逆の順序になります ( foldLeft.

または、Scalaz で遊びたい場合は

import scalaz.std.list._
import scalaz.syntax.foldable._

nums.foldMap { i =>
  val s = i * i
  if (s > N) List(s) else List()
}
于 2015-06-15T01:31:47.340 に答える
5

典型的なアプローチは、iterator(可能であれば)またはview(うまくiteratorいかない場合)を使用することです。これは2 つのトラバーサルを正確に回避するわけではありませんが、フルサイズの中間コレクションの作成を回避します。次に、map最初とfilterその後、map必要に応じてもう一度:

xs.iterator.map(x => x*x).filter(_ > N).map(_.toString)

このアプローチの利点は、非常に読みやすいことと、中間コレクションがないため、かなり効率的であることです。

これがパフォーマンスのボトルネックであるという理由で質問している場合、通常、答えは末尾再帰関数を記述するか、古いスタイルの while ループ メソッドを使用することです。たとえば、あなたの場合

def sumSqBigN(xs: Array[Int], N: Int): Array[String] = {
  val ysb = Array.newBuilder[String]
  def inner(start: Int): Array[String] = {
    if (start >= xs.length) ysb.result
    else {
      val sq = xs(start) * xs(start)
      if (sq > N) ysb += sq.toString
      inner(start + 1)
    }
  }
  inner(0)
}

inner外部ビルダーを使用する代わりに、パラメーターを前方に渡すこともできます(特に合計に役立ちます)。

于 2015-06-15T01:29:47.510 に答える
3

乗算演算を 1 回だけ行う非常に単純なアプローチ。また、レイジーなので、必要な場合にのみコードを実行します。

nums.view.map(x=>x*x).withFilter(x => x> N).map(_.toString)

との違いについては、こちらをご覧ください。filterwithFilter

于 2015-06-15T01:33:50.613 に答える
3

これが本当にシングル パスかどうかはまだ確認できていませんが、

  val sqNumsLargerThanN = nums flatMap { x =>
    val square = x * x
    if (square > N) Some(x) else None
  }
于 2015-06-15T01:37:46.227 に答える
3

コレクションが定義されているすべての値に部分関数を適用するcollectを使用できます。あなたの例は次のように書き直すことができます:

val sqNumsLargerThanN = nums collect {
    case (x: Int) if (x * x) > N => (x * x).toString
}
于 2015-06-15T01:17:09.877 に答える
2

理解のためにこれを考慮してください、

  for (x <- 0 until 10; v = x*x if v > N) yield v.toString

これは、範囲を超えて展開され、一度だけ計算された正方形に (遅延) 展開され、フィルター処理された結果を含むコレクションが生成されます。1 回の反復と 2 乗の計算が 1 回必要であることに注意してください (範囲の作成に加えて)。flatMapwithFilter

于 2015-06-15T04:20:15.210 に答える
0

使用できますflatMap

val sqNumsLargerThanN = nums flatMap { x =>
  val square = x * x
  if (square > N) Some(square.toString) else None
}

または Scalaz では、

import scalaz.Scalaz._

val sqNumsLargerThanN = nums flatMap { x =>
  val square = x * x
  (square > N).option(square.toString)
}

は、これを 1 回の反復で行う方法に関する質問を解決します。これは、Iterator のようにデータをストリーミングするときに役立ちます。

ただし...絶対最速の実装が必要な場合は、そうではありません。実際、変更可能な ArrayList と while ループを使用すると思います。しかし、プロファイリングを行って初めて、確実に知ることができます。いずれにせよ、それは別の質問です。

于 2015-06-15T06:09:59.667 に答える
-2

私も初心者ですが、次のようにしました

 for(y<-(num.map(x=>x*x)) if y>5 ) { println(y)}
于 2016-03-14T14:51:04.280 に答える