Pular para o conteúdo

Programação Avançada

Ciência da Computação do A-Level · Tópico 20

Treinar
Videoaula para este tópico Abrir a página do vídeo
13:52

Paradigmas de Programação

Aqui está um trabalho: liste todos os alunos com nota A. Um programador escreve um loop — comece no primeiro registro, verifique a nota, adicione o nome a uma lista, mova…

Narração em inglês · Legendas em inglês + 中文 gravadas

20.1

Paradigmas de programação

Programa
Os candidatos devem ser capazes de: Notas e orientações
Compreender o que se entende por um paradigma de programação
Demonstrar compreensão das características de vários paradigmas de programação:
• Baixo nível Programação de Baixo Nível: • compreensão e capacidade de escrever código de baixo nível usando vários modos de endereçamento: imediato, direto, indireto, indexado e relativo
• Imperativo (Procedural) Programação Imperativa (Procedural): • conhecimento pressuposto e compreensão de Programação Estruturada (ver detalhes na seção de conteúdo AS 11.3) • compreensão e capacidade de escrever código de programação imperativa (procedural) que usa variáveis, construções, procedimentos e funções. Ver detalhes no conteúdo AS
• Orientado a Objetos Programação Orientada a Objetos (POO): • compreensão da terminologia associada à POO (incluindo objetos, propriedades/atributos, métodos, classes, herança, polimorfismo, contenção (agregação), encapsulamento, getters, setters, instâncias) • compreensão de como resolver um problema projetando classes apropriadas • compreensão e capacidade de escrever código que demonstre o uso de POO
• Declarativo Programação Declarativa: • compreensão e capacidade de resolver um problema escrevendo fatores e regras adequados baseados nas informações fornecidas • compreensão e capacidade de escrever código que possa satisfazer um objetivo usando fatos e regras

Fonte: Programa Cambridge International

Um paradigma de programação 编程范式 é um estilo de programação — uma maneira de estruturar programas, com suas próprias ideias e recursos de linguagem. Quatro paradigmas de programação estão neste programa.

"Descreva o que significa uma linguagem imperativa (procedural) (duas marcas)." Uma linguagem na qual o programa é uma sequência de instruções executadas em ordem e que alteram o estado do programa; o programador diz como a tarefa é feita, usando procedimentos, sequência, seleção e iteração. "Descreva o que significa uma linguagem declarativa": o programa declara fatos e regras (o que é conhecido e o que se quer) e o motor de inferência da linguagem descobre como encontrar a resposta; o programador não dá a sequência de passos.

Identifique o paradigma a partir de um exemplo de código (uma questão típica do Paper 3): LDD 200, ADD #5, STO 201 são de baixo nível (mnemônicos, registradores, endereços de memória); FOR Count ← 1 TO 10 … NEXT Count com procedimentos e atribuições é imperativo; CLASS Dog … PRIVATE Name : STRING … PUBLIC PROCEDURE NEW(…) é orientado a objetos; type(lion, wild). e dangerous(X) IF type(X, wild) são declarativos (lógica). Na questão de correspondência: baixo nível combina com "mnemônicos que correspondem diretamente às instruções da máquina", imperativo com "uma sequência de instruções que alteram o estado", OOP com "objetos que combinam atributos e métodos" e declarativo com "fatos e regras, sem ordem de execução dada".

Quatro paradigmas: baixo nível, imperativo, orientado a objetos e declarativo
Quatro paradigmas: baixo nível, imperativo, orientado a objetos e declarativo

Programação de baixo nível

Programação próxima ao hardware em código de máquina 机器码 ou linguagem de montagem 汇编语言, onde cada instrução mapeia para o que a CPU executa. Fornece acesso direto a registradores 寄存器和 endereços de memória 内存地址, usando diferentes modos de endereçamento 寻址方式 (imediato, direto, indireto, indexado e relativo). É muito rápido e compacto, mas específico da arquitetura, tedioso e difícil de manter. Esta é a programação de baixo nível 低级, usada para drivers de dispositivos, firmware e bootloaders.

Os cinco modos de endereçamento. A ementa pede código de baixo nível que use cada modo de endereçamento (o conjunto de instruções está no Topic 4). O operando de uma instrução de carga pode ser lido de cinco formas, e o exame fornece os conteúdos da memória e pergunta qual o acumulador contém:

