пʼятниця, 11 жовтня 2013 р.

Быстрая сортировка: Функциональные vs Императивные ЯП


Разбираюсь со 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;

Как видно, на функциональных яп программа выглядит гораздо короче.
Зато программы на императивных яп работают быстрее. Каждый предназначен для своих нужд. Процитирую статью из викиучебника:
Поэтому суть пользы и вреда лени в данном случае сформулируем так: «Программы пишутся быстрее, но работают они медленнее». Это прекрасный компромисс, поскольку человеко-часы часто существенно дороже часов работы компьютера!

Немає коментарів:

Дописати коментар