Заметки о преподавании парадигм программирования1

Вид материалаСтатья

Содержание


Императивная парадигма программирования
Функциональная парадигма программирования
Логическая парадигма программирования
Подобный материал:

Заметки о преподавании

парадигм программирования1

Шилов Н.В.


Введение

Поводом для этих заметок послужила статья в журнале для старшеклассников и учителей «Потенциал», в которой (по мнению автора и редакции журнала) «в сжатой форме рассказывается про функциональный подход к описанию вычислительных процессов ..., а также про применение этого подхода в информатике в функциональной парадигме программирования» с примерами функциональных программ на языке Haskall [Душкин, 2009]. Цель благая тем более, что на русском языке фактически нет научно-популярной литературы по функциональному программированию и другим парадигмам программирования, отличным от императивного.

Такая цель не могла оставить равнодушным, так как автору данных заметок приходится знакомить с понятием о разных парадигмах программирования (включая функциональное программирование) студентов-математиков разного уровня: от первого курса до магистратуры. Но трудно передать меру разочарования, которое пришлось испытать после знакомства со статьей: так НЕЛЬЗЯ знакомить ни школьников, ни учителей, ни студентов с функциональным программированием. В этой статье не объяснено ни что такое «парадигма программирования», ни что существуют десятки парадигм программирования, ни основы синтаксиса и семантики языка Haskell (ни кто такой Haskell Carry), ни слова не сказано про другие функциональные языки программирования...

Таким образом появилось естественное желание вынести на обсуждение сообщества преподавателей программирования и информационных технологий вопрос, как, когда и зачем популярно и доступно (но корректно) знакомить студентов с разными парадигмами программирования (в том числе – с функциональным программированием). Поэтому в этих заметках мы обсудим само понятие парадигм программирования, образовательное значение знакомства с разными парадигмами программирования и представим на суд коллег некоторый опыт преподавания первоначальных знаний о парадигмах программирования студентов-математиков первого курса. Можно надеемся, что этот опыт может пригодиться и для старшеклассников, интересующихся программированием и математикой, и для студентов младших курсов, обучающихся по разным направлениям информационных технологий, но имеющих некоторый уровень математической культуры. В тоже время, представленные здесь соображения и опыт открыты для критики в той же степени, в какой выше критиковалась статья [Душкин, 2009].

О парадигмах программирования

Согласно Т. Куну [Кун, 2003], парадигма – это метод, подход к формулировке задач (проблем) и путей их решения. Само слово «парадигма» греческого происхождения и означает «пример», «образец», а в общефилософском смысле обозначает категорию, состоящую из сущностей с общими характеристиками.

Первым, кто явно ввел в употребление понятие «парадигма программирования» был Роберт Флойд в лекции в 1978 г. по случаю присуждения ему премии им. Тьюринга [Флойд, 1993]. По-видимому, ранее это понятие использовалось в компьютерных науках лишь эпизодически. По крайней мере, в своей лекции сам Р. Флойда ссылается только на известную диссертацию Т. Куна по философии науки вообще, которая была опубликована в 1970 г.

Широко известен плакат History of Programming Languages, подготовленный издательством O’REILL2. Полная версия плаката имеет длину около 6м и содержит сведения о хронологии и влиянии друг на друга 2500 языков программирования и отражает хронологию и взаимное влияние друг на друга наиболее значимых языков программирования. Хронология на этом плакате представлена осью времени (в верхней части плаката), влияние языков друг на друга – цветными линиями.

Заметим, однако, что этот плакат можно принять за основу «путеводителя» в мире языков программирования лишь на начальном этапе становления программирования и информационно-вычислительных технологий (первые 10-15 лет начиная с 1950 г.). В это время компьютерных языков было немного, и почти все они3 были языками императивного программирования фон-неймановских вычислительных устройств. Поэтому за основу классификации в этот период можно было принять хронологию появления и взаимного влияния языков.

Но уже с конца 1960-х годов путеводитель в стиле плаката O'REILLY становится неприемлемым, так как в это время наряду с языками последовательного императивного программирования появились языки параллельного программирования, языки декларативного программирования (прежде всего продукционного и функционального); в середине 1970-х – начале 1980-х годов родилось несколько новых «программирований» – логическое и объектно-ориентированное в том числе.

Для специалистов очевидно, что все перечисленные «программирования» – это (в соответствии с Т. Куном и Р. Флойдом) исторически сложившиеся парадигмы программирования, т. е. разные способы ставить и решать задачи. Однако для студентов, начинающих изучение программирования, знакомых только с одним-двумя языками императивного программирования, все другие (кроме императивного) способы постановки и решения программистских задач просто неведомы. Необходимость знакомить их с многообразием парадигм программирования диктуется как соображениями истории науки, технологии и техники, так и более важными причинами.