Uma tabela de memória com endereços 105, 106, 107, 27 e 145 e seus conteúdos, ao lado de cinco linhas mostrando o que o acumulador recebe de LDM #105, LDD 105, LDI 105, LDX 105 com IX = 2, e um salto relativo
O mesmo operando, 105, lido de cinco formas: como um valor, como um endereço, como o endereço de um endereço, como um endereço mais o registrador índice, e como um offset a partir da instrução atual
  • imediato (LDM #105): o operando é o valor; ACC torna-se 105.
  • direto (LDD 105): o operando é o endereço do valor; ACC torna-se o conteúdo de 105, aqui 27.
  • indireto (LDI 105): o operando é o endereço de um endereço; ACC torna-se o conteúdo de 27, aqui 91. Usado para ponteiros e para dados cuja posição é decidida em tempo de execução.
  • indexado (LDX 105): o endereço é o operando mais o registrador índice IX; com IX = 2, ACC torna-se o conteúdo de 107. Usado para percorrer uma matriz incrementando IX.
  • relativo (JMR +65): o destino é um offset a partir do endereço da instrução atual, o que torna o código relocalizável.

Exemplo resolvido. Memória: 105 contém 27, 106 contém 64, 200 contém 0. Escreva código para somar os conteúdos de 105 e 106, armazenar o resultado em 200 e exibi-lo. LDD 105 (ACC = 27), ADD 106 (ACC = 91), STO 200, OUT. Para dobrar o valor em 105 em vez disso: LDD 105, ADD 105, STO 105. Indique o conteúdo dos registradores após cada linha quando solicitado a rastrear.

Programação imperativa (procedural)

Na programação imperativa 命令式编程, o programador escreve uma sequência de comandos que alteram o estado do programa — atribuições, condicionais, laços, chamadas de função. Variáveis 变量 mantêm o estado; as instruções o alteram; o código é organizado em procedimentos e funções (também chamado de programação estruturada). Este é o estilo dos Topics 9 e 11 (Python, C). Forte quando o algoritmo tem passos sequenciais claros.

Programação orientada a objetos (OOP)

Na programação orientada a objetos 面向对象编程, programas são construídos a partir de objetos 对象 — unidades que combinam dados (atributos 属性) e operações (métodos 方法). Objetos são instâncias 实例 de classes 类. Os quatro pilares:

  • encapsulamento 封装 — os dados de um objeto estão ocultos atrás de seus métodos; o código externo usa apenas os métodos públicos, não os dados diretamente. Isso protege o objeto e permite que seu interior mude sem quebrar quem o chama. Por exemplo, um BankAccount esconde seus balance; você os altera apenas através de deposit() e withdraw(), que podem impor uma regra como "nunca descer abaixo de zero".
  • herança 继承 — uma subclasse 子类 especializa uma superclasse 父类, herdando seus atributos e métodos e adicionando ou sobrescrevendo 重写 eles. Modela "é-um" ("Um Gerente é um Funcionário").
  • polimorfismo 多态 — objetos diferentes respondem à mesma chamada de método de forma diferente; quem chama não precisa saber o tipo exato. Todo Shape tem Area(), e um Circle e um Rectangle o implementam de suas próprias maneiras.
  • abstração 抽象 — mostrar uma interface simples e ocultar a implementação.

Outros termos:

  • um construtor 构造函数 é um método especial executado quando um objeto é criado, para configurar seus atributos.
  • getters e setters leem e gravam os atributos de um objeto (suas propriedades) através de métodos.
  • agregação 聚合 e contenção 包含 constroem um objeto a partir de outros objetos (uma relação "tem-um").

OOP é usado para sistemas grandes, interfaces gráficas, simulações e jogos.

A mesma forma de chamada.Area() executa códigos diferentes para cada objeto: um Círculo calcula pi r quadrado, um Retângulo calcula largura vezes altura *Polimorfismo: a mesma chamada de método executa o código próprio de cada objeto

Um diagrama de classes UML para Shape: uma caixa de três partes com o nome da classe, atributos privados (Name, Area, Perimeter, marcados com menos) e métodos públicos (SetShape, calculateArea, calculatePerimeter, marcados com mais) *Um diagrama de classe para Shape: atributos privados e métodos públicos

Um diagrama de herança UML: a superclasse employee no topo, com as subclasses partTime e fullTime abaixo, cada uma ligada à superclasse por uma seta de generalização de triângulo vazio e adicionando seus próprios atributos e métodos *Herança: partTime e fullTime são subclases de employee

Um objeto BankAccount com um saldo privado acessado apenas através dos métodos públicos deposit() e withdraw(); o código externo não pode tocar nos dados diretamente *Encapsulamento: os dados de um objeto são privados, acessados apenas através de seus métodos públicos

OOP conforme o avaliador pontua

Definições. Classe: uma planta (projeto) que define os atributos e métodos dos objetos desse tipo. Objeto: uma instância de uma classe, criada a partir dela, com seus próprios valores para os atributos ("uma ocorrência de um objeto" é a expressão do exame para instância). Atributo (propriedade): um item de dado pertencente a uma classe. Método: um procedimento ou função pertencente a uma classe que atua sobre seus atributos. Encapsulamento: combinar atributos e métodos em uma classe e restringir o acesso externo aos dados: os atributos são privados e só podem ser lidos ou alterados através de métodos públicos. Herança: uma subclasse adquire os atributos e métodos de sua classe pai (super) e pode adicionar os seus próprios ou sobrescrevê-los. Polimorfismo: métodos com o mesmo nome que se comportam diferentemente em classes diferentes; tipicamente uma subclasse ** redefine** um método de seu pai, e a versão certa roda para cada objeto. Contenção: uma classe tem um objeto de outra classe como atributo (um carro tem um motor). "Identificar o recurso que restringe o acesso externo aos dados" é encapsulamento; "o termo para uma ocorrência de um objeto" é instância.

"Esboce a estrutura de uma classe" (três marcas): atributos (propriedades) que mantêm os dados do objeto, geralmente declarados como privados; métodos (procedimentos e funções) que atuam sobre esses atributos, geralmente públicos; e um construtor, um método que roda quando um objeto é criado para inicializar os atributos. "Dê três benefícios da OOP": o código é reutilizado através da herança; os dados são protegidos pelo encapsulamento, então só podem ser alterados pelos métodos próprios da classe; um programa grande é dividido em classes que são escritas e testadas independentemente, facilitando a manutenção e expansão; as classes modelam entidades do mundo real, tornando o design mais fácil de entender; o polimorfismo permite que a mesma chamada funcione para objetos diferentes.

A classe em pseudocódigo, conforme o Paper 3 a define:

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

Um objeto é criado com MyCar ← NEW Car("AB12 CDE", 2020) e usado com MyCar.AddMileage(150) e OUTPUT MyCar.GetMileage(). Uma subclasse reutiliza o construtor do pai através de 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

A mesma classe em Python, conforme o Paper 4 espera: os atributos tornam-se privados com dois underscores, o construtor é __init__, e uma subclasse nomeia seu pai entre parênteses e chama 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())

