\documentclass[a4paper,fontsize=14bp]{article} \input{settings/common-preamble} \input{settings/fancy-listings-preamble} \input{settings/bmstu-preamble} %\setcounter{secnumdepth}{0} \numerationTop \begin{document} \thispagestyle{empty} \makeBMSTUHeader % ... работе, номер, тема, предмет, ?а, кто \makeReportTitle{лабораторной}{№ 1}{Введение}{Программное обеспечение телекоммуникационных систем}{}{И.М.Сидякин} \newpage \sloppy \thispagestyle{empty} \tableofcontents \newpage \pagestyle{fancy} \section{Цель} Знакомство с языком Erlang, средой исполнения программ на языке. \section{Задание на часть 1} \begin{enumerate} \item Сделайте модуль с именем \code{fib}, в файле \code{fib.erl}. \item Создайте, не используя хвостовую рекурсию, функцию \code{fib_p} которая получает один аргумент \code{N}, и возвращает \code{N}-е значение последовательности Фибоначчи. \item Создайте другую функцию без хвостовой рекурсии \code{fib_g} по той же спецификации, используя сторожевые последовательности. \item Создайте третью функцию \code{tail_fib} по той же спецификации, но с хвостовой рекурсией. (Для хвостовой рекурсии понадобится вспомогательная функция). \end{enumerate} \section{Выполнение} \subsection{Сопоставление с образцом} Код выполненной лабораторной работы возможно найти по ссылке на \href{https://git.iovchinnikov.ru/ivan-igorevich/erlang-labs}{репозиторий}. Работа выполнена на двух устройствах (с синхронизацией работы через репозиторий): \begin{verbatim} Debian 11: - IDEA 2022.3.1 CE - ErlangPlugin 0.11.1162. - Erlang/OTP 23 [erts-11.1.8] [64-bit] Ubuntu 22.04: - IDEA 2022.3.2 CE - ErlangPlugin 0.11.1162. - Erlang/OTP 25 [erts-13.0.4] [64-bit] \end{verbatim} Основные синтаксические конструкции, использованные при написании функций взяты из материалов к лабораторной работе, со слайда 1-27 презентации. Код написанного модуля, вычисляющего число Фибоначчи приведён в листинге \hrf{lst:fib-p}, код юнит-теста написанного модуля в листинге \hrf{lst:fib-p-test}, а снимок экрана, демонстрирующий успешность пройденных тестов на рисунке \hrf{pic:fib-p-pow}. \begin{lstlisting}[language=Erlang, style=CCodeStyle, caption={Код модуля}, label={lst:fib-p}] -module(fib). -export([fib_p/1]). % <@\lh{dkgreen}{Сопоставление с образцом}@> fib_p(0) -> 0; fib_p(1) -> 1; fib_p(N) -> fib_p(N - 1) + fib_p(N - 2). \end{lstlisting} \begin{lstlisting}[language=Erlang, style=CCodeStyle, caption={Код тестов модуля}, label={lst:fib-p-test}] -module(fib_test). -include_lib("eunit/include/eunit.hrl"). fib_test_() -> [ {"Test fib_p", fun test_fib_p/0 } ]. test_fib_p() -> ?assertEqual(0, fib:fib_p(0)), ?assertEqual(1, fib:fib_p(1)), ?assertEqual(1, fib:fib_p(2)), ?assertEqual(2, fib:fib_p(3)), ?assertEqual(3, fib:fib_p(4)), ?assertEqual(5, fib:fib_p(5)), ?assertEqual(8, fib:fib_p(6)), ?assertEqual(13, fib:fib_p(7)), ?assertEqual(21, fib:fib_p(8)), ?assertEqual(34, fib:fib_p(9)), ?assertEqual(55, fib:fib_p(10)). \end{lstlisting} \begin{figure}[H] \centering \includegraphics[width=16cm]{04-tss-01-lab-fib-p.png} \caption{Запуск и успешное выполнение тестов} \label{pic:fib-p-pow} \end{figure} \subsection{Сторожевая последовательность и ключевое слово \code{when}} Основные синтаксические конструкции, использованные при написании функций взяты из материалов к лабораторной работе, со слайда 1-28 презентации и из \href{https://www.erlang.org/doc/getting_started/seq_prog.html#matching,-guards,-and-scope-of-variables}{документации} к языку. Код написанного модуля, вычисляющего число Фибоначчи с помощью сторожевой последовательности приведён в листинге \hrf{lst:fib-g}, код юнит-теста написанного модуля в листинге \hrf{lst:fib-g-test}, а снимок экрана, демонстрирующий успешность пройденных тестов для обеих функций на рисунке \hrf{pic:fib-g-pow}. \begin{lstlisting}[language=Erlang, style=CCodeStyle, caption={Код модуля}, label={lst:fib-g}] -module(fib). -export([fib_p/1, fib_g/1]). % <@\lh{dkgreen}{Сопоставление с образцом}@> % ... % <@\lh{dkgreen}{Сторожевая последовательность}@> fib_g(N) when N < 1 -> 0; fib_g(N) when N < 2 -> 1; fib_g(N) -> fib_g(N - 1) + fib_g(N - 2). \end{lstlisting} \begin{lstlisting}[language=Erlang, style=CCodeStyle, caption={Код тестов функции}, label={lst:fib-g-test}] -module(fib_test). -include_lib("eunit/include/eunit.hrl"). fib_test_() -> [ {"Test fib_p", fun test_fib_p/0 }, {"Test fib_g", fun test_fib_g/0 } ]. % test_fib_p()/0 -> ... test_fib_g() -> ?assertEqual(0, fib:fib_g(0)), ?assertEqual(1, fib:fib_g(1)), ?assertEqual(1, fib:fib_g(2)), ?assertEqual(2, fib:fib_g(3)), ?assertEqual(3, fib:fib_g(4)), ?assertEqual(5, fib:fib_g(5)), ?assertEqual(8, fib:fib_g(6)), ?assertEqual(13, fib:fib_g(7)), ?assertEqual(21, fib:fib_g(8)), ?assertEqual(34, fib:fib_g(9)), ?assertEqual(55, fib:fib_g(10)). \end{lstlisting} \begin{figure}[H] \centering \includegraphics[width=10cm]{04-tss-01-lab-fib-g.png} \caption{Запуск и успешное выполнение тестов} \label{pic:fib-g-pow} \end{figure} \subsection{Использование хвостовой рекурсии} Основные синтаксические конструкции, использованные при написании функций взяты из материалов к лабораторной работе, со слайда 1-31 презентации и из \href{https://ru.wikipedia.org/wiki/Хвостовая_рекурсия}{статьи} в Wikipedia. Код написанного модуля, вычисляющего число Фибоначчи с помощью сторожевой последовательности приведён в листинге \hrf{lst:fib-g}, код юнит-теста написанного модуля в листинге \hrf{lst:fib-g-test}, а снимок экрана, демонстрирующий успешность пройденных тестов трёх функций на рисунке \hrf{pic:fib-g-pow}. \begin{lstlisting}[language=Erlang, style=CCodeStyle, caption={Код модуля}, label={lst:fib-t}] -module(fib). -export([fib_p/1, fib_g/1, tail_fib/1]). % <@\lh{dkgreen}{Сопоставление с образцом}@> % ... % <@\lh{dkgreen}{Сторожевая последовательность}@> % ... % <@\lh{dkgreen}{Хвостовая рекурсия}@> tail_fib(N) -> tfib(N, 0, 1). tfib(0, Result, Next) -> Result; tfib(N, Result, Next) -> tfib(N - 1, Next, Result + Next). \end{lstlisting} \begin{lstlisting}[language=Erlang, style=CCodeStyle, caption={Код тестов функции}, label={lst:fib-t-test}] -module(fib_test). -include_lib("eunit/include/eunit.hrl"). fib_test_() -> [ {"Test fib_p", fun test_fib_p/0 }, {"Test fib_g", fun test_fib_g/0 }, {"Test tail_fib", fun test_tail_fib/0 } ]. % test_fib_p()/0 -> ... % test_fib_g()/0 -> ... test_tail_fib() -> ?assertEqual(0, fib:tail_fib(0)), ?assertEqual(1, fib:tail_fib(1)), ?assertEqual(1, fib:tail_fib(2)), ?assertEqual(2, fib:tail_fib(3)), ?assertEqual(3, fib:tail_fib(4)), ?assertEqual(5, fib:tail_fib(5)), ?assertEqual(8, fib:tail_fib(6)), ?assertEqual(13, fib:tail_fib(7)), ?assertEqual(21, fib:tail_fib(8)), ?assertEqual(34, fib:tail_fib(9)), ?assertEqual(55, fib:tail_fib(10)). \end{lstlisting} \begin{figure}[H] \centering \includegraphics[width=10cm]{04-tss-01-lab-fib-t.png} \caption{Запуск и успешное выполнение тестов} \label{pic:fib-t-pow} \end{figure} \subsection{Проверка времени выполнения} Основные синтаксические конструкции, использованные при написании функций взяты из \href{https://www.erlang.org/doc/man/timer.html#tc-3}{документации} к языку и \href{https://www.tutorialspoint.com/erlang/erlang_loops.htm}{статьи}. Код функций, вычисляющих время исполнения функций приведён в листинге \hrf{lst:fib-time}, снимки экрана, демонстрирующие время выполнения функций рисунке \hrf{pic:fib-t-time}. \begin{lstlisting}[language=Erlang, style=CCodeStyle, caption={Время выполнения}, label={lst:fib-time}] say() -> io:format("The result is: ~p~n", [tail_fib(10)]). exec_time(Depth) -> Start = os:timestamp(), tail_fib(Depth), io:format("Depth = ~p, Time ~f sec~n", [Depth, (timer:now_diff(os:timestamp(), Start) / 1000)]). for(0, _, _) -> []; for(N, Term, Step) when N > 0 -> exec_time(N), [Term | for(N - Step, Term, Step)]. start_time_chack() -> for(50000, 5000, 5000). \end{lstlisting} \begin{figure}[H] \centering \begin{subfigure}[b]{0.47\textwidth} \centering \includegraphics[width=\textwidth]{04-tss-01-lab-fib-time-p.png} \caption{\code{fib_p}} \label{pic:fib-p-time} \end{subfigure} \hfill \begin{subfigure}[b]{0.47\textwidth} \centering \includegraphics[width=\textwidth]{04-tss-01-lab-fib-time-t.png} \caption{\code{tail_fib}} \label{pic:fib-tail-time} \end{subfigure} \caption{Время выполнения функций} \label{pic:fib-t-time} \end{figure} \subsection{Контрольные вопросы} \begin{enumerate} \item Начиная с \code{N = 20} увеличивать \code{N} с шагом \code{5} до тех пор, пока вычисление \code{fib_p(N)} будет занимать меньше пяти секунд. При каком \code{N} это условие перестает выполняться? Почему так происходит? Время исполнения превышает \code{5} секунд при подсчёте 30-го значения Фибоначчи. Это происходит, потому что при прямом подсчёте чисел Фибоначчи рекурсивным способом происходит повторное вычисление каждого числа, входящего в состав вычисляемого и дерево рекурсивных вызовов разрастается квадратично. \item Сколько времени тратится на вычисление \code{tail_fib(10000)}? Почему? На вычисление 10000-го значения Фибоначчи функцией с хвостовой рекурсией тратится значительно меньше времени (1,371 сек), поскольку функция переиспользует вычисленные на ранних этапах значения для вычисления каждого следующего, что делает её больше похожей на простой цикл в процедурном программировании. \end{enumerate} \section{Задание на часть 2} Квадраты простых чисел и функция Мёбиуса \begin{enumerate} \item Создайте модуль с именем \code{mobius}, в файле \code{mobius.erl}. \item Создайте функцию \code{is_prime}, которая получает аргумент \code{N} и возвращает \code{true}, если число простое или \code{false}, если число не является простым. \item Сделайте функцию \code{prime_factors}, которая получает аргумент \code{N} и возвращает список простых сомножителей \code{N}. \item Сделайте функцию \code{is_square_multiple}, которая получает аргумент \code{N}, и возвращает \code{true}, если аргумент делится на квадрат простого числа или \code{false}, если не длится. \item Наконец, сделайте функцию \code{find_square_multiples(Count, MaxN)}. Эта функция получает \code{Count} -- длину последовательности чисел делящихся на квадрат простого числа, и \code{MaxN} -- максимальное значение, после которого надо прекратить поиск. \begin{itemize} \item Если функция находит \code{Count} чисел подряд, делящихся на квадрат простого числа в диапазоне \code{[2, MaxN]}, она должна вернуть первое число из этой последовательности. \item Если функция не находит серию из \code{Count} чисел делящихся на квадрат простого числа в этом диапазоне, она должна вернуть атом fail. \end{itemize} \item \label{item:task-work} Найдите первый ряд из чисел делящихся на квадрат простого числа длиной 4, длиной 5, и длиной 6. Нужно выбрать значение \code{MaxN} равное 30000. Программа должна завершить вычисления в пределах одной минуты. \end{enumerate} \section{Выполнение} \subsection{Функция вычисления простого числа} Функция последовательно перебирает все числа от «корня из искомого + 1» до 2, возвращая ложь при $N < 2$. Если найдено число, остаток от деления на которое искомого даёт 0 -- искомое не считается простым. В работе используется встроенная в язык функция \code{trunc()}, отсекающая дробную часть передаваемого в аргументе числа. Код функции приведён в листинге \hrf{lst:is-prime}, сниок экрана, демонстрирующий корректность выполнения функции (с использованием тестов) на рисунке \hrf{pic:is-prime}. Во время работы также была написана функция для проведения ручного тестирования, представленная на рисунке \hrf{pic:say-prime}, демонстрирующем, что число 1111 не является простым.. \begin{figure}[H] \centering \includegraphics[width=10cm]{04-tss-01-lab-say-prime.png} \caption{Ручная проверка работы фукнции} \label{pic:say-prime} \end{figure} \begin{lstlisting}[language=Erlang, style=CCodeStyle, caption={Код функции}, label={lst:is-prime}] %% region is_prime is_prime(N) when N < 2 -> false; is_prime(N) -> is_prime(N, 2, trunc(math:sqrt(N)) + 1). is_prime(_, Max, Max) -> true; is_prime(N, I, Max) -> if N rem I =:= 0 -> false; true -> is_prime(N, I + 1, Max) end. %% endregion \end{lstlisting} \begin{figure}[H] \centering \includegraphics[width=10cm]{04-tss-01-lab-is-prime.png} \caption{Запуск и успешное выполнение тестов} \label{pic:is-prime} \end{figure} \subsection{Вычисление сомножителей} Функция имеет ограничение и выполняется только для тех чисел, на которые делится без остатка исходное (\code{when X rem N == 0}) с 2 до искомого, складывая в список все значения, меньшие, чем «корень из искомого + 1». Код функции приведён в листинге \hrf{lst:prime-factors}, сниок экрана, демонстрирующий корректность выполнения функции на рисунке \hrf{pic:prime-factors}. \begin{lstlisting}[language=Erlang, style=CCodeStyle, caption={Код функции}, label={lst:prime-factors}] %region factor prime_factors(N) -> prime_factors(N, 2, []). prime_factors(X, N, Primes) when X rem N == 0 -> prime_factors(trunc(X / N), 2, [N | Primes]); prime_factors(X, N, Primes) -> case N < math:sqrt(X) + 1 of true -> prime_factors(X, N + 1, Primes); false -> [X | Primes] end. %endregion \end{lstlisting} \begin{figure}[H] \centering \includegraphics[width=15cm]{04-tss-01-lab-prime-factors.png} \caption{Демонстрация работы функции} \label{pic:prime-factors} \end{figure} \subsection{Функция вычисления делимости на квадрат простого числа} Функция перебирает список полученных функцией \code{prime_factors} множителей на наличие повторений, последовательно выделяя головной элемент списка и вызывая функцию \code{lists:member}\footnote{\href{https://www.tutorialspoint.com/erlang/erlang_member.htm}{примеры использования функции}}. Для пустого списка функция возвращает ложь. Например, для числа 111 (\hrf{is-sq-mul1}) функция возвращает ложь, а для 10000 (\hrf{is-sq-mul2}) истину. Код функции представлен в листинге \hrf{lst:is-sq-mul}, а результаты проверки представлены на рисунке \hrf{pic:is-sq-mul} \begin{lstlisting}[language=Erlang, style=CCodeStyle, caption={Код функции}, label={lst:is-sq-mul}] %% region is_square_multiple is_square_multiple(N)-> is_square_multiple_fun(prime_factors(N)). is_square_multiple_fun([H | T]) -> case lists:member(H, T) of true -> true; false -> is_square_multiple_fun(T) end; is_square_multiple_fun([]) -> false. %% endregion is_square_multiple \end{lstlisting} \begin{figure}[H] \centering \begin{subfigure}[b]{0.47\textwidth} \centering \includegraphics[width=\textwidth]{04-tss-01-lab-mul1.png} \caption{} \label{pic:is-sq-mul1} \end{subfigure} \hfill \begin{subfigure}[b]{0.47\textwidth} \centering \includegraphics[width=\textwidth]{04-tss-01-lab-mul2.png} \caption{} \label{pic:is-sq-mul2} \end{subfigure} \caption{} \label{pic:is-sq-mul} \end{figure} \subsection[Функция-обёртка \code{find\_square\_multiples}]{Функция-обёртка \code{find_square_multiples}} Функция работает с ограничением по длине найденных значений в списке (\code{when length(FoundSquareMultipleNumber) == Count}) и последовательно уменьшает максимальное число до которого нужно выполнять поиск (ищет не с начала, а с конца). Для максимального значения 2 -- нужно прекратить поиск, в этом случае функция выводит атом \code{fail}. Если проверка \code{is_square_multiple(TestNumber)} возвращает истину, число прибавляется (конкатенируется) к списку уже найденных. Код функции представлен в листинге \hrf{lst:find-sq-mul}. Для проверки работы был написан тест с предложенными в методическом пособии значениями (листинг \hrf{lst:sq-mul-test}). Тест пройден успешно (рисунок \hrf{pic:sq-mul-test}). \begin{lstlisting}[language=Erlang, style=CCodeStyle, caption={Код тестов}, label={lst:sq-mul-test}] test_find_square_multiples() -> ?assertEqual(fail, mobius:find_square_multiples(3, 20)), ?assertEqual(48, mobius:find_square_multiples(3, 50)). \end{lstlisting} \begin{figure}[H] \centering \includegraphics[width=12cm]{04-tss-01-lab-sq-mul-test.png} \caption{Результат прохождения теста} \label{pic:sq-mul-test} \end{figure} \begin{lstlisting}[language=Erlang, style=CCodeStyle, caption={Код функции}, label={lst:find-sq-mul}] %% region find_square_multiples find_square_multiples(Count, MaxN) -> find_square_multiples_fun(Count, MaxN, []). find_square_multiples_fun(Count, CurrentNumber, FoundSquareMultipleNumber) when length(FoundSquareMultipleNumber) == Count -> CurrentNumber + 1; find_square_multiples_fun(_, 2, _) -> fail; find_square_multiples_fun(Count, TestNumber, FoundSquareMultipleNumber) -> case is_square_multiple(TestNumber) of true -> FoundsNumber = FoundSquareMultipleNumber ++ [TestNumber]; false -> FoundsNumber = [] end, find_square_multiples_fun(Count, TestNumber - 1, FoundsNumber). %% endregion find_square_multiples \end{lstlisting} Для выполнения п. \hrf{item:task-work} был описан цикл, последовательно увеличивающий число находимых множителей. Результат работы цикла с замерами времени приведён на рис. \hrf{pic:final} \begin{figure}[H] \centering \includegraphics[width=17cm]{04-tss-01-lab-final.png} \caption{Результат выполнения задания} \label{pic:final} \end{figure} \end{document}