Программирование с нуля - это совсем просто! 175) Функциональное программирование и Лисп
Школа программирования
175) Функциональное программирование и Лисп : Функции
Последний выпуск по Лиспу, был N 171.
Сегодня мы узнаем, как создаются полноценные программы на Лиспе. Пока мы запускали отдельные операторы, кусочки кода, в интерактивной среде newLISP. Теперь мы создадим законченный модуль кода, который можно использовать в других проектах как отчуждаемую подпрограмму.
Но сначала - арифметические функции.
Четыре арифметические операции над целыми числами (дробные числа в элементарном Лиспе не поддерживаются :) называются в Лиспе + (сложение), - (вычитание), * (умножение), / (целочисленное деление) и % (остаток от целочисленного деления).
Эти функции могут иметь множество аргументов. Функцию QUOTE к аргументам применять не надо.
Примеры:
(+ 1 2 3 4 5) равно атому 15
(* 1 2 3 4 5) равно атому 120
(- 10 7) равно атому 3
(- 10 -7) равно атому 17
(- 10 7 3) равно атому 0
(/ 10 3) равно атому 3
(/ 16 2 2) равно атому 4
(% 10 3) равно атому 1
Соответственно, в качестве аргументов можно использовать другие арифметические выражения:
(* (+ 1 2) (- 5 3) ) равно атому 6
Запись функций
В Лиспе, как и в Си, существуют только функции, которые всегда вычисляют некоторое значение. Любая программа на классическом Лиспе представляет собой функцию, но для удобства впоследствии в его диалекты были добавлены более привычные механизмы поддержки законченных программ.
В общем случае функция Лиспа записывается так:
(lambda (список-параметров) выражение)
Список параметров - это просто набор атомов, фактически - имена переменных, которые используются в выражении. Значение этого выражения и считается значением функции.
Слово lambda (можно также записать сокращение fn, от "функция") выбрано потому, что язык Лисп - это так называемый функциональный язык, реализующий логику лямбда-вычислений Чёрча.
Например, опишем функцию, которая имитирует работу стандартной функции first. Так как в диалекте newLISP нет стандартной функции CAR (оригинального аналога функции first в классическом Лиспе), то мы ее и реализуем для наглядности.
(lambda (X) (first X))
Как вызывать такую функцию? Хотя мы решили назвать ее CAR, пока данная наша функция анонимна. В ней есть только список параметров и тело.
Лисп допускает следом за определением функции сразу задавать конкретные значения ее параметров. Так сделано, потому что исходно Лисп проектировался как интерактивный язык, ориентированный на сеансовую работу с терминалом. Написал выражение и сразу получил нужный результат.
Параметр должен быть представлен как атом или список, с помощью функции QUOTE.
Например:
((lambda (X) (first X)) ' (1 2 3) )
Здесь (1 2 3) - список, который подставляется вместо параметра функции (условной переменной X). Обратите внимание, что в скобки он не берется! Если его заключить в скобки, то Лисп, как и положено, расценит эти скобки как выражение с вызовом функции.
Таким образом, вызывается выражение (first ' (1 2 3)), и его значением становится атом 1 .
Самые наблюдательные уже, наверное, подметили, что в этом определении функции отсутствует вроде бы самое главное - ее имя. Но так как пока не задан контекст вызова функций, их имена тоже особого значения не имеют.
Итак, с помощью LAMBDA мы можем определить некоторую пользовательскую функцию. Чтобы дать ей имя, надо воспользоваться стандартной функцией define (в классическом Лиспе - SEXPR). Синтаксис ее таков:
(define имя-функции lambda-описание-функции)
Например:
(define CAR (lambda (X) (first X)))
Теперь мы можем пользоваться функцией CAR так же, как и функцией first:
(CAR ' (A B (C)) )
Предварительный вызов функции QUOTE перед значением итогового аргумента говорит о том, что весь аргумент, перед тем как будет передан в пользовательскую функцию CAR, подлежит вычислению. То есть допустима, например, и такая запись:
(CAR (last ' (A B (C))) )
Перед вызовом функции CAR сначала вычисляется ее аргумент - выражение (last ' (A B (C))), значением которого будет список (C). Именно этот список, результат вычисления, и передается функции CAR в качестве аргумента. Это общепринятая практика и в других языках программирования.
Структура программы на Лиспе (его базовой, или элементарной, версии) крайне проста. Она представляет собой последовательность lambda-описаний функций и их вызовов с параметрами. Каждая функция поочередно вызывается, и результат ее работы сразу выводится на консоль. А главным механизмом управления последовательностью вычислений в Лиспе считается рекурсия (вызов функцией самой себя), а не условные и циклические вычисления, как принято в массовых языках программирования наподобие Си или Паскаля.
В элементарном Лиспе их просто нету, кроме некоего условного прообраза cond!
Рассмотрим рекурсию на стандартном примере - определим функцию вычисления факториала. Факториал целого числа N - это перемноженные друг на друга значения 1, 2, ..., (N-2), (N-1), N.
Например, факториал числа 5 равен 1*2*3*4*5 = 120.
Расчитать факториал можно с помощью оператора цикла, однако в элементарном Лиспе таких операторов нету. Поэтому алгоритм расчета факториала надо переписать в рекурсивной форме. Он может быть таким:
Факториал-числа( N ) равен:
- единице (1), если N = 1;
- выражению (N * Факториал-числа( N-1 )) , если N больше 1.
В таком случае вычисление факториала произойдет автоматически. Например, по нашему алгоритму
Рекурсивное определение как бы самораскручивается, автоматически вычисляя самое себе. Разработчику фактически достаточно лишь указать рекурсивное определение. Но создание рекурсивных алгоритмов, с другой стороны, требует иной формы мышления, нежели при использовании общеизвестных универсальных языков программирования (в них, впрочем, рекурсия обычно тоже допустима, но не применяется так активно, как в Лиспе).
Запишем этот алгоритм на языке Лисп:
; N - параметр, как переменная :
(define FAKTORIAL (lambda (N)
; условие :
(cond
; если N <= 1 , то возвращаем единицу...
((<= N 1) 1)
; в противном случае...
(true
; ...возвращаем N*FAKTORIAL(N-1)
(* N (FAKTORIAL (- N 1)) ))
) ) )
В следующий раз мы рассмотрим более привычное управление последовательностью действий в Лиспе, которое было в него добавлено из обычных языков программирования.
(c) 2004-2007 Сергей Бобровский : bo собака russianenterprisesolutions.com
Все эти учебные курсы рассчитаны не только на разработчиков, но и на всех тех, кто хочет стать ИТ-менеджером. Для этого как минимум нужно иметь общее представление о современных технологиях разработки и их истории и владеть соответствующей терминологией. Именно это мои книги и дают.
В учебных курсах описаны десятки технологий, каждой из которых посвящены отдельные книги. Таким образом, купив один учебный курс, вы существенно сэкономите :) В книгах полностью описаны:
- Delphi (версия 2006, полностью совместимая с Delphi 2005/2006/2007 и Turbo Delphi) для обеих платформ - Win32 и .NET;
- C# (новый язык Microsoft, на котором базируется платформа .NET и все новые версии Windows);
- C++ для платформы Win32.
Охвачены также темы работы с файлами на этих платформах, создания файл-серверных, клиент-серверных, распределенных приложений, веб-программ (Indy, ASP.NET, веб-сервисы). Описаны языки SQL и OCL. Немало глав посвящены истории программирования и различных технологий. Особое внимание уделено созданию программ с помощью технологии ECO и языка моделирования UML - программы фактически рисуются, и теперь даже для создания корпоративных приложений и их переноса в Интернет не обязательно знать программирование!
Отдельная часть отведена технологиям организации групповой работы, управления требованиями, контроля версий, локализации и тестирования.
Тут подробнее про книги.