Em Java, as mesmas ideias são private/public campos, um construtor com o nome da classe, extends e super(…); em VB.NET Private/Public, Sub New, Inherits e MyBase.New. Um método polimórfico é escrito no pai e sobrescrito no filho com o mesmo nome; uma chamada através de uma variável do tipo pai executa a versão do filho.

Estruturas de dados como objetos. O Paper 4 constrói uma pilha, lista encadeada ou árvore binária a partir de uma Node classe cujos atributos são os dados e uma ou duas referências para outros nós; uma Tree (ou LinkedList) classe mantém a raiz (ou início) e os métodos.

Uma árvore binária de objetos Node: o objeto Tree Root aponta para o nó 15, cujas referências Left e Right apontam para os nós 8 e 19, e assim por diante, com None para referências vazias
Uma árvore binária construída a partir de objetos: cada Node mantém Data além das referências Left e Right, e o Tree mantém o Root; a inserção percorre as referências
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

Um método find percorre o mesmo caminho e retorna TRUE quando Current.Data = Target, FALSE quando atinge NULL; um método produção in-order é recursivo: output da subárvore esquerda, do nó, então da subárvore direita. Para uma lista encadeada o nó tem uma referência, Next, e a classe da lista mantém Start; para uma pilha construída a partir de uma lista, push e pop ambos funcionam em Start.

