Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Шпоры к ГОСАМ (все билеты).doc
Скачиваний:
0
Добавлен:
01.04.2025
Размер:
1.56 Mб
Скачать

Типы кластеров:

  • Класс I. Класс машин строится целиком из стандартных деталей, которые продают многие поставщики компьютерных компонентов (низкие цены, простое обслуживание, аппаратные компоненты доступны из различных источников).

  • Класс II. Система имеет эксклюзивные или не слишком широко распространенные детали. Таким образом можно достичь очень хорошей производительности, но при более высокой стоимости.

Кластеры могут существовать в различных конфигурациях. Наиболее распространенными типами кластеров являются: системы высокой надежности; системы для высокопроизводительных вычислений; многопоточные системы. Отметим, что границы между этими типами кластеров до некоторой степени размыты, и кластер может иметь такие свойства или функции, которые выходят за рамки перечисленных типов. Более того, при конфигурировании большого кластера, используемого как система общего назначения, приходится выделять блоки, выполняющие все перечисленные функции.

Билет 2

Вопрос 1. Функциональные языки программирования. Представление и интерпретация функциональных программ. Отладка функциональных программ: функции трассировки, организация процесса отладки. Рекурсивные функции. Основы теории рекурсивных функций, виды рекурсии, рекурсия и итерации.

Особенности. Основной особенностью функционального программирования, определяющей как преимущества, так и недостатки данной парадигмы, является то, что в ней реализуется модель вычислений без состояний. Если императивная программа на любом этапе исполнения имеет состояние, то есть совокупность значений всех переменных, и производит побочные эффекты, то чисто функциональная программа ни целиком, ни частями состояния не имеет и побочных эффектов не производит. То, что в императивных языках делается путём присваивания значений переменным, в функциональных достигается путём передачи выражений в параметры функций. Непосредственным следствием становится то, что чисто функциональная программа не может изменять уже имеющиеся у неё данные, а может лишь порождать новые путём копирования и/или расширения старых. Следствием того же является отказ от циклов в пользу рекурсии.

Сильные стороны. Привлекательная сторона вычислений без состояний — повышение надёжности кода за счет чёткой структуризации и отсутствия необходимости отслеживания побочных эффектов. Любая функция работает только с локальными данными и работает с ними всегда одинаково, независимо от того, где, как и при каких обстоятельствах она вызывается. Невозможность мутации данных при пользовании ими в разных местах программы исключает появление труднообнаруживаемых ошибок (таких, например, как случайное присваивание неверного значения глобальной переменной в императивной программе). Отсутствие состояний даёт возможность применять к функциональным программам достаточно сложные методы автоматической оптимизации, которые непригодны для программ с состояниями и побочными эффектами. Ещё одним преимуществом функциональных программ является то, что они предоставляют широчайшие возможности для автоматического распараллеливания вычислений. Поскольку отсутствие побочных эффектов гарантировано, в любом вызове функции всегда допустимо параллельное вычисление двух различных параметров — порядок их вычисления не может оказать влияния на результат вызова. Ещё одной, традиционно упоминаемой положительной особенностью функционального программирования является то, что оно позволяет описывать программу в декларативном стиле; программист может сосредоточиться на описании отношений, в которых результат вычислений находится с исходными данными. Последовательность операций, приводящая к вычислению результата, в явном виде не описывается, а получается автоматически.

Недостатки функционального программирования вытекают из тех же самых его особенностей. Отсутствие присваиваний и замена их на порождение новых данных приводят к необходимости постоянного выделения и автоматического освобождения памяти, поэтому в системе исполнения функциональной программы обязательным компонентом становится высокоэффективный сборщик мусора. Для эффективности сборки мусора необходимо отслеживать ссылки на данные, что также требует затрат времени и памяти. Отсутствие циклов для промышленных программ является очень серьёзным ограничением — далеко не все виды рекурсии могут быть автоматически преобразованы в цикл.

В качестве основных свойств функциональных языков программирования обычно рассматриваются следующие:

* краткость и простота;

* строгая типизация;

* модульность;

* функции — объекты вычисления;

* чистота (отсутствие побочных эффектов);

* отложенные (ленивые) вычисления.

Некоторые языки функционального программирования:

* Лисп

* Haskell

* Nemerle

* Clean

* ML

o OCaml

o F#

* Flang

* Erlang

* Miranda

Рекурсия

Рекурсивным называется объект, частично состоящий или определяемый с помощью самого себя. В математике рекурсия применяется повсеместно, например, следующее определение рекурсивно: "0 является натуральным числом; натуральное число плюс единица есть натуральное число".

