Учебник_Информатика. Стандарт третьего поколениян. В. Макарова, В. Б. Волков
Скачать 14.49 Mb.
|
Пример. Рассмотрим предложение: «Это кошка страус зеленая». В этом примере все используемые в предложении слова и символы допустимы для русского языка, но вместе они составляют недопустимую конструкцию. Формализация описания языка программирования нужна для создания ма тематической или логической модели работы транслятора, то есть те правила, на основании которых транслятор будет производить синтаксический разбор. Од ним из известных) способов формального описания синтаксиса языка является метаязык — формы Бэкуса-Наура. Задание синтаксиса языка программирования сводится к перечислению всех его допустимых конструкций. Таким образом синтаксис связан только с перечислением всех конструкций язы ка, которые могут быть преобразованы транслятором в машинный код. Смысловая же составляющая языка программирования описывается понятием семантики. Пример. Рассмотрим предложение: «Зеленая кошка — это страус». Этот пример проходит синтаксическую проверку, поскольку предложение построено правиль но, но с семантической точки зрения он лишен смысла. Семантические модели для языков программирования могут быть операци онными (моделируется выполнение на абстрактной вычислительной машине), деривационными (модель описывается языком логических выражений) и денота ционными (модель описывается понятиями, близкими к математическим, такими как множества, утверждения, суждения). Синтаксические и семантические способы описания языков программирования служат основой для создания эффективных трансляторов. 556 Глава 19. Технологии и инструменты программирования Помимо программы-транслятора, разработка которой основана на формальной математической модели, можно выделить несколько менее формализованных требований к языку. □ Полнота. Язык программирования должен исчерпывающе описывать синтаксис и семантику, то есть все допустимые конструкции языка. □ Ясность. Язык программирования должен быть понятен человеку. Несколько мощных языков программирования, которым сулили прекрасное будущее, не были востребованы программистами из-за того, что программы на этих языках получались слишком сложными и не наглядными. □ Естественность. Условные обозначения, термины и конструкции языка должны быть близки программисту. Возможно, язык программирования, записываемый с помощью иероглифов, выглядел бы очень компактно и красиво, но если ие роглифическое письмо не свойственно нашей культуре, то для нас такой язык будет неестественным. □ Реализм. Реализация языка программирования должна учитывать фактические условия его использования. Язык программирования, предполагающий наличие нереальных объемов памяти или нереальной производительности процессора компьютера, востребован не будет, так же как язык программирования, про граммы на котором выполняются слишком медленно. □ Наличие среды разработки. Многие программисты создают программы в про стейшем текстовом редакторе и отлаживают из командной строки. Однако более быстрая и менее затратная технология разработки программ возможна только при наличии специальных сред и соответствующего инструментария. □ Низкая стоимость использования. Необходимо оценивать затраты на написание программ, их отладку и последующее выполнение. Это часто становится опре деляющим фактором при выборе языка программирования. Перечислим основные понятия, свойственные большинству языков програм мирования. □ Переменная — область памяти, адрес (имя) которой можно использовать для получения или изменения хранящегося в ней значения. Переменные могут быть простыми, то есть не иметь внутренней структуры, и сложными, то есть иметь внутреннюю структуру, к элементам которой возможен доступ. □ Константа — область памяти, адрес (имя) которой можно использовать для получения хранящегося в ней значения. □ Инструкция — наименьшая автономная часть языка программирования. Про грамма представляет собой последовательность инструкций. □ Подпрограмма — поименованный фрагмент программы, который может быть неоднократно вызван и выполнен в процессе выполнения программы. □ Функция — подпрограмма, которая может быть использована в выражении. □ Процедура — любая подпрограмма, не являющаяся функцией. □ Оператор — специальный вид функции, записываемый в виде символа. Спо соб записи операторов делает их сходными с математическими операторами, 19.1. Основные понятия и классификация языков программирования 557 а возможность перегрузки (то есть замены действия, которое выполняется над операндами другим действием) делает операторы похожими на функции. Пример. Оператор + в математике и в языках программирования традиционно обозначает операцию сложения двух операндов. Однако в некоторых языках программирования (например, в Pascal) эту операцию можно выполнять над строками и в случае строковых операндов оператор + реализует конкатенацию (соединение) двух строк в одну. □ Выражение — последовательность операций, результат выполнения которых может быть присвоен переменной. □ Тип данных — относительно устойчивая и независимая совокупность данных, которую можно выделить в рассматриваемом множестве. Есть два базовых типа данных: числовые (бинарные) и символьные. К первому типу данных относятся все виды чисел (целые, дробные, положительные, отрицательные), ко второму — символы и строки. Каждый язык определяет свое множество как числовых, так и символьных данных. Логический тип данных, представляющий собой тип с двумя значениями (true или fal se, 1 или 0), в одних языках выделяется в отдель ный тип, в других реализуется через числовые типы. Кроме того, могут существо вать сложные типы данных, состоящие из комбинации простых типов. Деление данных на типы удовлетворяет две потребности: во-первых, каждый тип данных имеет свой размер в памяти. Когда задается переменная определенного типа, то ясно, сколько места нужно выделить для хранения значения этой переменной и как она будет там представлена. Во-вторых, тип данных определяет, какие опе рации можно производить над этими данными. Например, можно перемножить между собой два числа, но нельзя проделать эту операцию над двумя строками. 19.1.2. Классификация языков программирования По способу выполнения языки программирования делятся на компилируемые, интерпретируемые, компилируемые на основе псевдокода и совмещенные. □ Компилируемые языки. К этой группе относят языки программирования, ис ходный код которых преобразуется специальной программой-компилятором в объектные модули, которые затем собираются при помощи программы сборки (линковщика) в единый загружаемый модуль. Этот модуль представляет собой выполняемую программу. Компилируемыми являются языки С, C++, Object Pascal. □ Интерпретируемые языки. К этой группе относят языки программирования, исходный код которых считывается и выполняется специальной программой- интерпретатором инструкция за инструкцией. Интерпретируемыми являются большинство версий языков Basic и Forth. □ Языки, компилируемые на основе псевдокода («шитого» кода). В этих языках исходный код программы компилируется и выполняется как последователь ность вызовов подпрограмм из существующих библиотек. Таковыми являются некоторые версии Basic. 558 Глава 19. Технологии и инструменты программирования □ Совмещенные языки. В совмещенных языках исходный код проходит две стадии обработки. На первой стадии происходит компиляция исходного кода до уровня промежуточного языка (байт-код в Java или язык MSIL в технологии .NET). На второй стадии происходит интерпретация промежуточного кода (в Java этим занимается виртуальная машина Java) или докомпиляция промежуточного кода до выполняемого машинного кода (эту функцию осуществляет среда вы полнения CLR в технологии .NET). По факту созданию процесса языки программирования делятся на создающие процесс и сценарные. □ Язы ки, создающие процесс. После запуска программы создается отдельный процесс выполнения этой программы. Так происходит в языках С, C++, Oblect Pascal. □ Сценарные языки. Сценарий, или скрипт, — это программа, которую выполняет другая программа. В качестве примером можно привести скрипты оболочки в UNIX, программы на языках РНР, Python, Ruby. По степени автономности языки программирования делятся на автономные и встроенные. □ Автономные языки программирования (С, C++, Java) являются автономным инструментом для создания программ. □ Встроенные языки программирования являю тся частью какой-то системы и позволяют создавать программы, предназначенные для работы только в этой системе. Пример наиболее известного встроенного языка — VBA (Visual Basic for Application), который используется только внутри приложений Microsoft Office для автоматизации и расширения их функциональности. Встроенными являются также язык программирования системы 1C и язык JavaScript, который выполняется только внутри интернет-приложений. По уровню отдаленности языка программирования от естественных языков их делят на низкоуровневые и высокоуровневые. □ Языки низкого уровня ближе к логике процессора вычислительной машины, например, машинный язык, ассемблер. □ Языки высокого уровня ближе лингвистически к человеческому языку. Это все остальные языки программирования. По парадигмам языки программирования делятся на императивные (процедур ные), функциональные, логические и объектно-ориентированные. □ Императивные (процедурные) языки описывают решение задачи как последо вательность процедур. К императивным относится большинство современных языков программирования. □ Функциональные языки описывают требуемый результат в виде набора вложен ных друг в друга функций (Haskell). □ Логические языки описывают требуемый результат в виде суммы логических операций (Prolog). 19.2. Краткая история языков программирования 559 □ Объектно-ориентированные языки способны определять абстрактные типы данных и реализовывать основные парадигмы объектно-ориентированного программирования (ООП): инкапсуляцию, наследование и полиморфизм (C++, Java, Object Pascal, и др.). 19.2. Краткая история языков программирования 19.2.1. Первый этап — машинные коды Появление первых языков программирования можно отнести к периоду 2 0 - 40-х гг. XX в. Самые первые опыты программирования, когда в память вычисли тельной машины вводились инструкции на машинном языке сначала с помощью перемычек, а потом — набора тумблеров, сегодня могут вызывать только улыбку. Это была очень тяжелая и непроизводительная работа.-Появление терминала и клавиатуры упростило процесс ввода, но не процесс программирования. Пример. Фрагмент программы на машинном языке, представлен на рис. 19.2. Адреса памяти \ 7С90104А 00648B0D 7С90104Е 1800 7С901050 0000 7С901052 8В542404 7С901056 ЕВС4 Коды команд Рис. 19.2. Фрагмент программы в машинных кодах Слева на рисунке находятся адреса, справа — машинные коды, которыми коди руются инструкции процессора (коды команд). При программировании в машинных кодах программисту нужно постоянно под рукой держать справочник, описывающий каждое числовое значение: что это за команда, как именно она работает. Если учесть, что в современных процессорах число инструкций может превышать несколько сотен, то вполне понятно, что даже при долгой практике запомнить все инструкции — задача практически невыполни мая. А обращаться для ввода каждой инструкции к справочнику — значит, делать процесс написания даже маленькой программы длительным и утомительным. По этой причине программирование в машинных кодах длилось очень недолго, а для программирования непосредственно на аппаратном уровне довольно быстро был разработан язык ассемблера. В языке ассемблера команды легко группируются по функциональности. 560 Глава 19. Технологии и инструменты программирования Пример. Фрагмент программы на языке ассемблера представлен на рис. 19.3. По сути это те же машинные команды, но только в символьной записи (см. рис. 19.3). add [ebx+ecx*4+$0d], ah sbb [e a x ], al add [e a x ],a l mov edx,[esp+$04] jmp - $3c Рис. 19.3. Фрагмент программы на языке ассемблера Запомнить, что команда IT10V означает «переместить», а указанные затем два операнда показывают, откуда нужно взять и куда положить значение, — совсем не трудно. 19.2.2. Второй этап — языки высокого уровня В языках программирования высокого уровня не требуется знания отдельных машинных команд. Каждая команда в языке программирования высокого уровня скрывает десятки и сотни команд на языке ассемблера, Программисты получа ют возможность сосредоточиться на алгоритме работы программы, не заботясь об аппаратной реализации компьютера, как было с языками первого поколения. В языках высокого уровня появились средства вызова подпрограмм и создания локальных переменных. В 60-е гг. были созданы такие языки, как ALGOL, Fort ran, APL. 19.2.3. Третий этап — структурное программирование Однако программные проекты на языках программирования высокого уровня оставляли желать лучшего: сроки выполнения проектов были очень велики, а ма териальные издержки выходили за самые пессимистические прогнозы. Наступило время смены уже не языков, а самого подхода к программированию. Программи рование «снизу вверх», когда вначале разрабатывались подпрограммы, а затем эти подпрограммы объединялись в единое целое при помощи инструкций пере хода, не оправдало себя. Код получался запутанным, неэкономичным и сложным в отладке. Требовалось изменить саму методологию программирования, вместо программирования «снизу вверх» надо было ввести программирование «сверху вниз», при котором сначала определялись стратегические параметры программного обеспечения, вырабатывался наиболее общий алгоритм работы программы, а затем производилась декомпозиция этого алгоритма до уровня подпрограмм размером в 50-70 строк кода. Далее подпрограммы объединялись в единую программу. Такой подход требовал усовершенствований в самих языках программирования на уровне команд управления ходом выполнения программы. Такие усовершенствования были внесены в языки ALGOL-68, С и Pascal, а сама новая методология получила название структурного программирования. 19.2. Краткая история языков программирования 561 19.2.4. Четвертый этап — модульное программирование Порядок, который был наведен с внедрением методологии структурного про граммирования, был достаточно эффективен, но эта методология не решала про блемы многократного использования уже разработанного кода. Следующим шагом в развитии языков и методологий программирования было создание модульного программирования. При модульном программировании программа делилась на модули, которые при компиляции образовывали отдельные объектные файлы. Объ ектные файлы затем собирались в единое целое и получалась исполняемая програм ма. Появилась возможность объединять фрагменты программы по функциональ ному признаку. В каждой программе есть код, который уникален только для этой программы, но всегда есть код, применимый при разработке другого программного продукта, например, код, отвечающий за обработку строковых данных. Фрагменты кода, объединенные в модули, можно было использовать многократно в разных программах как в виде исходного кода, так и в виде скомпилированных объектных файлов. Такой подход многократно увеличил скорость разработки программного обеспечения и позволил дополнительно упорядочить сам процесс разработки. 19.2.5. Пятый этап — объектно-ориентированный подход Алгоритмические языки предыдущих этапов, в основном, обеспечивали реа лизацию функциональности. При объектно-ориентированном подходе програм мист должен думать в терминах объектов окружающего мира, нашедших свое абстрактное отражение в сложных типах данных, называемых классами. А языки программирования призваны были давать возможность отображать в коде объек ты внешнего мира с их свойствами и поведением. Этот этап можно смело назвать революционным по значимости, поскольку все остальные новации в программи ровании продолжают основываться на принципах объектно-ориентированного программирования (ООП). Яркими представителями языков, поддерживающих ООП, являются C++, Java, Object Pascal и все семейство языков в технологии Mi crosoft .NET. Именно объектно-ориентированный подход лежит в основе создания средств визуального проектирования и быстрой разработки программ, таких как Borland Delphi, Borland J Builder и Microsoft Visual Studio. 19.2.6. Шестой этап — компонентный подход Компонентный подход предполагает построение программного обеспечения из независимых друг от друга «кирпичиков», которые объединяются между собой благодаря специальным стандартизированным интерфейсам. При компонентом подходе совместимость различных частей, или объектов, программы между собой обеспечивается тем, что все эти объекты соблюдают единые правила, декларируе мые на уровне операционной системы. Компонентный подход позволяет в рамках одной программы работать компонентам, написанным на разных языках програм мирования и скомпилированным в разных средах разработки. Наиболее известными технологиями, реализующими компонентный подход, являются COM, CORBA и .NET. 562 Глава 19. Технологии и инструменты программирования 19.2.7. Седьмой этап — архитектура, управляемая моделью Этот этап развития технологий программирования пока еще не достиг своего логического завершения, да и сама идея архитектуры, управляемой моделью (Model Driving Architecture, MDA), продолжает развиваться и совершенствоваться, в част ности, в рамках технологии ECO для .NET. Смысл технологии MDA — исключить этап кодирования из процесса разработки программного обеспечения. Для того чтобы создать завершенное приложение, достаточно построить его модель на языке UML, а все остальное за вас сделает среда разработки: сгенерирует классы данных, процедуры их обработки, способы подключения к данным, пользовательский ин терфейс. Хотя это направление развития является весьма многообещающим, на сегодняшний день основным способом создания программного обеспечения все- таки остается ручное кодирование. 19.3. Концепция объектно-ориентированного программирования Поскольку все современные средства разработки программного обеспечения, языки и технологии создания программ продолжают основываться на объектно- ориентированном программировании, предлагаем ознакомиться с основными положениями этого подхода. Объектно-ориентированное программирование бази руется на четырех основных принципах: абстракции, наследовании, инкапсуляции и полиморфизме. 19.3.1. Абстракция Абстрактные структуры, при помощи которых реализуется этот принцип, на зываются классами. Классы представляют собой абстрактные описания структур данных, но сами данные они не содержат. Данные появляются тогда, когда по описаниям классов в памяти программы выделяется необходимое пространство и в нем создаются экземпляры класса, или объекты. Тогда для каждого поля класса отводится необ ходимая область памяти и в эту область можно поместить нужное значение. |