Exemplo resolvido. Um jogo tem personagens. Cada um tem um nome, saúde (começando em 100) e uma posição dada por X e Y. Escreva uma classe Character com um construtor e um método Move(DX, DY); então uma subclass Wizard que adiciona Mana (começando em 50) e um método CastSpell() que consome 10 mana e retorna TRUE se houvesse suficiente.

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

As marcas são para atributos privados, um construtor que define todos os atributos, a linha de herança, a chamada ao construtor do pai e um método que usa e altera os dados próprios do objeto. Quando a questão pede um diagrama de classe, desenhe uma caixa em três partes (nome; atributos com - para privado; métodos com + para público) e junte uma subclasse a seu pai com uma seta apontando para o pai.

Programação declarativa

Em programação declarativa 声明式编程, você diz o que calcular, não como — a execução decide os passos. Dois tipos:

  • programação funcional 函数式编程 — construída a partir de funções puras 纯函数 (sem efeitos colaterais 副作用; mesma entrada sempre gera mesma saída) compostas entre si. Exemplos: Haskell, Lisp.
  • programação lógica 逻辑编程 — declara fatos e regras; o motor responde a um objetivo (interrogação) por inferência. Exemplo: Prolog.

Um exemplo familiar de programação declarativa é SQL 结构化查询语言: SELECT * FROM Customer WHERE Country = 'UK' diz o que você quer, não como percorrer os registros.

Fatos, regras e objetivos são o que as provas testam no paradigma declarativo. Dados esses fatos 事实 (afirmações verdadeiras) e uma regra 规则 (uma conclusão válida quando suas condições forem válidas):

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).

"Escreva o resultado do objetivo type(X, wild)":* X = leopard, X = lion. O motor compara o objetivo com cada fato sequencialmente; toda correspondência é uma solução, e uma letra maiúscula é uma variável preenchida pela correspondência. "Escreva um fato para mostrar que um guepardo é selvagem": type(cheetah, wild). "Explique o que a linha 07 faz": ela define uma regra com a conclusão dangerous(X), que é verdadeira para qualquer X que seja tanto selvagem quanto grande, então dangerous(A) retorna A = leopard, A = lion. "Escreva uma regra: um recurso F pode estar disponível para um estilo de carroceria B se F for um recurso e B for um estilo de carroceria e F não estiver indisponível para B": may_be_available(F, B) IF feature(F) AND body_style(B) AND NOT unavailable(F, B). Copie exatamente os nomes dos predicados e a ordem dos argumentos usados nos fatos da questão; um novo fato termina com ponto final, e as condições de uma regra são unidas por AND.

Comparando paradigmas

Paradigma Força Linguagens típicas
Baixo nível controle máximo, velocidade assembly
Imperativa direta, intuitiva C, Python
Orientada a objetos modular, modela entidades Java, C#, Python
Funcional clara, sem efeitos colaterais Haskell, F#
Lógica inferência, regras Prolog
Banco de dados consultas de dados SQL

Linguagens modernas frequentemente misturam paradigmas — Python suporta todos os de procedural, POO e funcional. A escolha depende do problema.

Explorar

Laboratório de conceito de programação

Conecte exemplos à ideia de programação que eles mostram.