С точки зрения реализации различают прямую рекурсию (когда процедура содержит ссылку на себя) и косвенную (когда процедура содержит ссылку на другую процедуру, содержащую ссылку на исходную). Более подробная информация о рекурсии вообще может быть найдена в источнике (Вирт, 1986).

В большинстве процедурных языков программирования рекурсия является скорее нежелательным методом программирования (вследствие возможности переполнения стека), однако в Lisp она является не только допустимой без ограничений, но, более того, является основным инструментом организации циклов. В чистом Lisp операторы DO и LOPP, а тем более PROG отсутствуют, и должны быть заменены рекурсией. Здесь мы рассмотрим основные приёмы использования этого механизма.

Простейшая рекурсия заключается в том, что некоторая функция вызывает сама себя для проведения некоторых вычислений. Рекурсия отражает рекуррентные соотношения (когда в ряду значений каждое новое вычисляется на основании предыдущих). Простейшим примером является факториал, который задаётся в частности как n!=n* (n-1)!. В примере с функцией DO было рассмотрено вычисление факториала с помощью цикла. С помощью рекурсии можно вычислить его так:

>(defun fc2 (x)

(cond

((eql x 0) 1)

((eql x 1) 1)

(T (* x (fc2 (- x 1))))))

FC2

>(fc2 5)

120

>(defun lst2 (x lst)

(cond

((eql x 0) lst)

(T (lst2 (- x 1) (cons x lst)))))

LST2

>(LST2 5 nil)

(1 2 3 4 5)

Как видно из примера, рекурсивный вызов полностью отражает рекуррентное соотношение, описывающее факториал. Тем не менее, можно с помощью такого же метода организовывать и циклы, например, в следующем примере функция создаёт список из элементов от 1 до n

Основной проблемой, которая может возникнуть при работе с таким механизмом, является бесконечная рекурсия. Бесконечная рекурсия возникает вследствие логических ошибок в программе и приводит к зацикливанию алгоритма и исчерпанию системных ресурсов машины. Примером бесконечной рекурсии может быть следующее выражение:

>(defun err1 () (err1))

ERR1

>(err1)

Error: Stack overflow (signal 1000)

[condition type: SYNCHRONOUS-OPERATING

-SYSTEM-SIGNAL]

В этом примере функция ERR1 выполняет свой рекурсивный вызов всегда, тем самым, вызывая переполнение стека и ошибку. Для того, чтобы избегать подобного, необходимо следовать следующим простым правилам:

  • В теле функции всегда должно присутствовать правило, гарантирующее выход из рекурсии.

  • Правила, гарантирующие выход из рекурсии, должны предшествовать правилам, содержащим рекурсию

Даже при соблюдении таких правил зацикливание всё равно возможно, поэтому необходимо использовать отладку. Отладка рекурсивных функций является, вообще говоря, не слишком простым делом. Для неё используется функция TRACE

Отладка

trace

(trace функция) Функция позволяет выводить на экран дерево вызовов некоторой функции.

untrace

(untrace функция) Функция позволяет отменить режим вывода дерева вызовов некоторой функции, установленный функцией trace.

Подобный механизм способен существенно упростить отладку рекурсивных функций. К сожалению, не все версии Lisp позволяют выполнять отладку нескольких функций одновременно.

Типы рекурсий

  • Параллельная рекурсия. Такая рекурсия возникает, когда в теле некоторой функции f1 содержится вызов функции f2, в качестве аргументов которой используется вызов функции f1.

  • Взаимная рекурсия. Возникает при вызове в теле функции f1 функции f2, в теле которой, в свою очередь, существует вызов функции f1.

  • Вложенная рекурсия - возникает, когда функция в своём теле вызывает саму себя, используя себя же в качестве параметра этого вызова.

> (defun akk (m n)

(cond

((eql m 0) (+ 1 n))

((eql n 0) (akk (- m 1) 1))

(T (akk (- m 1) (akk m (- n 1))))))

AKK

>(akk 2 2)

7


Вложенная рекурсия является достаточно редкой. Обычно этот вид рекурсии иллюстрируется с помощью специализированных математических функций, таких как функции Аккермана

Такая функция определяется следующим образом:

A(0,n)=n+1;

A(m,0)=A(m-1,1); (m>0)

A(m,n)=A(m-1,A(m,n-1)); (m,n>0)

Очевидно, что функция Lisp, вычисляющая функцию Аккермана, будет построена в соответствии с определением:

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]