Разбираюсь со Scala, так как он является гибридом императивного и функционального языков программирования, захотелось разобраться в функциональном программировании (масло маслянное :)). После прочтения статьи Hello Haskell, Goodbye Scala, захотелось познакомиться еще и с Haskell, так как это чистый функциональный яп. Очень понравилась статья Язык Haskell: О пользе и вреде лени После её прочтения, становится более понятным синтаксис Scala (он видимо у хаскеля многое слямзил) да и вообще суть функциональных яп. В конце статьи приводится пример Быстрой сортировки на C и Haskell. Сегодня просматирвал ScalaByExample и какраз увидел там реализацию этого алгоритма на Scala в императивном и функциональном стиле. Решил их все собрать в одном месте:
БЫСТРАЯ СОРТИРОВКА
Haskell:
qsort [] = []
qsort (x:xs) = qsort [y | y <- xs, y < x ] ++ [x] ++ qsort [y | y <- xs, y >= x]
Erlang:
qsort([]) -> [];
qsort([Pivot|Rest]) ->
qsort([ X || X <- Rest, X < Pivot]) ++ [Pivot] ++ qsort([ Y || Y <- Rest, Y >= Pivot]).
Scala (функциональный стиль):
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 <)))
}
}
Scala (императивный стиль):
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)
}
C:
void qsort(int *ds, int *de, int *ss){
int vl = *ds, *now = ds + 1, *inl = ss, *ing = ss + (de - ds);
if(de <= ds + 1) return;
for(; now != de; ++now){
if(*now <= vl) *inl++ = *now;
else *ing-- = *now;
}
*++inl = vl;
qsort(ds, ds + (inl - ss), ss);
qsort(ds + (inl - ss), de, inl + 1);
}
Pascal:
// Реализация на языке pascal
// Если раскомментировать строки кода, начинающиеся с "//",
// то получится реализация с одной рекурсивной ветвью, в которой
// меньшая часть разделённого массива сортируется рекурсивным вызовом,
// а бо'льшая - в цикле, что гарантирует глубину рекурсии не более lg(N).
procedure qSort(var ar: array of real);
// Вложенная функция сортировки для рекурсивного вызова
// Нужна, чтобы не передавать в вызов основной функции границы массива
procedure sort(var ar: array of real; low, high: integer);
var i, j: integer;
m, wsp: real;
begin
// repeat
i:=low; j:=high; m:=ar[(i+j) div 2]; // Взятие среднего опорного элемента
repeat
while ar[i]<m do Inc(i);
while ar[j]>m do Dec(j);
if i<=j then begin
wsp:=ar[i]; ar[i]:=ar[j]; ar[j]:=wsp;
Inc(i); Dec(j);
end;
until i>j;
// if (j - low) < (high - i) then begin
if low<j then sort(ar, low, j);
// low := i;
// end
// else begin
if i<high then sort(ar, i, high);
// high := j;
// end;
//until low = high;
end;
begin
sort(ar, 0, High(ar));
end;
Как видно, на функциональных яп программа выглядит гораздо короче.
Зато программы на императивных яп работают быстрее. Каждый предназначен для своих нужд. Процитирую статью из викиучебника:
Поэтому суть пользы и вреда лени в данном случае сформулируем так: «Программы пишутся быстрее, но работают они медленнее». Это прекрасный компромисс, поскольку человеко-часы часто существенно дороже часов работы компьютера!
Немає коментарів:
Дописати коментар