Vocabulário Treinar
Inglês Chinês Pinyin
programming paradigm/ˈprəʊɡræmɪŋ ˈpærədaɪm/ 编程范式 biān chéng fàn shì
facts/fækts/ 事实 shì shí
rule/ruːl/ 规则 guī zé
low-level/ləʊ ˈlevl/ 低级 dī jí
registers/ˈredʒɪstəz/ 寄存器 jì cún qì
memory addresses/ˈmeməri əˈdresɪz/ 内存地址 nèi cún dì zhǐ
objects/ˈɒbdʒekts/ 对象 duì xiàng
attributes/ˈætrɪbjuːts/ 属性 shǔ xìng
methods/ˈmeθədz/ 方法 fāng fǎ
machine code/məˈʃiːn kəʊd/ 机器码 jī qì mǎ
assembly language/əˈsemblɪ ˈlæŋɡwɪdʒ/ 汇编语言 huì biān yǔ yán
addressing modes/əˈdresɪŋ məʊdz/ 寻址方式 xún zhǐ fāng shì
classes/ˈklæsɪz/ 类 lèi
encapsulation/ɪnˌkæpsjʊˈleɪʃn/ 封装 fēng zhuāng
inheritance/ɪnˈherɪtəns/ 继承 jì chéng
subclass/ˈsʌbklæs/ 子类 zi lèi
superclass/ˈsuːpəklæs/ 父类 fù lèi
overriding/ˌəʊvəˈraɪdɪŋ/ 重写 chóng xiě
polymorphism/ˈpɒlɪmɔːfɪzəm/ 多态 duō tài
abstraction/əbˈstrækʃn/ 抽象 chōu xiàng
constructor/kənˈstrʌktə/ 构造函数 gòu zào hán shù
aggregation/ˌæɡrɪˈɡeɪʃn/ 聚合 jù hé
containment/kənˈteɪnmənt/ 包含 bāo hán
declarative programming/dɪˈklærətɪv ˈprəʊɡræmɪŋ/ 声明式编程 shēng míng shì biān chéng
functional programming/ˈfʌŋkʃənl ˈprəʊɡræmɪŋ/ 函数式编程 hán shù shì biān chéng
pure functions/pjʊə ˈfʌŋkʃnz/ 纯函数 chún hán shù
side effects/saɪd ɪˈfekts/ 副作用 fù zuò yòng
logic programming/ˈlɒdʒɪk ˈprəʊɡræmɪŋ/ 逻辑编程 luó jí biān chéng
SQL/ˌes kjuː ˈel/ 结构化查询语言 jié gòu huà chá xún yǔ yán
20.2

Processamento de arquivos

Programa
Os candidatos devem ser capazes de: Notas e orientações
Escrever código para realizar operações de processamento de arquivos Abrir (no modo leitura, escrita, anexar) e fechar um arquivo Ler um registro de um arquivo e escrever um registro em um arquivo Realizar operações de processamento de arquivos em arquivos série, sequenciais, aleatórios
Demonstre compreensão de uma exception e da importância do exception handling Saiba quando é apropriado usar exception handling Escreva código de programa para usar exception handling

Fonte: Programa Cambridge International

Isso estende o manuseio de arquivo 文件 do Tema 10, processando arquivos seriais, sequenciais e aleatórios (acesso direto). Operações em pseudocódigo: OPENFILE name FOR READ | WRITE | APPEND (READ abre um arquivo existente, WRITE cria/sobrescreve, APPEND adiciona ao final); READFILE name, line; WRITEFILE name, value; CLOSEFILE name; e EOF(name) que é VERDADEIRO no final.

Leia um arquivo inteiro:

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

Pesquise um arquivo (pare ao encontrar):

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"

Atualizando um arquivo in-place

A maioria das linguagens não pode editar um arquivo de texto in-place. Em vez disso: abra o original para READ e um arquivo temporário para WRITE; para cada linha, escreva a nova versão se deve mudar, senão a original; feche ambos; depois substitua o original pelo arquivo temporário. O mesmo padrão lida com exclusão de linhas (pule-as) e inserção de linhas.

Atualizando um arquivo in-place: leia o arquivo original, escreva as linhas alteradas para um arquivo temp, depois substitua o original pelo arquivo temp
Atualizando um arquivo in-place: leia o original, escreva alterações para um arquivo temp, depois substitua o original

Registros e arquivos de acesso aleatório

Modos de abertura. READ: o arquivo deve existir e a leitura começa no início. WRITE: um arquivo novo é criado, e um arquivo existente desse nome é sobrescrito. APPEND: a escrita adiciona ao final de um arquivo existente. Todo arquivo aberto é fechado com CLOSEFILE, e EOF(name) é TRUE quando o último item foi lido.

Três organizações de arquivos. Em um arquivo serial, os registros estão na ordem em que foram adicionados; em um arquivo sequencial, estão em ordem de chave; ambos são lidos do início. Um arquivo aleatório 随机文件 (arquivo de acesso direto) armazena cada registro em um endereço calculado a partir de sua chave por uma função de hashing 哈希, permitindo encontrar um registro sem ler os outros. Registros são declarados como tipo definido pelo usuário:

TYPE AccountRecord
    DECLARE AccNo : INTEGER
    DECLARE Name : STRING
    DECLARE Balance : REAL
    DECLARE Active : BOOLEAN
ENDTYPE
Uma chave 2317 hashada com MOD 1000 para o endereço 317, então SEEK e GETRECORD no arquivo Acc.dat, mostrado como uma fileira de slots de registro de tamanho igual com o slot 317 destacado
Encontrando um registro em um arquivo aleatório: a chave é hashada para um endereço, o ponteiro do arquivo vai diretamente até aquele slot e o registro é lido; nenhum outro registro é tocado

