Перейти к содержанию

Дальнейшее программирование

A-Level Информатика · Тема 20

Видеоурок по этой теме Открыть страницу видео
13:52

Парадигмы программирования

Вот одна задача: вывести всех студентов с оценкой A. Один программист пишет цикл — начать с первой записи, проверить оценку, добавить имя в список, перейти к следующей…

Английское озвучивание · Английский + китайские субтитры (встроенные)

20.1

Парадигмы программирования

Программа
Кандидаты должны уметь: Примечания и рекомендации
Понимать значение понятия парадигма программирования
Показать понимание особенностей ряда парадигм программирования:
• Низкоуровневое программирование Низкоуровневое программирование: понимание и способность писать низкоуровневый код, использующий различные режимы адресации: непосредственный (immediate), прямой (direct), косвенный (indirect), индексированный (indexed) и относительный (relative).
• Императивное (Процедурное) Императивное (Процедурное) программирование: • предполагаемые знания и понимание Структурного программирования (см. детали в разделе содержания AS 11.3) • понимание и способность писать императивный (процедурный) программный код, использующий переменные, конструкции, процедуры и функции. См. детали в содержании AS
• Объектно-ориентированное Объектно-ориентированное программирование (ООП): • понимание терминологии, связанной с ООП (включая объекты, свойства/атрибуты, методы, классы, наследование, полиморфизм, включение (агрегация), инкапсуляцию, геттеры, сеттеры, экземпляры) • понимание того, как решать задачи путем проектирования соответствующих классов • понимание и способность писать код, демонстрирующий использование ООП
• Декларативное Декларативное программирование: • понимание и способность решать задачи путем написания соответствующих фактов и правил на основе предоставленной информации • понимание и способность писать код, который может достичь цели с использованием фактов и правил

Источник: Программа Cambridge International

Парадигма программирования — это стиль программирования, способ структурирования программ со своими идеями и языковыми возможностями. В данной программе изучаются четыре парадигмы программирования.

«Опишите, что понимается под императивным (процедурным) языком» (два балла). Язык, в котором программа представляет собой последовательность инструкций, выполняемых по порядку и изменяющих состояние программы; программист указывает, как выполняется задача, используя процедуры, последовательности, выбор и итерации. «Опишите, что понимается под декларативным языком»: программа описывает факты и правила (что известно и чего хотят достичь), а движок вывода языка определяет, как найти ответ; программист не задает последовательность шагов.

Определите парадигму по фрагменту кода (типичный вопрос Бумаги 3): LDD 200, ADD #5, STO 201 — низкоуровневый (мнемонические коды, регистры, адреса памяти); FOR Count ← 1 TO 10 … NEXT Count с процедурами и присваиваниями — императивный; CLASS Dog … PRIVATE Name : STRING … PUBLIC PROCEDURE NEW(…) — объектно-ориентированный; type(lion, wild). и dangerous(X) IF type(X, wild) — декларативный (логика). В вопросе на сопоставление: низкоуровневый соответствует «мнемоническим кодам, напрямую соответствующим машинным инструкциям», императивный — «последовательности операторов, изменяющих состояние», ООП — «объектам, объединяющим атрибуты и методы», декларативный — «фактам и правилам без указания порядка выполнения».

Четыре парадигмы: низкоуровневая, императивная, объектно-ориентированная и декларативная
Четыре парадигмы: низкоуровневая, императивная, объектно-ориентированная и декларативная

Низкоуровневое программирование

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

Пять режимов адресации. Программа требует написания низкоуровневого кода, использующего каждый режим адресации (набор инструкций находится в Разделе 4). Операнд инструкции загрузки может быть прочитан пятью способами, и на экзамене вам дают содержимое памяти и спрашивают, что содержит аккумулятор:

Таблица памяти с адресами 105, 106, 107, 27 и 145 и их содержимым, рядом с пятью строками, показывающими, что получает аккумулятор от LDM #105, LDD 105, LDI 105, LDX 105 при IX = 2, и относительного перехода
Тот же операнд, 105, прочитанный пятью способами: как значение, как адрес, как адрес адреса, как адрес плюс индексный регистр и как смещение от текущей инструкции
  • непосредственный (LDM #105): операнд является значением; ACC становится равным 105.
  • прямой (LDD 105): операнд является адресом значения; ACC становится равным содержимому 105, здесь 27.
  • косвенный (LDI 105): операнд является адресом адреса; ACC становится равным содержимому 27, здесь 91. Используется для указателей и данных, положение которых определяется во время выполнения.
  • индексный (LDX 105): адрес равен операнду плюс индексный регистр IX; при IX = 2, ACC становится равным содержимому 107. Используется для перебора массива путем инкремента IX.
  • относительный (JMR +65): целевой адрес — это смещение от адреса текущей инструкции, что делает код перемещаемым.

Разобранный пример. Память: 105 содержит 27, 106 содержит 64, 200 содержит 0. Напишите код для сложения содержимого 105 и 106, сохранения результата в 200 и его вывода. LDD 105 (ACC = 27), ADD 106 (ACC = 91), STO 200, OUT. Чтобы удвоить значение в 105 вместо этого: LDD 105, ADD 105, STO 105. Укажите содержимое регистров после каждой строки, если требуется выполнить трассировку.

Императивное (процедурное) программирование

В императивном программировании программист пишет последовательность команд, изменяющих состояние программы — присваивания, условные операторы, циклы, вызовы функций. Переменные хранят состояние; операторы его изменяют; код организуется в процедуры и функции (также называемые структурным или структурным программированием). Это стиль тем 9 и 11 (Python, C). Сильная сторона — когда алгоритм имеет четкие последовательные шаги.

Объектно-ориентированное программирование (ООП)

В объектно-ориентированном программировании программы строятся из объектов — единиц, объединяющих данные (атрибуты) и операции (методы). Объекты являются экземплярами классов. Четыре столпа:

  • инкапсуляция — данные объекта скрыты за его методами; внешний код использует только публичные методы, а не данные напрямую. Это защищает объект и позволяет изменять его внутреннюю структуру без нарушения работы вызывающего кода. Например, BankAccount скрывает свои balance; вы изменяете их только через deposit() и withdraw(), которые могут enforce правило, например, «никогда не уходить ниже нуля».
  • наследование — подкласс специализирует суперкласс, наследуя его атрибуты и методы и добавляя или переопределяя их. Моделирует отношение «является» («Менеджер является Сотрудником»).
  • полиморфизм — разные объекты реагируют на один и тот же вызов метода по-разному; вызывающему коду не обязательно знать точный тип. Каждый Shape имеет Area(), а Circle и Rectangle реализуют его по-своему.
  • абстракция — показать простой интерфейс и скрыть реализацию.

Другие термины:

  • конструктор — специальный метод, запускаемый при создании объекта для настройки его атрибутов.
  • геттеры и сеттеры читают и записывают атрибуты объекта (его свойства) через методы.
  • агрегация и композиция создают объект из других объектов (отношение «имеет»).

ООП используется для крупных систем, графических интерфейсов, симуляций и игр.

Та же самая форма вызова. Area() выполняет различный код для каждого объекта: Circle вычисляет pi умножить на r в квадрате, Rectangle вычисляет ширину умноженную на высоту
Полиморфизм: один и тот же вызов метода запускает собственный код каждого объекта
UML-диаграмма классов Shape: трехчастная коробка с именем класса, приватными атрибутами (Name, Area, Perimeter, отмеченными минусом) и публичными методами (SetShape, calculateArea, calculatePerimeter, отмеченными плюсом)
Диаграмма классов для Shape: приватные атрибуты и публичные методы
UML-диаграмма наследования: суперкласс employee сверху, подклассы partTime и fullTime снизу, каждый соединен со суперклассом стрелкой обобщения с пустым треугольником и имеет собственные атрибуты и методы
Наследование: partTime и fullTime являются подклассами employee
Объект BankAccount с приватным балансом, доступ к которому возможен только через публичные методы deposit() и withdraw(); внешний код не может напрямую обращаться к данным
Инкапсуляция: данные объекта находятся в закрытом доступе, доступны только через его публичные методы

ООП как оценивает экзаменатор

Определения. Класс: шаблон (чертеж), определяющий атрибуты и методы объектов данного типа. Объект: экземпляр класса, созданный на его основе, со своими значениями атрибутов («случай появления объекта» — формулировка экзамена для экземпляра). Атрибут (свойство): элемент данных, принадлежащий классу. Метод: процедура или функция, принадлежащая классу и действующая на его атрибуты. Инкапсуляция: объединение атрибутов и методов в одном классе и ограничение внешнего доступа к данным: атрибуты приватны и могут быть прочитаны или изменены только через публичные методы. Наследование: подкласс наследует атрибуты и методы своего родительского (супер) класса и может добавить свои или переопределить их. Полиморфизм: методы с одинаковым названием, которые ведут себя по-разному в различных классах; обычно подкласс переопределяет метод родительского, и для каждого объекта выполняется соответствующая версия. Композиция: класс содержит объект другого класса в качестве атрибута (машина имеет двигатель). «Определите признак, ограничивающий внешний доступ к данным» — это инкапсуляция; «термин для случая появления объекта» — это экземпляр.

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

Класс на псевдокоде, как требуется в Paper 3:

CLASS Car
    PRIVATE Registration : STRING
    PRIVATE Year : INTEGER
    PRIVATE Mileage : INTEGER
    PUBLIC PROCEDURE NEW(NewReg : STRING, NewYear : INTEGER)
        Registration ← NewReg
        Year ← NewYear
        Mileage ← 0
    ENDPROCEDURE
    PUBLIC FUNCTION GetMileage() RETURNS INTEGER
        RETURN Mileage
    ENDFUNCTION
    PUBLIC PROCEDURE AddMileage(Extra : INTEGER)
        Mileage ← Mileage + Extra
    ENDPROCEDURE
ENDCLASS

Объект создается с помощью MyCar ← NEW Car("AB12 CDE", 2020) и используется с MyCar.AddMileage(150) и OUTPUT MyCar.GetMileage(). Подкласс переиспользует конструктор родителя через SUPER:

CLASS ElectricCar INHERITS Car
    PRIVATE BatteryCapacity : REAL
    PUBLIC PROCEDURE NEW(NewReg : STRING, NewYear : INTEGER, NewCapacity : REAL)
        SUPER.NEW(NewReg, NewYear)
        BatteryCapacity ← NewCapacity
    ENDPROCEDURE
ENDCLASS

Тот же самый класс на Python, как ожидает Paper 4: атрибуты делаются приватными двойным подчеркиванием, конструктор — это __init__, а имя подкласса указывает родителя в скобках и вызывает super().__init__(…):

class Car:
    def __init__(self, reg, year):
        self.__registration = reg
        self.__year = year
        self.__mileage = 0
    def get_mileage(self):
        return self.__mileage
    def add_mileage(self, extra):
        self.__mileage = self.__mileage + extra

class ElectricCar(Car):
    def __init__(self, reg, year, capacity):
        super().__init__(reg, year)
        self.__capacity = capacity

cars = []
cars.append(Car("AB12 CDE", 2020))
cars.append(ElectricCar("EV21 XYZ", 2023, 75.0))
cars[1].add_mileage(150)
print(cars[1].get_mileage())

В Java те же идеи представлены как private/public поля, конструктор с именем класса, extends и super(…); в VB.NET — Private/Public, Sub New, Inherits и MyBase.New. Полиморфный метод пишется в родителе и переопределяется в потомке тем же именем; вызов через переменную типа родителя выполняет версию потомка.

Структуры данных как объекты. Paper 4 строит стек, связный список или бинарное дерево из Node класса, чьи атрибуты — это данные и один или два ссылки на другие узлы; Tree (или LinkedList) класс хранит корень (или начало) и методы.

Бинарное дерево объектов Node: Root объекта Tree указывает на узел 15, чьи ссылки Left и Right указывают на узлы 8 и 19, и так далее, с None для пустых ссылок
Бинарное дерево, построенное из объектов: каждый Node хранит Data плюс ссылки Left и Right, а Tree хранит Root; вставка происходит путем спуска по ссылкам
CLASS Node
    PUBLIC Data : INTEGER
    PUBLIC Left : Node          // NULL when there is no child
    PUBLIC Right : Node
    PUBLIC PROCEDURE NEW(NewData : INTEGER)
        Data ← NewData
        Left ← NULL
        Right ← NULL
    ENDPROCEDURE
ENDCLASS

CLASS Tree
    PRIVATE Root : Node
    PUBLIC PROCEDURE Insert(NewData : INTEGER)
        DECLARE NewNode, Current : Node
        DECLARE Placed : BOOLEAN
        NewNode ← NEW Node(NewData)
        IF Root = NULL THEN
            Root ← NewNode
        ELSE
            Current ← Root
            Placed ← FALSE
            WHILE NOT Placed
                IF NewData < Current.Data THEN
                    IF Current.Left = NULL THEN
                        Current.Left ← NewNode
                        Placed ← TRUE
                    ELSE
                        Current ← Current.Left
                    ENDIF
                ELSE
                    IF Current.Right = NULL THEN
                        Current.Right ← NewNode
                        Placed ← TRUE
                    ELSE
                        Current ← Current.Right
                    ENDIF
                ENDIF
            ENDWHILE
        ENDIF
    ENDPROCEDURE
ENDCLASS

Метод поиска проходит тот же путь и возвращает TRUE, когда Current.Data = Target, FALSE при достижении NULL; рекурсивный метод вывода в порядке in-order: выводит левое поддерево, узел, затем правое поддерево. Для связного списка узел имеет одну ссылку, Next, а класс списка хранит Start; для стека, построенного на основе списка, push и pop работают в Start.

Разобраный пример. В игре есть персонажи. У каждого есть имя, здоровье (начальное значение 100) и позиция, заданная X и Y. Напишите класс Character с конструктором и методом Move(DX, DY); затем подкласс Wizard, добавляющий Mana (начальное значение 50) и метод CastSpell(), который тратит 10 маны и возвращает TRUE, если её было достаточно.

CLASS Character
    PRIVATE Name : STRING
    PRIVATE Health : INTEGER
    PRIVATE X : INTEGER
    PRIVATE Y : INTEGER
    PUBLIC PROCEDURE NEW(NewName : STRING, StartX : INTEGER, StartY : INTEGER)
        Name ← NewName
        Health ← 100
        X ← StartX
        Y ← StartY
    ENDPROCEDURE
    PUBLIC PROCEDURE Move(DX : INTEGER, DY : INTEGER)
        X ← X + DX
        Y ← Y + DY
    ENDPROCEDURE
ENDCLASS

CLASS Wizard INHERITS Character
    PRIVATE Mana : INTEGER
    PUBLIC PROCEDURE NEW(NewName : STRING, StartX : INTEGER, StartY : INTEGER)
        SUPER.NEW(NewName, StartX, StartY)
        Mana ← 50
    ENDPROCEDURE
    PUBLIC FUNCTION CastSpell() RETURNS BOOLEAN
        IF Mana >= 10 THEN
            Mana ← Mana - 10
            RETURN TRUE
        ELSE
            RETURN FALSE
        ENDIF
    ENDFUNCTION
ENDCLASS

Баллы начисляются за приватные атрибуты, конструктор, устанавливающий все атрибуты, строку наследования, вызов конструктора родителя и метод, использующий и изменяющий собственные данные объекта. Когда вопрос требует диаграмму классов, нарисуйте коробку из трех частей (имя; атрибуты с - для приватных; методы с + для публичных) и соедините подкласс с родителем стрелкой, указывающей на родителя.

Декларативное программирование

В декларативном программировании вы указываете, что нужно вычислить, а не как — время выполнения определяет шаги. Два вида:

  • функциональное программирование — построено из чистых функций (без побочных эффектов; одинаковый вход всегда даёт одинаковый выход), объединённых вместе. Примеры: Haskell, Lisp.
  • логическое программирование — задаёт факты и правила; движок отвечает на цель (запрос) путём вывода. Пример: Prolog.

Примером декларативного подхода является SQL: SELECT * FROM Customer WHERE Country = 'UK' описывает желаемый результат, а не алгоритм обхода записей.

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

01 type(leopard, wild).
02 type(lion, wild).
03 type(tabby, domestic).
04 size(leopard, large).
05 size(lion, large).
06 size(tabby, small).
07 dangerous(X) IF type(X, wild) AND size(X, large).

"Запишите результат цели type(X, wild)": X = leopard, X = lion. Движок сопоставляет цель с каждым фактом по очереди; каждое совпадение является решением, а заглавная буква — это переменная, которую заполняет совпадение. "Запишите факт, показывающий, что гепард дикий": type(cheetah, wild). "Объясните, что делает строка 07": она определяет правило с выводом dangerous(X), которое верно для любого X, который одновременно дикий и крупный, поэтому dangerous(A) возвращает A = leopard, A = lion. "Запишите правило: функция F может быть доступна для стиля кузова B, если F является функцией, B — стилем кузова, и F не недоступен для B": may_be_available(F, B) IF feature(F) AND body_style(B) AND NOT unavailable(F, B). Скопируйте точные имена предикатов и порядок аргументов, использованные в фактах вопроса; новый факт заканчивается точкой, а условия правила соединяются через AND.

Сравнение парадигм

Парадигма Сильная сторона Типичные языки
Низкоуровневая максимальный контроль, скорость ассемблер
Императивная прямая, интуитивная C, Python
Объектно-ориентированная модульная, моделирует сущности Java, C#, Python
Функциональная понятная, без побочных эффектов Haskell, F#
Логическая вывод, правила Prolog
Баз данных запросы к данным SQL

Современные языки часто сочетают парадигмы — Python поддерживает все три: процедурную, ООП и функциональную. Правильный выбор зависит от задачи.

Исследовать

Лабораторная работа по программированию

Сопоставьте примеры с показанной ими программной идеей.

English Русский
programming paradigm/ˈprəʊɡræmɪŋ ˈpærədaɪm/ парадигма программирования
facts/fækts/ факты
rule/ruːl/ правило
low-level/ləʊ ˈlevl/ низкоуровневая
registers/ˈredʒɪstəz/ регистры
memory addresses/ˈmeməri əˈdresɪz/ адреса памяти
objects/ˈɒbdʒekts/ предметы
attributes/ˈætrɪbjuːts/ атрибуты
methods/ˈmeθədz/ методы
machine code/məˈʃiːn kəʊd/ машинный код
assembly language/əˈsemblɪ ˈlæŋɡwɪdʒ/ язык ассемблера
addressing modes/əˈdresɪŋ məʊdz/ режимы адресации
array/əˈreɪ/ массив (array)
imperative programming/ɪmˈperətɪv ˈprəʊɡræmɪŋ/ императивное программирование
Variables/ˈveərɪəblz/ Переменные
object-oriented programming/ˈɒbdʒekt ˈɔːrɪəntɪd ˈprəʊɡræmɪŋ/ объектно-ориентированное программирование
instances/ˈɪnstənsɪz/ экземпляры
classes/ˈklæsɪz/ классов
encapsulation/ɪnˌkæpsjʊˈleɪʃn/ инкапсуляции
inheritance/ɪnˈherɪtəns/ наследование
subclass/ˈsʌbklæs/ подкласс
superclass/ˈsuːpəklæs/ суперкласс
overriding/ˌəʊvəˈraɪdɪŋ/ переопределением
polymorphism/ˈpɒlɪmɔːfɪzəm/ полиморфизм
abstraction/əbˈstrækʃn/ абстракцией
constructor/kənˈstrʌktə/ конструктор
aggregation/ˌæɡrɪˈɡeɪʃn/ агрегация
containment/kənˈteɪnmənt/ вложенность
declarative programming/dɪˈklærətɪv ˈprəʊɡræmɪŋ/ декларативное программирование
functional programming/ˈfʌŋkʃənl ˈprəʊɡræmɪŋ/ функциональное программирование
pure functions/pjʊə ˈfʌŋkʃnz/ чистые функции
side effects/saɪd ɪˈfekts/ побочных эффектов
logic programming/ˈlɒdʒɪk ˈprəʊɡræmɪŋ/ логическое программирование
SQL/ˌes kjuː ˈel/ SQL
20.2

Обработка файлов

Программа
Кандидаты должны уметь: Примечания и рекомендации
Писать код для выполнения операций обработки файлов Открывать (в режимах чтения, записи, добавления) и закрывать файл Считывать запись из файла и записывать запись в файл Выполнять операции обработки файлов над последовательными, линейными, случайного доступа файлами
Проявлять понимание исключения и важности обработки исключений Знать, когда уместно использовать обработку исключений Писать программный код для использования обработки исключений

Источник: Программа Cambridge International

Это расширение обработки файлов из Темы 10, включающее последовательные, серийные и случайного доступа (прямой доступ). Операции псевдокода: OPENFILE name FOR READ | WRITE | APPEND (READ открывает существующий файл, WRITE создаёт/перезаписывает, APPEND добавляет в конец); READFILE name, line; WRITEFILE name, value; CLOSEFILE name; и EOF(name), который равен TRUE в конце.

Прочитать весь файл:

OPENFILE "names.txt" FOR READ
WHILE NOT EOF("names.txt") DO
    READFILE "names.txt", thisName
    OUTPUT thisName
ENDWHILE
CLOSEFILE "names.txt"

Поиск в файле (остановиться при нахождении):

found ← FALSE
OPENFILE "people.txt" FOR READ
WHILE NOT EOF("people.txt") AND NOT found DO
    READFILE "people.txt", line
    IF line = target THEN
        found ← TRUE
    ENDIF
ENDWHILE
CLOSEFILE "people.txt"

Обновление файла на месте

Большинство языков не могут редактировать текстовый файл на месте. Вместо этого: откройте оригинал для READ и временный файл для WRITE; для каждой строки запишите новую версию, если она должна измениться, иначе оригинальную; закройте оба; затем замените оригинал временным файлом. Этот же паттерн используется для удаления строк (пропускать их) и вставки строк.

Обновление файла на месте: чтение исходного файла, запись изменённых строк во временный файл, затем замена оригинала временным файлом
Обновление файла на месте: чтение оригинала, запись изменений во временный файл, затем замена оригинала

Записи и файлы случайного доступа

Режимы открытия. READ: файл должен существовать, чтение начинается с начала. WRITE: создаётся новый файл, а существующий файл с таким именем перезаписывается. APPEND: запись добавляется в конец существующего файла. Каждый открытый файл закрывается с помощью CLOSEFILE, и EOF(name) становится TRUE после прочтения последнего элемента.

Три организации файлов. В серийном файле записи расположены в порядке их добавления; в последовательном файле они упорядочены по ключу; оба читаются от начала. Случайный файл (файл прямого доступа) хранит каждую запись по адресу, вычисленному из её ключа с помощью функции хэширования, поэтому одну запись можно найти без чтения остальных. Записи объявляются как тип, определённый пользователем:

TYPE AccountRecord
    DECLARE AccNo : INTEGER
    DECLARE Name : STRING
    DECLARE Balance : REAL
    DECLARE Active : BOOLEAN
ENDTYPE
Ключ 2317 хэшируется через MOD 1000 до адреса 317, затем SEEK и GETRECORD на файле Acc.dat, показанном как ряд слотов равного размера с выделенным слотом 317
Нахождение одной записи в случайном файле: ключ хэшируется до адреса, указатель файла переходит непосредственно к этому слоту, и запись читается; ни одна другая запись не затрагивается

Операции со случайным файлом в псевдокоде — это OPENFILE "Acc.dat" FOR RANDOM, SEEK "Acc.dat", Address (переместить указатель файла к этой записи), GETRECORD "Acc.dat", Rec (прочитать запись там) и PUTRECORD "Acc.dat", Rec (записать запись туда). Поиск клиента по номеру счета, как задано в Paper 3:

DECLARE Rec : AccountRecord
DECLARE Target, Address : INTEGER
INPUT Target
Address ← Target MOD 1000              // the hashing function
OPENFILE "Acc.dat" FOR RANDOM
SEEK "Acc.dat", Address
GETRECORD "Acc.dat", Rec
WHILE Rec.AccNo <> Target AND Rec.AccNo <> 0    // 0 marks an empty slot
    Address ← Address + 1               // a collision: try the next slot
    SEEK "Acc.dat", Address
    GETRECORD "Acc.dat", Rec
ENDWHILE
IF Rec.AccNo = Target THEN
    OUTPUT Rec.Name, Rec.Balance
ELSE
    OUTPUT "No such account"
ENDIF
CLOSEFILE "Acc.dat"

Чтобы сохранить запись, хэшируйте её ключ, SEEK по адресу и PUTRECORD, перешагивая через уже занятые слоты. Баллы ставятся за хэширование, за SEEK перед GET или PUT, за сравнение с целевым значением, за обработку коллизии и за закрытие файла.

Разобранный пример. ActiveFile.dat содержит AccountRecord записей. Напишите псевдокод, который копирует каждую запись, чьё поле Active равно FALSE, в конец ArchiveFile.dat.

DECLARE Rec : AccountRecord
OPENFILE "ActiveFile.dat" FOR READ
OPENFILE "ArchiveFile.dat" FOR APPEND
WHILE NOT EOF("ActiveFile.dat")
    READFILE "ActiveFile.dat", Rec
    IF Rec.Active = FALSE THEN
        WRITEFILE "ArchiveFile.dat", Rec
    ENDIF
ENDWHILE
CLOSEFILE "ActiveFile.dat"
CLOSEFILE "ArchiveFile.dat"

Текстовые файлы в Python (Paper 4): file = open("HighScore.txt", "r"), затем for line in file: с line.strip() и line.split(",") для разделения полей, int(…) для преобразования оценки, и file.close(); для записи — open(name, "w") (или "a" для добавления) и file.write(str(score) + "\n"). Таблица рекордов считывается в список записей, новая оценка вставляется на своё место, и весь список записывается обратно. Экзаменатор оценивает открытие с правильным режимом, цикл, читающий каждую строку, преобразование текста в числа и закрытие файла.

Подводные камни

Забывание закрыть файл (данные могут потеряться); открытие для WRITE вместо APPEND (перезапишет всё); чтение за пределами EOF; жёстко закодированные пути — путь вроде /Users/Admin/data.txt не сработает на другом компьютере, поэтому используйте относительную константу, например DataFile = "./data/scores.txt".

Исследовать

Маршрут доступа к файлу

Безопасно следите за файлом от хранилища до программы и обратно.

English Русский
file/faɪl/ файл
random file/ˈrændəm faɪl/ случайный файл
hashing/ˈhæʃɪŋ/ хэширование
20.2

Обработка исключений

Исключение — это ошибка или неожиданный сбой во время выполнения программы: деление на ноль, файл не найден, сетевая ошибка, выход индекса массива за допустимые пределы. Обработка исключений позволяет программе обнаружить ошибку и отреагировать на неё корректно, вместо того чтобы завершиться аварийно.

Это важно, потому что реальные программы сталкиваются с ошибками, которые невозможно предотвратить заранее (перемещённые файлы, недоступная сеть, неверный ввод); без обработки исключения каждая операция потребовала бы собственной проверки IF; кроме того, обработка исключений разделяет нормальный поток выполнения и логику обработки ошибок, благодаря чему основной код остаётся чистым и понятным. Например, другой пользователь может удалить файл между моментом, когда ваша программа проверяла его существование, и моментом открытия — вы не можете этого предотвратить, но можете обработать сбой, когда он произойдёт.

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

"Назовите причины использования обработки исключений" (три балла). Чтобы предотвратить аварийное завершение (неожиданный краш) программы; чтобы вывести пользователю понятное сообщение вместо системной ошибки; чтобы позволить программе восстановиться и продолжить работу, например, запросив ввод снова, или безопасно закрыть файлы перед завершением; а также потому, что некоторые ошибки нельзя предсказать на этапе написания программы. "Опишите, как избежать аварийного завершения программы из-за исключения": поместите операторы, способные вызвать исключение, внутрь блока TRY; напишите блок EXCEPT (перехватывающий) для данного исключения, который обрабатывает его, например, выводя сообщение, чтобы выполнение продолжилось после блока, а не прервалось. "Объясните, что понимается под обработкой исключений": обнаружение исключения в момент его возникновения и выполнение кода (обработчика), который справляется с ним, чтобы программа могла продолжать работу.

Шаблон

TRY
    OPENFILE "data.txt" FOR READ
    READFILE "data.txt", line
    OUTPUT line
    CLOSEFILE "data.txt"
EXCEPT FileNotFound
    OUTPUT "Sorry, the file does not exist."
EXCEPT ReadError
    OUTPUT "Sorry, error reading the file."
ENDTRY

Блок TRY содержит код, который может вызвать ошибку; выполняется первый подходящий блок EXCEPT. В реальных языках также есть универсальный блок EXCEPT и блок FINALLY, который выполняется независимо от того, произошло ли исключение — это полезно для очистки ресурсов (закрытия файлов).

Поток обработки исключений: если блок TRY вызывает исключение, управление переходит к соответствующему блоку EXCEPT; при отсутствии исключения он пропускается. В любом случае выполняется блок FINALLY, после чего программа продолжает работу *Поток обработки исключений: исключение приводит к переходу к соответствующему блоку EXCEPT; блок FINALLY всегда выполняется перед продолжением работы программы

Вызов исключения

Подпрограмма, обнаружившая ошибку, может вызвать исключение, чтобы вызывающий код обработал его:

PROCEDURE Divide(a : INTEGER, b : INTEGER) RETURNS INTEGER
    IF b = 0 THEN
        RAISE DivideByZero
    ENDIF
    RETURN a DIV b
ENDPROCEDURE

Где обрабатывать исключения

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

Распространённые исключения: FileNotFound, IOError, DivisionByZero, IndexOutOfRange, InvalidArgument, NullReference, OutOfMemory. Обёртывание каждой потенциально ошибочной операции в TRY с правильными EXCEPT обработчиками обеспечивает корректную деградацию программы вместо аварийного завершения.

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

def read_scores(filename):
    scores = []
    try:
        file = open(filename, "r")
        for line in file:
            scores.append(int(line))
        file.close()
    except FileNotFoundError:
        print("The file", filename, "does not exist")
    except ValueError:
        print("A line in the file was not a whole number")
    return scores

Блок try содержит код, который может дать сбой (открытие файла и преобразование); каждый блок except называет одно исключение и выполняет полезные действия; функция всё равно возвращает список, поэтому вызывающий код продолжает работу. В Java аналогичная структура выглядит как try { … } catch (FileNotFoundException e) { … } catch (NumberFormatException e) { … }; в VB.NET — как Try … Catch ex As FileNotFoundException … End Try. Баллы начисляются за: опасные операторы внутри try, правильные названия исключений, сообщения для каждого случая и продолжение работы программы afterwards; универсальный перехватчик except: получает балл за предотвращение краша, но не за указание «соответствующего исключения».

Разбор примера. В текстовом файле со списком членов необходимо изменить телефон одного из них. Почему нельзя просто перезаписать эту строку и каков правильный шаблон? Строки в текстовом файле имеют различную длину, и в файле нет пустых мест, чтобы компенсировать разницу: более длинная замена затронет следующую запись, а более короткая оставит часть старой строки. Поэтому шаблон таков: открываем оригинальный файл для чтения и временный файл для записи, последовательно читаем все строки, записывая новую версию для изменяемой строки и оригинальную для всех остальных, закрываем оба файла, затем заменяем оригинал временным файлом. Тот же шаблон используется для удаления (пропуск строки) и добавления (запись дополнительной строки). Важно отметить, что каждая строка должна быть записана, а не только изменённая — запись только нового значения с потерей остальной части файла является классической ошибкой.

Исследовать

Как протекает обработка исключений

Разберите по шагам, что происходит при ошибке в коде. Исключение跳出 нормального потока к обработчику, FINALLY выполняет очистку в любом случае, и программа продолжает работу вместо того, чтобы аварийно завершиться.

English Русский
exception/ekˈsepʃn/ исключение
exception handling/ekˈsepʃn ˈhændlɪŋ/ обработка исключений
raise/reɪz/ повышают
call stack/kɔːl stæk/ стек вызовов
20.2

Определения, принимаемые экзаменатором

Вопросы на определение оцениваются по фиксированной формулировке. Выучите их точно и дайте только один ответ.

Термин Определение
парадигма программирования стиль или подход к программированию, имеющий свой собственный способ структурирования программы
императивный язык программа представляет собой последовательность инструкций, изменяющих состояние программы; программист явно указывает, как выполняется задача
декларативный язык программа описывает факты и правила, а движок вывода определяет, как найти ответ
класс шаблон, определяющий атрибуты и методы объектов данного типа
объект (экземпляр) конкретный случай реализации класса, обладающий собственными значениями атрибутов
атрибут элемент данных, принадлежащий классу
метод процедура или функция, принадлежащая классу и действующая с его атрибутами
инкапсуляция объединение атрибутов и методов в одном классе и ограничение внешнего доступа к данным, чтобы они изменялись только через публичные методы
наследование подкласс получает атрибуты и методы своего родительского класса и может добавлять или переопределять их
полиморфизм методы с одинаковым названием, которые ведут себя по-разному для разных классов
конструктор метод, который выполняется при создании объекта и инициализирует его атрибуты
композиция класс имеет объект другого класса в качестве одного из своих атрибутов
факт утверждение в декларативной программе, которое является истинным
правило вывод, который верен при выполнении его условий
последовательный, упорядоченный, случайный файл записи в порядке добавления; записи в порядке ключей; каждая запись по адресу, вычисленному на основе её ключа
исключение неожиданная ошибка или событие во время выполнения, прерывающее нормальный поток
обработка исключений обнаружение исключения при его возникновении и выполнение кода, обрабатывающего его, чтобы программа продолжала работу
20.2

Советы для экзамена

  • Парадигмы: знать однострочное описание каждой и уметь назвать парадигму по образцу кода; вопросы низкого уровня требуют знания пяти режимов адресации и того, что получает аккумулятор.
  • Определения ООП встречаются на каждой сессии: класс, объект, атрибут, метод, инкапсуляция, наследование, полиморфизм, конструктор. Написать класс на псевдокоде с PRIVATE-атрибутами, PUBLIC NEW и геттерами; подкласс с INHERITS и SUPER.NEW.
  • Декларативные программы: цель с переменной возвращает все совпадающие факты; правило — это вывод, ЕСЛИ условия соединены логическим И; точное копирование имён предикатов из вопроса.
  • Файлы: три режима и то, что каждый делает с существующим файлом; READFILE в цикле WHILE NOT EOF; случайные файлы используют хеш-функцию, SEEK, GETRECORD и PUTRECORD, с перекрытием записей при коллизиях.
  • Исключения: определение с примером, три причины их обработки, и конструкция TRY с именованным EXCEPT, позволяющая программе продолжить работу.

Распространенные ошибки

  • Описание декларативной программы как «последовательности шагов, дающей ответ»; она утверждает, что истинно и чего хотят, а не как это сделать.
  • Путаница между объектом и классом, или экземпляром и атрибутом; вопрос «экземпляр объекта» требует ответа instance.
  • Объявление атрибутов PUBLIC или обращение к ним извне класса вместо использования геттера, что нарушает маркировку инкапсуляции.
  • Конструктор подкласса, который устанавливает атрибуты родителя напрямую вместо вызова SUPER.NEW.
  • Объяснение полиморфизма как «множества объектов»; это одно и то же имя метода, ведущего себя по-разному для разных классов.
  • Открытие файла FOR WRITE для добавления записи, что уничтожает существующее содержимое; используйте APPEND.
  • Чтение случайного файла с начала; сначала выполните SEEK по хешированному адресу.
  • Размещение обработчика исключений вокруг кода, который не может дать сбой, или перехват всего без сообщения, или описание обработки исключений как «проверки ввода с помощью IF».

Интерактивные уроки по этой теме

Пройдите его шаг за шагом с упражнениями мгновенной проверки.

Архив экзаменационных работ

Больше тем в A-Level Информатика

Войти или создать аккаунт

IGCSE, A-Level & AP