Soru iki bölümden oluşuyor. İlki kavramsaldır. Bir sonraki adım aynı soruya Scala'da daha somut bir şekilde bakıyor.
- Bir programlama dilinde yalnızca değişmez veri yapılarının kullanılması, belirli algoritmaların / mantığın uygulanmasını pratikte hesaplama açısından daha pahalı hale getirir mi? Bu, değişmezliğin tamamen işlevsel dillerin temel ilkelerinden biri olduğu gerçeğini ortaya çıkarır. Bunu etkileyen başka faktörler var mı?
- Daha somut bir örnek alalım. Quicksort genellikle bir bellek içi veri yapısı üzerinde değiştirilebilir işlemler kullanılarak öğretilir ve uygulanır. Böyle bir şey, değiştirilebilir versiyona kıyasla karşılaştırılabilir bir hesaplama ve depolama ek yükü ile PURE işlevsel bir şekilde nasıl uygulanır? Özellikle Scala'da. Aşağıda bazı kaba ölçütler ekledim.
Daha fazla detay:
Zorunlu programlama geçmişinden geliyorum (C ++, Java). Fonksiyonel programlamayı, özellikle de Scala'yı araştırıyorum.
Saf fonksiyonel programlamanın temel ilkelerinden bazıları:
- Fonksiyonlar birinci sınıf vatandaşlardır.
- Fonksiyonların yan etkileri yoktur ve bu nedenle nesneler / veri yapıları değişmezdir .
Modern JVM'ler nesne oluşturma konusunda son derece verimli olsalar ve kısa ömürlü nesneler için çöp toplama çok ucuz olsa da , nesne oluşturmayı en aza indirmek muhtemelen daha iyidir, değil mi? En azından eşzamanlılık ve kilitlemenin sorun olmadığı tek iş parçacıklı bir uygulamada. Scala hibrit bir paradigma olduğu için, gerekirse değiştirilebilir nesnelerle zorunlu kod yazmayı seçebiliriz. Ancak, nesneleri yeniden kullanmak ve tahsisi en aza indirmek için çok fazla yıl harcamış biri olarak. Buna bile izin vermeyecek düşünce okulunun iyi bir şekilde anlaşılmasını isterim.
Özel bir durum olarak, bu öğretici 6'daki bu kod parçacığı beni biraz şaşırttı . Quicksort'un Java sürümüne ve ardından aynı düzgün görünümlü Scala uygulamasına sahiptir.
İşte benim uygulamaları kıyaslama girişimim. Ayrıntılı profilleme yapmadım. Ama benim tahminim Scala sürümünün daha yavaş olduğu, çünkü tahsis edilen nesnelerin sayısı doğrusaldır (özyineleme çağrısı başına bir tane). Kuyruk arama optimizasyonlarının devreye girme şansı var mı? Haklıysam, Scala kendi kendini yinelemeli aramalar için kuyruk arama optimizasyonlarını destekler. Yani sadece ona yardım ediyor olmalı. Scala 2.8 kullanıyorum.
Java sürümü
public class QuickSortJ {
public static void sort(int[] xs) {
sort(xs, 0, xs.length -1 );
}
static void sort(int[] xs, int l, int r) {
if (r >= l) return;
int pivot = xs[l];
int a = l; int b = r;
while (a <= b){
while (xs[a] <= pivot) a++;
while (xs[b] > pivot) b--;
if (a < b) swap(xs, a, b);
}
sort(xs, l, b);
sort(xs, a, r);
}
static void swap(int[] arr, int i, int j) {
int t = arr[i]; arr[i] = arr[j]; arr[j] = t;
}
}
Scala versiyonu
object QuickSortS {
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 <)))
}
}
Uygulamaları karşılaştırmak için Scala Kodu
import java.util.Date
import scala.testing.Benchmark
class BenchSort(sortfn: (Array[Int]) => Unit, name:String) extends Benchmark {
val ints = new Array[Int](100000);
override def prefix = name
override def setUp = {
val ran = new java.util.Random(5);
for (i <- 0 to ints.length - 1)
ints(i) = ran.nextInt();
}
override def run = sortfn(ints)
}
val benchImmut = new BenchSort( QuickSortS.sort , "Immutable/Functional/Scala" )
val benchMut = new BenchSort( QuickSortJ.sort , "Mutable/Imperative/Java " )
benchImmut.main( Array("5"))
benchMut.main( Array("5"))
Sonuçlar
Art arda beş çalıştırma için milisaniye cinsinden süre
Immutable/Functional/Scala 467 178 184 187 183
Mutable/Imperative/Java 51 14 12 12 12
O(n)
list concat'ı kullanır . Sözde kod sürümünden daha kısa olsa da;)