As operações de arquivo aleatório em pseudocódigo são OPENFILE "Acc.dat" FOR RANDOM, SEEK "Acc.dat", Address (move o ponteiro do arquivo para esse registro), GETRECORD "Acc.dat", Rec (lê o registro ali) e PUTRECORD "Acc.dat", Rec (escreve o registro ali). Encontrar um cliente por número de conta, conforme definido no 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"

Para armazenar um registro, hashie sua chave, SEEK para o endereço e PUTRECORD, avançando sobre qualquer slot já ocupado. Marcas vão para o hash, o SEEK antes do GET ou PUT, a comparação com o alvo, o tratamento de colisão e o fechamento do arquivo.

Exemplo resolvido. ActiveFile.dat contém AccountRecord registros. Escreva pseudocódigo que copie todos os registros cujo campo Active seja FALSE para o final de 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"

Arquivos de texto em Python (Paper 4): file = open("HighScore.txt", "r"), depois for line in file: com line.strip() e line.split(",") para separar os campos, int(…) para converter uma pontuação, e file.close(); para escrever, open(name, "w") (ou "a" para anexar) e file.write(str(score) + "\n"). Uma tabela de maiores pontuações é lida em uma lista de registros, a nova pontuação inserida em seu lugar, e toda a lista reescrita. O examinador atribui marcas pela abertura com modo correto, um laço que lê cada linha, a conversão de texto para números e o fechamento.

Armadilhas

Esquecer de fechar um arquivo (dados podem ser perdidos); abrir para WRITE quando queria APPEND (sobrescreve tudo); ler além de EOF; caminhos fixos — um caminho como /Users/Admin/data.txt falha em outra máquina, então use uma constante relativa como DataFile = "./data/scores.txt".

Explorar

Rota de acesso a arquivo

Siga um arquivo do armazenamento ao programa e de volta com segurança.

Vocabulário Treinar
Inglês Chinês Pinyin
file/faɪl/ 文件 wén jiàn
random file/ˈrændəm faɪl/ 随机文件 suí jī wén jiàn
hashing/ˈhæʃɪŋ/ 哈希 hā xī
20.2

Tratamento de exceções

Uma exceção 异常 é um erro ou condição inesperada durante a execução — divisão por zero, arquivo não encontrado, falha de rede, índice de array 数组 fora do intervalo. Tratamento de exceções 异常处理 permite que um programa detecte isso e responda graciosamente em vez de travar.

Importa porque programas reais enfrentam erros que não podem ser prevenidos antecipadamente (arquivos movidos, redes caídas, entrada inválida); sem isso, cada operação precisaria de sua própria verificação IF; e separa o fluxo normal do tratamento de erros, mantendo o caminho principal limpo. Por exemplo, um arquivo pode ser deletado por outro usuário entre seu programa verificar que existe e realmente abri-lo — você não pode prevenir isso, apenas lidar com a falha quando ocorrer.

"Descreva, com um exemplo, o que significa uma exceção (duas marcas). Um evento inesperado ou erro que ocorre durante a execução de um programa (em tempo de execução) e interrompe seu fluxo normal; por exemplo, dividir por zero, abrir um arquivo que não existe, converter entrada não numérica em inteiro, índice de array fora do intervalo ou esgotar memória. "Identifique duas possíveis causas de exceções" é respondido a partir dessa lista, mais "um dispositivo ou rede não está disponível" e "tipo de dado inválido inserido".

"Enumere as razões para incluir tratamento de exceções (três marcas). Para evitar que o programa trave (termine inesperadamente); para exibir uma mensagem significativa ao usuário em vez de um erro do sistema; para permitir que o programa recupere e continue, por exemplo pedindo a entrada novamente, ou fechar arquivos com segurança antes de terminar; e porque alguns erros não podem ser previstos quando o programa é escrito. "Descreva como evitar o término do programa devido a uma exceção: coloque as instruções que poderiam levantar uma exceção dentro de um bloco TENTAR; escreva um bloco EXCEPT (catch) para essa exceção que a trate, por exemplo exibindo uma mensagem, para que a execução continue após o bloco em vez de parar. "Explique o que significa tratamento de exceções: detectar uma exceção quando ocorre e executar código (o manipulador) que a lidem para que o programa continue.