Это, прежде всего, содействие в выборе языка программирования из существующих для новых проектов в области информационных систем и технологий; разработка новых языков, отвечающих новым потребностям и парадигмам4; и, наконец, образование.

Заметим, что, говоря про образование, мы имеем в виду не только приобретение навыков, знаний и квалификации, но и формировании ума5, менталитета будущих специалистов в области программирования и информационных технологий. Образовательное значение парадигм программирования чрезвычайно велико особенно для России, так как подавляющее большинство специалистов обычно из всего многообразия парадигмам программирования имеют представление только об императивном и объектно-ориентированном программировании. К сожалению, существующая система среднеспециального и высшего образования ориентирована на закрепление этой ситуации. Возможность познакомиться с другими парадигмами программирования как можно раньше (на первом курсе или даже в старших классах школы) могло бы существенно исправить ситуацию.

Такой разный факториал

Теперь мы кратко познакомим с опытом преподавания понятия о парадигмах программирования на первом курсе Механико-математического факультета Новосибирского государственного университета. Курс программирования читается во втором семестре, базовый язык программирования Си, другие языки не изучаются. Однако понятие императивного Pascal-подобного псевдокода вводится на самой первой лекции для документирования алгоритмов, которые потом предстоит кодировать на семинарских и практических занятиях на языке Си. А на второй лекции студенты знакомятся с некоторыми другими парадигмами программирования и соответствующим им вариантам псевдокода.

Начинаем мы, как уже было сказано, с императивного программирования. Минимальный набор «других» парадигм включает функциональное и логическое программирование, а иногда остаётся время познакомить студентов ещё и с (неограниченно-)параллельным, объектно-ориентированным и доказательным программированием.

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

Императивная парадигма программирования
  • «Императивный» - от латинского «командный».
  • Императивная постановка задачи: факториал натурального числа n – это произведение всех натуральных чисел от 1 до n.
  • Императивный псевдокод состоит из команд (операторов над памятью):

1: var n, f : integer ;

2: input(n) ;

3: f:= 1 ;

4: if n = 1 then goto 8 ;

5: f:= f*n ;

6: n:= n-1 ;

7: goto 4 ;

8: output(f) ;

9: end.
  • Команды преобразуют «ячейки памяти» ЭВМ.
  • Запуск императивной программы соответствует размещению ее в памяти и последовательному исполнению команд начиная с первой.
  • Например, вычисление факториала числа 3 может быть представлено следующей последовательностью моментальных состояний памяти ЭВМ:

Номер ячейки

1 - для номера команды

2

3

...

...

...

Момент времени

1

1

выделена для n

выделена для f

Свободная память

2

2

3

выделена для f

Свободная память

3

3

3

1

Свободная память

4

4

3

1

Свободная память

5

5

3

3

Свободная память

6

6

2

3

Свободная память

7

7

2

3

Свободная память

8

4

2

3

Свободная память

9

5

2

6

Свободная память

10

6

1

6

Свободная память

11

7

1

6

Свободная память

12

4

1

6

Свободная память

13

8

1

6

Свободная память

14

9

Свободная память


Функциональная парадигма программирования
  • В функциональной парадигме постановка задачи и решение задачи – определения функции, а вычисление – это цепочка равенств, где каждое следующее получается из предыдущего в результате применения функции к аргументу (или аргументам).
  • Функциональная постановка задачи: факториал – это функция F, которая каждому натуральному числу сопоставляет натуральное число и удовлетворяет следующему тождеству:

F(x) = если x=1 то 1 иначе x*F(x-1).
  • Функциональный псевдокод состоит из одного определения функции F, но включает два «клауза»:

F : 1  1 ;

x  x* F(x -1) .
  • Приведённая программа означает, что если конкретное значение аргумента функции F может быть отождествлено с 1, то значение функции F на этом аргументе равно 1; в противном случае надо значение функции F на этом аргументе равно значению аргумента, умноженному на значение функции F на декременте6 аргумента.
  • Никаких ячеек памяти нет, но есть память, в которой хранится функциональная программа.
  • Вызов функциональной программы состоит в применении одной из функций программы к конкретному значению аргумента.
  • Вычисление вызванной функциональной программы – это цепочка равенств, начинающаяся с вызова этой программы, заканчивающаяся значением.
  • Например, вычисление факториала числа 3:

F(3) = (3 нельзя отождествить с 1, но можно с x) =

= 3 * F(3 – 1) = 3 * F(2) = (2 нельзя отождествить с 1, но можно с x) =

= 3 * (2 * F(2 – 1) ) = 3 * (2 * F(1) ) = (1 отождествимо с 1) =

