Методические указания студенту к лабораторной работе по теме «Парадигмы и языки программирования» Ниже приведены четыре возможных подхода к написанию алгоритма «быстрой сортировки» на четырёх разных языках программирования. Справа от каждого блока кода приведено его краткое описание. Также обязательно нужно ознакомиться с полными версиями этих программ, которые раздал преподаватель. Для более детальных справок по синтаксису языков, а также по более глубоким аспектам различных парадигм программирования, рекомендуется воспользоваться свободно доступной в интернете информацией. Императивный процедурный подход (на примере языка C) void swap(int i, int j) { int t = x[i]; x[i] = x[j]; x[j] = t; } Процедура quicksort принимает два целых числа — минимальный l и максимальный u индексы в глобальном массиве x, который нужно отсортировать в этом диапазоне. void quicksort(int l, int u) { int i, m; if(l >= u) return; swap(l, randint(l, u)); m = l; for(i = l+1; i <= u; i++) if(x[i] < x[l]) swap(++m, i); swap(l, m); quicksort(l, m-1); quicksort(m+1, u); } Её работа заключается в следующем: выбирается случайным образом из нашего диапазона число, ставится на первую позицию. Потом последовательным сравнением в начало массива попадают все числа меньше него. После них идёт наше сравниваемое число, а дальше до конца массива идут оставшиеся числа, те, которые больше нашего числа. После этого эти две половинки массива (с мéньшими числами и с бóльшими) по отдельности сортируются таким же образом. Императивный объектно-ориентированный подход (на примере языка C#) public class SortableArray { private int[] x; public SortableArray(int[] values) { x = values; } public void Sort() { quickSort(0, x.Length - 1); } public int[] Values { get { return x; } } private void swap(int i, int j) { int t = x[i]; x[i] = x[j]; x[j] = t; } private void quickSort(int l, int u) { int i, m; if (l >= u) return; swap(l, new Random().Next(l, u + 1)); m = l; for (i = l + 1; i <= u; i++) if (x[i] < x[l]) swap(++m, i); swap(l, m); quickSort(l, m - 1); quickSort(m + 1, u); } Сердце этого кода, по большому счёту, такое же, как в предыдущем примере. Но здесь из разряда глобальных переменных сортируемый массив x перешёл в приватные переменные класса, также детальная реализация quickSort() скрыта за публичным методом Sort(). Конструктор класса инициализирует массив x переданными ему значениями, публичное свойство Values позволяет воспользоваться результатами сортировки извне, метод Sort() просто вызывает сортировку всего массива. } 1 Декларативный функциональный подход (на примере языка Haskell) quicksort [] = [] quicksort (p:aaa) = quicksort [ y | y <- aaa, y < p ] ++ [p] ++ quicksort [ y | y <- aaa, y >= p ] Посмотрим, как реализована функция сортировки списка quicksort построчно. В первой строке мы указываем, что отсортированный пустой список — это пустой список. Это условие нужно для предотвращения бесконечной рекурсии. В последней строчке кода мы пишем, что для остальных случаев мы воспринимаем переданный список как первый элемент p и все остальные элементы aaa, а результат сортировки склеиваем из отсортированных y, где y берутся из aaa, притом только такие, чтобы были меньше p; самого p; а также отсортированных элементов списка больше или равных p. Декларативный логический подход (на примере языка Prolog) quicksort([X|Xs],Ys) :partition(Xs,X,Left,Right), quicksort(Left,Ls), quicksort(Right,Rs), appendLists(Ls,[X|Rs],Ys). quicksort([],[]). partition([X|Xs],P,[X|Ls],Rs) :X =< P, partition(Xs,P,Ls,Rs). partition([X|Xs],P,Ls,[X|Rs]) :X > P, partition(Xs,P,Ls,Rs). partition([],P,[],[]). Правило quicksort чем-то похоже на аналогичную функцию из предыдущего примера. Только оно в первую очередь разделяет по правилу partition первым элементом остаток списка на две половинки — Left и Right, содержащие числа меньше сравниваемого числа и больше него, соответственно. Эти половинки сортируются по отдельности, а после чего склеиваются в результирующий отсортированный список Ys. appendLists([],Ys,Ys). appendLists([X|Xs],Ys,[X|Zs]) :appendLists(Xs,Ys,Zs). Источники Бесплатно скачать GNU Prolog можно с сайта http://www.gprolog.org, а интерпретатор Hugs 98 для Haskell можно загрузить с сайта http://haskell.org/hugs. Доступных инструментов для работы с языками C и C# можно найти довольно много. В частности, для первого можно воспользоваться Dev-Cpp, для второго подойдёт стандартный компилятор csc.exe в составе .NET Framework. Самостоятельная работа 1. Ознакомиться с работой вышеприведённых программ «вживую», вставляя в исходный код программы данные из файлов «dataN.txt». a. В системе Hugs файл открывается командой :load “<Путь к файлу>\quicksort.hs”, после чего пишется название вызываемой функции — main. b. В GNU Prolog файл открывается командой consult(“<Путь к файлу>\quicksort.pro”). (точка обязательна) c. В остальных средах способ загрузки файла зависит от того, какой продукт вы используете, обычно программу требуется скомпилировать и запустить. 2. Написать на этих четырёх языках программирования процедуру/класс/функцию/правило, позволяющее подсчитать, сколько раз в заданном массиве встречается искомое число. Выдержать программу в рамках своей парадигмы. На C реализовать программу в рамках двух подходов: в императивном процедурном и декларативном функциональном. 3. Запустить написанные программы с исходными данными из файлов, в соответствии со своим вариантом. 2 Вариант I II III IV V VI VII VIII IX X Файл data1.txt data2.txt data3.txt data4.txt data5.txt data1.txt data2.txt data3.txt data4.txt data5.txt Число 1 2 3 4 5 6 7 8 9 10 4. Сообщить преподавателю получившийся ответ и подготовить к устной защите пять исходных файлов, выполненных по заданию. Что нужно знать Понимать принципы работы «быстрой сортировки» и её реализаций в примерах. Понимать и уметь объяснять собственный код. Понимать, чем отличается императивный подход от декларативного. Уметь определять признаки процедурного, объектно-ориентированного, функционального и логического программирования. Знать и понимать основные принципы ООП. Уметь строить рекурсивные алгоритмы. 3