Padrão

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

O bloco TRY contém o código que pode falhar; o primeiro bloco EXCEPT correspondente é executado. Linguagens reais também têm um catch-all EXCEPT e um bloco FINALLY que roda independentemente de exceção ter ocorrido — útil para limpeza (fechar arquivos).

Fluxo de exceção: se o bloco TRY levanta uma exceção, o controle salta para o EXCEPT correspondente; sem exceção, é pulado. De qualquer forma, o bloco FINALLY roda, depois o programa continua
Fluxo de exceção: uma exceção salta para o EXCEPT correspondente; FINALLY sempre roda antes do programa continuar

Levantando uma exceção

Uma subrotina que detecta um erro pode levantar 抛出 uma exceção para que o chamador a trate:

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

Onde tratar exceções

Trate-as perto do erro se a resposta for simples (uma mensagem, uma retrial), ou mais acima na pilha de chamada 调用栈 se apenas o código externo souber o que fazer (um loop GUI de nível superior registra o erro e mostra um diálogo amigável). Não engula exceções silenciosamente — pelo menos registre-as, caso contrário a depuração torna-se impossível.

Exceções comuns: FileNotFound, IOError, DivisionByZero, IndexOutOfRange, InvalidArgument, NullReference, OutOfMemory. Envolver cada operação falhante em um TRY com manipuladores EXCEPT adequados dá a um programa que degrada graciosamente em vez de travar.

Exemplo resolvido (Paper 4). Escreva uma função que leia números inteiros, um por linha, de um arquivo cujo nome é passado como parâmetro e os retorne em uma lista. Ela não deve travar se o arquivo não existir ou uma linha não for um número inteiro.

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

O bloco try contém o código que pode falhar (a abertura e a conversão); cada except nomeia uma exceção e faz algo útil; a função ainda retorna uma lista, então o chamador continua. Em Java a mesma estrutura é try { … } catch (FileNotFoundException e) { … } catch (NumberFormatException e) { … }; em VB.NET Try … Catch ex As FileNotFoundException … End Try. Marcas: as instruções arriscadas dentro do try, os nomes corretos de exceção, uma mensagem para cada, e o programa continuando depois; um catch-all except: ganha a marca de crash mas não a de "exceção apropriada".

Exemplo resolvido. Um arquivo de texto com membros precisa que o número de telefone de um membro seja alterado. Por que o programa não pode simplesmente sobrescrever essa linha, e qual é o padrão? As linhas de um arquivo de texto têm comprimentos diferentes, e o arquivo não possui espaços para absorver a diferença: uma substituição mais longa invadiria o próximo registro, e uma mais curta deixaria parte da linha antiga para trás. Portanto, o padrão consiste em abrir o original para LEITURA e um arquivo temporário para ESCRITA, ler cada linha sequencialmente, escrever a versão nova para a linha que muda e a linha original para todas as outras, fechar ambos e, em seguida, substituir o original pelo arquivo temporário. A mesma estrutura serve para excluir (omitar a linha) e inserir (escrever a linha extra). Observe que todas as linhas são escritas, não apenas a alterada — escrever apenas o novo registro e perder o resto do arquivo é um erro clássico.

Explorar

Como o tratamento de exceções flui

Passe passo a passo sobre o que acontece quando o código falha. A exceção salta fora do fluxo normal para um manipulador, FINALLY limpa de qualquer forma, e o programa continua em vez de travar.

Vocabulário Treinar
Inglês Chinês Pinyin
array/əˈreɪ/ 数组 shù zǔ
imperative programming/ɪmˈperətɪv ˈprəʊɡræmɪŋ/ 命令式编程 mìng lìng shì biān chéng
Variables/ˈveərɪəblz/ 变量 biàn liàng
object-oriented programming/ˈɒbdʒekt ˈɔːrɪəntɪd ˈprəʊɡræmɪŋ/ 面向对象编程 miàn xiàng duì xiàng biān chéng
instances/ˈɪnstənsɪz/ 实例 shí lì
exception/ekˈsepʃn/ 异常 yì cháng
exception handling/ekˈsepʃn ˈhændlɪŋ/ 异常处理 yì cháng chǔ lǐ
raise/reɪz/ 抛出 pāo chū
call stack/kɔːl stæk/ 调用栈 diào yòng zhàn
20.2

