10

私は Scala が初めてで、Scala By Exampleを読んでいたところです。第 2 章では、著者は 2 つの異なるバージョンの Quicksort を使用しています。

1 つは命令型のスタイルです。

def sort(xs: Array[Int]) {
    def swap(i: Int, j: Int) {
        val t = xs(i); xs(i) = xs(j); xs(j) = t
    }
    def sort1(l: Int, r: Int) {
        val pivot = xs((l + r) / 2)
        var i = l; var j = r
        while (i <= j) {
            while (xs(i) < pivot) i += 1
            while (xs(j) > pivot) j -= 1
            if (i <= j) {
                swap(i, j)
                i += 1
                j -= 1
            }
        }
        if (l < j) sort1(l, j)
        if (j < r) sort1(i, r)
    }
    sort1(0, xs.length - 1)
}

1 つは機能的なスタイルです。

def sort(xs: Array[Int]): Array[Int] = {
  if (xs.length <= 1) xs
  else {
    val pivot = xs(xs.length / 2)
    Array.concat(
      sort(xs filter (pivot >)),
           xs filter (pivot ==),
      sort(xs filter (pivot <)))
  }
}

関数型のスタイルが命令型のスタイルよりも優れていることは明らかです。それは簡潔さです。しかし、パフォーマンスはどうですか?再帰を使用するため、C などの他の命令型言語と同じように、パフォーマンスのペナルティを支払う必要がありますか? または、Scala はハイブリッド言語であり、"Scala 方式" (関数型) が優先されるため、より効率的です。

注: 著者は、機能的なスタイルはより多くのメモリを使用することに言及しました。

4

1 に答える 1

12

場合によります。Scala ソースを見ると、パフォーマンスを向上させるために「内部」で使用される命令型スタイルがよくありますが、多くの場合、まさにこれらの微調整により、パフォーマンスの高い関数型コードを記述できます。したがって、通常は十分に高速な機能的なソリューションを考え出すことができますが、注意して、何をするか (特にデータ構造に関して) を理解する必要があります。たとえば、2 番目の例の配列 concat は適切ではありませんが、おそらくそれほど悪くはありませんが、ここでリストを使用して ::: でそれらを連結するのはやり過ぎです。

しかし、実際にパフォーマンスを測定しなければ、それは知識に基づく推測にすぎません。複雑なプロジェクトでは、パフォーマンスを予測するのは非常に困難です。特に、オブジェクトの作成やメソッドの呼び出しなどは、コンパイラや JVM によってますます最適化されるためです。

機能的なスタイルから始めることをお勧めします。遅すぎる場合は、プロファイリングします。通常、より機能的な解決策があります。そうでない場合は、最後の手段として命令型スタイル (または両方の組み合わせ) を使用できます。

于 2010-07-25T11:47:05.817 に答える