И.Г. Головин, И.А. Волкова - Языки и методы программирования (1160773), страница 2
Текст из файла (страница 2)
Особенно этоотносится к третьей части учебника.1.2. Языки и основные парадигмыпрограммированияКак уже отмечалось, спектр применения современных компьютеров очень широк, поэтому общая совокупность разработанныхкомпьютерных программ весьма велика и с трудом поддается классификации. Тем не менее, все программы (как и деятельность по ихсозданию) можно разделить на дна больших типа: программы «длясебя» и программы «для других».Создание программ «для себя» назовем (несколько условно)научно-развлекательным программированием. Люди создают программы для развлечения (когда развлечением является сам процесспрограммирования, а не использование уже готовых программ),для изучения программирования (студенты, выполняющие заданияпрактикума работы на ЭВМ), для экспериментальных расчетов,демонстрирующих работоспособность математических моделей (физики, математики, химики, биологи и т.д.), для решения каких-либосиюминутных задач (ученые, инженеры).При научно-развлекательном программировании число создателейпрограммы крайне невелико (как правило, один-два человека), также невелико число пользователей программы (как правило, толькоавторы и небольшое число коллег).
Такие программы невелики поразмеру (тысячи и реже несколько десятков тысяч строк), «живут»(т.е. активно используются) они недолго (дни, месяцы, реже годы)и в процессе жизни редко модифицируются.Языки научно-развлекательного программирования должныбыть легко изучаемыми, несложными для понимания и реализации.6Основной критерий их качества — удобство применения для соответствующих целей (учебы, науки, развлечения).
Самые известныеи широко используемые до сих пор языки — Фортран, Бейсик,Паскаль.Создание программ «для других», т. е. программ, специальносоздаваемых в расчете на эксплуатацию пользователями, не имеющими отношения к авторам, значительно отличается от научноразвлекательного. Такие программы называют программнымипродуктами, а процесс их создания — индустриальным программированием.Число разработчиков программного продукта варьируется в широких пределах (как правило, их десятки, а иногда и сотни), то жеотносится и к его размерам (сотни тысяч и миллионы строк кода),и к числу пользователей. Успешные программные продукты «живут»годы (иногда десятилетия) и в процессе использования активно модифицируются (исправляются обнаруженные в ходе их эксплуатацииошибки, добавляются новые функции, выполняется адаптация кновому оборудованию или программному окружению).Языки индустриального программирования отличаются от языков научно-развлекательного программирования.
Первые сложнее визучении и реализации, включают в себя большое число концепцийи понятий, обладают объемными библиотеками.В рамках нашего курса сосредоточимся на основных понятияхсовременных языков индустриального программирования.Важным свойством индустриальных языков является наличиеизобразительных средств, поддерживающих различные стили программирования.
Совокупность идей и понятий, определяющих стильпрограммирования, называется парадигмой программирования.Основополагающую роль в парадигме программирования играетименно язык программирования, поскольку именно на нем мы выражаем то, как мы «мыслим».В настоящее время в индустриальном программировании активноиспользуются императивная и объектная парадигмы. Есть основанияполагать, что в ближайшее время начнет активно использоватьсяфункциональная парадигма.Рассмотрим вкратце эти парадигмы и соответствующие примеры программ.
Для иллюстрации приведем небольшие программы,решающие одну и ту же задачу, используя разные стили программирования.Задача состоит в обращении входной последовательности. В стандартном канале ввода (обычно это клавиатура компьютера) заданапоследовательность символов. Требуется выдать в стандартный каналвывода элементы входной последовательности в обратном порядке(т. е. последний элемент входной последовательности первым, предпоследний — вторым и т.д.).
Будем считать, что эта последовательность «не очень длинная», точнее предполагаем, что все элементы7могут поместиться в оперативную память компьютера. Такое предположение упрощает решение задачи.Императивная парадигмаИмперативная парадигма (другое ее название процедурная)основана на фон-неймановской модели компьютера, названной вчесть предложившего ее математика Дж. фон Неймана. Модель фонНеймана до сих пор является основой большинства современныхархитектур, что обусловило популярность и доминирование императивной парадигмы.Модель содержит три основных компонента:• центральное процессорное устройство (ЦПУ);• оперативная память (ОН);• устройства ввода-вывода (У ВВ).Оперативная память — это однородная последовательность ячеек.Каждая ячейка имеет свой номер, называемый адресом.
Адресацияначинается с нуля. ЦПУ считывает или записывает информациюв ячейку, используя ее адрес. Скорость доступа ко всем ячейкамодинаковая и она не зависит от адреса. Такие последовательности впрограммировании называют массивами. Ячейки могут содержатькак данные (числа или символы), так и команды (т.е. команды закодированы с помощью чисел).Устройства ввода-вывода позволяют обмениваться информациеймежду ЦПУ и окружающей средой.«Мозг» компьютера — ЦПУ состоит из двух частей: арифметикологического устройства (АЛУ), которое выполняет арифметическиеи логические команды (сложение, вычитание, умножение, деление,побитовые сдвиги, побитовая инверсия и т.д.), и устройства управления, которое отвечает за порядок выборки, декодирование ивыполнение команд.
ЦПУ содержит ряд специальных ячеек (не изОП), называемых регистрами, каждая из которых выполняет своюроль в работе ЦПУ. Например, регистр команд содержит текущуювыполняемую команду, регистр адреса — адрес этой команды, а варифметико-логических регистрах находятся операнды арифметикологических команд.Команды ЦПУ подразделяются следующим образом:• пересылки между ОП и регистрами ЦПУ;• арифметико-логические команды;• команды управления, включающие в себя команды перехода и некоторые специальные команды (например, команду останова);• команды ввода-вывода — концептуально они похожи на командыпересылки (в некоторых архитектурах команды ввода-вывода отсутствуют и реализуются как команды пересылки для выделеннойобласти ОП).8Команды выбираются из памяти и выполняются последовательно одна за другой.
Исключение составляют команды условного ибезусловного переходов, содержащие адрес команды, которая будетвыполняться следующей, и позволяющие изменять нормальную последовательность выполнения команд.Основные понятия императивных языков программирования(ИЯП) представляют собой абстракции основных понятий фоннеймановской модели. В самом деле, любой ИЯП включает в себяпонятие переменной (в языке Паскаль — VAR X: Integer, в языкеС — i n t х), понятие операции (А * в — в любом языке), понятиеоператора (оператор цикла, оператор присваивания и др.).Понятие простой переменной абстрагирует понятие ячейкипамяти. Кроме простых переменных в императивном языке содержатся составные (т.
е. состоящие из других переменных) массивы изаписи (в ряде языков записи называются структурами). Понятиеоперации обобщает арифметико-логические команды. Неслучайнопочти для любой операции в ИЯП можно найти прототип — команду в машинном языке. Понятие оператора абстрагирует общеепонятие команды. Операторы в императивном языке делятся натри группы:• оператор присваивания;• операторы управления;• операторы ввода-вывода.Основным оператором в любом императивном языке являетсяоператор присваивания, имеющий видV :=Егде V — это переменная; Е — выражение.Выражение — это средство комбинирования операций для вычисления некоторого значения (например, X * (Y+1)/2).
Выполнениеоператора присваивания состоит в вычислении значения выраженияЕ и пересылке вычисленного значения в ячейку (или ячейки) ОП,соответствующую переменной V. Таким образом, оператор присваивания в ИЯП может представляться последовательностью команд пересылки и арифметико-логических команд (и даже командперехода). Это действительно основной оператор в императивныхязыках.Операторы управления (циклы, операторы выбора, перехода и т.п.)абстрагируют машинные команды перехода.Операторы ввода-вывода обобщают машинные команды вводавывода.Подробнее основные понятия ИЯП разбираются в гл. 5 нашегоучебника.Мы видим, что императивные языки концептуально близки машинной архитектуре, поэтому программирование на таких языкахпозволяет весьма эффективно управлять поведением компьютеров.9Это объясняет популярность и распространенность ИЯП.
В индустриальном программировании в настоящее время доминируют либочисто императивные языки (такие, как С), либо языки со смешаннойобъектно-императивной парадигмой (C++, Java, С#, Delphi, ObjectiveС и многие другие).Решим нашу пробную задачу реверсирования входной последовательности в императивном стиле на языке С. Для этого стиляхарактерно представление алгоритма решения задачи в виде последовательности шагов, каждый из которых в свою очередь представляетсяпоследовательностью более мелких шагов, и т.д., пока не получимшаг «размером» в один оператор.Многие задачи по обработке данных (в том числе и наша) сводятсяк следующим трем шагам:• подготовить данные;• обработать данные;• завершить.Условие нашей задачи позволяет хранить всю входную последовательность в ОП, поэтому шаг подготовки данных сводится к вводуданных в некоторую структуру данных, которая позволяет выбиратьданные и в прямом порядке, и в обратном (для обращения).Шаг обработки сводится к реверсированию этой структуры данных, шаг завершения — к выводу обращенной структуры.Основной вопрос — какую структуру данных выбрать? Нашимтребованиям отвечает, например линейный двунаправленный список, однако такой встроенной структуры в языке С нет.
Работу сосписками в языке С должен реализовывать сам программист. Единственное понятие в языке С, соответствующее последовательностиоднородных элементов, это массив. Проблема в том, что массивыв языке С — это последовательности фиксированной и заранееизвестной длины. Для того чтобы упростить решение задачи, предположим, что «не очень длинная» последовательность содержит неболее 1024 символов. Тогда мы можем использовать массив символовсоответствующей длины.Прежде чем привести полное решение, заметим, что можно объединить шаг обработки и завершения: вместо обращения элементовв массиве, можно сразу перейти к выводу, только выводить элементыследует не с начала, а с конца.
В этом случае выполнение становитсянесколько более быстрым (вместо двух операторов цикла мы имеемодин в объединенном шаге).#include <stdio.h>#define MAX_ELEMENTS 1024char Input[MAX_ELEMENTS];int m a i n (){int current, count = 0;10while ((current = getcharO) != EOF)if (count == MAX_ELEMENTS) {fprintf(stderr, "Слишком много символов");return 1;} elseInput[count++] = current;for (int i = count-1; i >= 0; i--)putchar(Input[i] ) ;return 0;}Первая строка данной программы сообщает о включении информации о библиотеке стандартного ввода-вывода, в следующих двухстроках объявляются константа MAX_ELEMENTS (предельная длинавходной последовательности) и массив Input для хранения последовательности (заметим, что в языке С элементы массива индексируются снуля).