Definições aceitas pelo examinador

Uma questão de definição é avaliada contra wording fixo. Aprenda estas exatamente, e dê apenas uma resposta.

Termo Definição
paradigma de programação um estilo ou modo de programar, com sua própria forma de estruturar um programa
linguagem imperativa o programa é uma sequência de instruções que alteram o estado do programa; o programador diz como a tarefa é realizada
linguagem declarativa o programa declara fatos e regras, e o motor de inferência descobre como encontrar a resposta
classe um modelo que define os atributos e métodos dos objetos desse tipo
objeto (instância) uma ocorrência de uma classe, com seus próprios valores para os atributos
atributo um item de dados pertencente a uma classe
método um procedimento ou função pertencente a uma classe e que atua sobre seus atributos
encapsulamento manter os atributos e métodos juntos em uma classe e restringir o acesso externo aos dados, de modo que sejam alterados apenas por meio de métodos públicos
herança uma subclasse adquire os atributos e métodos de sua superclasse e pode adicioná-los ou sobrescrevê-los
polimorfismo métodos com o mesmo nome que se comportam de maneira diferente para classes distintas
construtor um método que é executado quando um objeto é criado e inicializa seus atributos
contenção uma classe possui um objeto de outra classe como um de seus atributos
fato uma afirmação em um programa declarativo que é verdadeira
regra uma conclusão que vale quando suas condições são verdadeiras
arquivo serial, sequencial, aleatório registros na ordem em que foram adicionados; registros em ordem de chave; cada registro em um endereço calculado a partir de sua chave
exceção um erro ou evento inesperado durante a execução que interrompe o fluxo normal
tratamento de exceções detectar uma exceção quando ela ocorre e executar código que a trata, para que o programa continue
20.2

Dicas de prova

  • Paradigmas: saiba a descrição de uma linha de cada um e esteja pronto para nomear o paradigma a partir de um trecho de código; perguntas de nível baixo exigem os cinco modos de endereçamento e o que o acumulador recebe.
  • Definições de POO aparecem em toda sessão: classe, objeto, atributo, método, encapsulamento, herança, polimorfismo, construtor. Escreva uma classe em pseudocódigo com atributos PRIVADOS, um CONSTRUTOR PÚBLICO e getters; uma subclasse com HERDA e SUPER.CONSTRUTOR.
  • Declarativa: um objetivo com uma variável retorna todos os fatos correspondentes; uma regra é uma conclusão SE condições unidas com E; copie exatamente os nomes dos predicados da questão.
  • Arquivos: os três modos e o que cada um faz com um arquivo existente; READFILE em um loop WHILE NOT EOF; arquivos aleatórios usam hash, SEEK, GETRECORD e PUTRECORD, com colisão de passo-over.
  • Exceções: definição com exemplo, três razões para tratá-las, e TRY com EXCEPT nomeado que permite ao programa continuar.

Erros comuns

  • Descrever um programa declarativo como "uma sequência de passos que dá a resposta"; ele declara o que é verdadeiro e o que se deseja, não como.
  • Confundir um objeto com uma classe, ou uma instância com um atributo; a pergunta "uma ocorrência de um objeto" quer dizer instância.
  • Declarar os atributos PÚBLICOS, ou acessá-los de fora da classe em vez de por meio de um getter, o que perde as marcas de encapsulamento.
  • O construtor de uma subclasse que define diretamente os atributos do pai em vez de chamar SUPER.CONSTRUTOR.
  • Explicar polimorfismo como "muitos objetos"; é o mesmo nome de método se comportando diferentemente para classes distintas.
  • Abrir um arquivo FOR WRITE para adicionar um registro, o que destrói o conteúdo existente; use APPEND.
  • Ler um arquivo aleatório do início; USE SEEK no endereço hash primeiro.
  • Colocar o manipulador de exceções ao redor de código que não pode falhar, ou capturar tudo sem mensagem, ou descrever tratamento de exceções como "verificar a entrada com IF".

Aulas interativas sobre este tópico

Passe por ele passo a passo, com exercícios de verificação instantânea.

Provas Anteriores

Mais tópicos em Ciência da Computação do A-Level

Entrar ou criar conta

IGCSE, A-Level & AP