12.07.2015 Views

Выпуск 14

Выпуск 14

Выпуск 14

SHOW MORE
SHOW LESS

Create successful ePaper yourself

Turn your PDF publications into a flip-book with our unique Google optimized e-Paper software.

СЕМИНАР38сентябрь 2011 / ИНФОРМАТИКАязыке. Выполняя эти задания, машина представляетсобой автомат — она имеет набор состояний и выполняетпереходы между этими состояниями по заданнымправилам. Например, вы можете объявитьпеременную a и присвоить ей некоторое значение,например, 3. Затем вы можете изменить ее. Такжевы можете применить целый набор управляющихструктур для более хитрого описания действий.Кроме того, вы можете определить несколько функций,которые будут, по сути, обобщением вашихприказаний и управляющих структур (здесь необходимосделать важное уточнение: в современнойлитературе принято называть такие функции блоками,а термин функция оставлять для математическичистых функций, о которых вы прочитаетедалее). Описанная схема напрямую соответствуетмодели машины фон Неймана.В чисто функциональных языках, и в языкеHaskell в первую очередь, вы увидите совершенноиное. В них вы не говорите машине, как она должнадействовать, вы описываете ей, что вы хотитеполучить. Простой пример: факториал. Вы говорите:“Факториал — это произведение всех целыхчисел от 1 до заданного. Сумма набора чисел — этосумма первого числа и суммы оставшихся чисел,и т.д.”. Важным фактом является то, что все данныев чисто функциональных языках неизменяемы(immutable). Вы не можете поменять значение переменной,которой один раз уже было что-то присвоено(если вас возмущает этот факт, подумайтео переменной не как о ячейке памяти, а как о переменнойв математическом смысле, дальше вы увидите,что неизменяемость никак не связывает вамруки). И, наконец, одна из самых важных концепцийфункционального программирования: функциидолжны быть чистыми. Никаких измененийвнешнего контекста программы: изменения переменных,безусловных переходов, — внутри функциипроисходить не должно. Функция описываетсяв строго математическом смысле, как отображениеиз одного множества в другое, и для одних и тех жевходных данных она должна вернуть один и тот жерезультат (для любопытных читателей скажу, чтодля реализации ввода-вывода, генерации случайныхчисел и прочих “нечистых” задач существуютспециальные формализмы, подробнее о них можнопрочитать в [4, 5, 6]). Чистота функций обеспечиваеттри важных преимущества: широкие возможностиоптимизации кода, простоту верификации (доказательстваправильности) программ, что имеетвесьма важное значение при тестировании, и простотуиерархического проектирования (в первуюочередь проектирования снизу вверх): разработкипрограмм из маленьких составных частей.Особенности, описанные в предыдущем абзаце,в принципе могут подходить для любогофункционального языка программирования.Теперь пару слов о том, что выделяет Haskell средиостальных функциональных языков (кстати,именно в Haskell нижеописанные возможностидостигли своего совершенства). Haskell — этоленивый язык. Ленивость языка означает, чтозначения функций и переменных не будут вычисленыдо тех пор, пока они не понадобятсяна самом деле, полезная работа откладываетсяна самый последний момент (тут некоторыечитатели могут вспомнить свое студенчество).Какие преимущества это обес печивает? Одиниз немаловажных плюсов ленивости, особеннозаметный в современном мире многопоточныхмногопроцессорных систем, — это оптимизациявыполнения участков кода по времени и оптимальноераспределение работы между потокамии процессами. Важный теоретический аспектленивости — возможность создания бесконечныхструктур данных, множеств контиуальноймощности, так распространенных в математике(другой подход к описанию подобных структур— это так называемые генераторы, впервыепоявившиеся в языке Icon и успешно перекочевавшиев Python). Кроме того, ленивые языкимогут представить выражение как самое настоящеечистое преобразование данных. Для пониманияэтого приведем небольшой пример. У насесть список xs = [1, 2, 3, 4] и функцияdoubleMe, удваивающая все элементы в списке ивозвращающая новый список. Также представим,что у нас есть выражение doubleMe(doubleMe(doubleMe(xs))). Каким будет вычисление этоговыражения в неленивом языке? Третья функцияdoubleMe удвоит значения в списке и отдастновый список второй функции, вторая функцияудвоит значения в полученном списке и вернетновый список первой функции, первая функцияудвоит значения в новом полученном списке ивернет результат. Все это произойдет ровно в тотмомент, в который компилятор или интерпретаторвстретит это выражение. В ленивом языкеэто выражение начнет вычисляться только тогда,когда его результат действительно будет нужен,например, при выводе на экран. При этом перваяфункция попросит вторую вернуть результат, втораяпопросит третью, третья возьмет первый элементсписка, удвоит его и возвратит второй, втораяудвоит его и возвратит первой, первая удвоитего и возвратит внешней функции, затем потребуетследующий элемент и так далее. Заметьте,что в данной схеме список проходится один раз ине имеет дополнительных промежуточных представлений.Вот что называется чистым преобразованиемданных: мы можем гарантировать, чтонаше выражение преобразует список в список ине усложнит ситуацию созданием его дополнительныхпредставлений.В заключение обзора специфических возможностейязыка Haskell необходимо сказать о статическойсистеме типов, унаследованной им изсемейства языков ML (изобретенных Робином

Hooray! Your file is uploaded and ready to be published.

Saved successfully!

Ooh no, something went wrong!