= 3 * (2 * 1 ) = 3 * 2 = 6.

Логическая парадигма программирования
  • В логической парадигме постановка задачи и решение задачи – аксиоматическое определение отношения между аргументами и результатами, а вычисление – это поиск доказательства.
  • Логическое определение факториал: отношение P(m, n)  «число m является факториалом числа n» определяется следующими двумя аксиомами:

P(1, 1) ;

P(m/n, n-1)  P(m, n).
  • Функциональный псевдокод состоит из одного факта и одного правила:

P (1 1) ;

P(m, n) :: P(m/n, n-1).
  • Вызов логической программы – это один из предикатов этой программы, в который подставлены значения некоторых аргументов (т. е. фактические параметры вместо формальных), а значения некоторых аргументов предстоит определить так, что при их подстановке получится утверждение, доказуемое в аксиоматике, соответствующей этой логической программе.
  • Унификация двух выражений, в логическом программировании – это подбор значений переменных в этих выражениях, при котором оба выражения синтаксически совпадут.
  • Вычисление по логической программе – это это поиск «кратчайшего» доказательства с использованием унификации. Никаких ячеек памяти нет, но есть память, в которой хранится сама логическая программа.
  • Например, вычисление факториала числа 3:

P(m, 3) :: P((m/3), 3 – 1) так как P(m, 3) невозможно унифицировать с фактом P(1, 1) :: P((m/3), 2) так как (3-1) есть 2 :: P(((m/3)/2), 2 – 1) так как P((m.3), 2) невозможно унифицировать с фактом P(1, 1) :: P(((m/3)/2), 1) так как (2-1) есть 1 :: ((m/3)/2) может быть 1 так как P(((m/3)/2), 1) можно унифицировать с фактом P(1, 1) :: (m/3) может быть 2 поскольку ((m/3)/2) может быть 1 :: m может быть 6 поскольку (m/3) может быть 2.

Вместо заключения

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

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

Многообразие компьютерных языков является распределенным хранилищем научного знания о парадигмах, опыте и истории применения информационных технологий. Для усвоения студентами этого знания необходимо иметь разумную систему классификации компьютерных языков и парадигм, с современной технической поддержкой. Подход к созданию такой классификации, основанный на построении открытой темпоральной прикладной онтологии предметной области компьютерных языков, намечен в работах [Андреева и др., 2008] и [Anureev at al., 2008].

Литература

[Душкин, 2009] Душкин Р.В. Функциональный подход в программировании. Потенциал, 2009, №8, стр.47-55.

[Кун, 2003] Кун Т. Структура научных революций. Издательство АСТ, 2003.

[Флойд, 1993] Флойд Р. О парадигмах программирования. В кн.: Лекции лауреатов премии Тьюринга. М: Мир, 1993.

[McCarthy, 1960] McCarthy J. Recursive Functions of Symbolic Expressions and Their Computation by Machine. Communications of ACM. 1960, v.3 (4).

[Андреева и др., 2008] Андреева Т.А., Ануреев И.С., Бодин Е.В., Городняя Л.В., Марчук А.Г., Мурзин Ф.А., Шилов Н.В. Компьютерные языки как форма и средство представления, порождения и анализа научных и профессиональных знаний. Труды XV Всероссийской научно-методической конференции «Телематика-2008». Санкт-Петербург. 2008.

[Anureev at al., 2008] Anureev I.S., Bodin E.V., Gorodnyaya L.V., Marchuk A.G., Murzin F.A., Shilov N.V. On the Problem of Computer Language Classification. Joint NCC&IIS Bulletin, Series Computer Science. 2008, v. 27.



1Работа подготовлена при поддержке гранта РФФИ 08-01-00899а и интеграционного проекта РАН 2/12.

2см. ly.com/news/graphics/prog_lang_poster.pdf

3За, может быть, единственным исключением – LISP [McCarthy, 1960].

4Роберт Флойд в своей Тьюринговской лекции 1978 г. по этому поводу сказал следующее: «To the designer of programming languages, I say: unless you can support the paradigms I use when I program, or at least support my extending your language into one that does support my programming methods, I don’t need your shiny new languages!»

5Русское слово «образование» многозначно: это не только приобретение навыков, знаний и квалификации, но и “формирование”. Такие же значения имеет немецкое слово Bildung. Для иллюстрации здесь уместно привести русский перевод с немецкого известной фразы Карла Вейерштрасса: «Цель среднего образования – образование ума, а не приобретение точных знаний». А вот английское education и французское éducation имеют значение «образование» и «воспитание», т. е. передают другой оттенок.

6Декремент – это операция вычитания единицы.