5  A linguagem de programação

Nesta página vamos revisar os aspetos principais da linguagem de programação do sistema GAP.

5.1 Expressões if-then-else

A expressão if-then (manual) está usada quando queremos executar instruções de acordo com alguma condição que pode ser verdadeira ou falsa (Seção 2.2). Considere o seguinte exemplo:

gap> a := 3;                # seja a inicialmente 3
3
gap> if a mod 2 = 0 then    # se a for par, dividimos a por 2
> a := a/2;
> fi;
gap> a;                     # valor de a continua 3
3
gap> if a mod 2 = 1 then    # se a for ímpar, multiplicamos a por 3 e somamos 1
> a := 3*a+1;
> fi;
gap> a;                     # o valor de a mudou
10

Expressões mais complicadas podem ser montadas usando as expressões else e elif.

gap> a := 3;
3
gap> if a mod 2 = 0 then 
> a := a/2;
> else
> a := 3*a+1;
> fi;
gap> a;
10
gap> a := 8;
8
gap> if a mod 2 = 0 then
> a := a/2;
> elif a mod 2 = 1 then 
> a := 3*a+1;
> else
> a := -2
> fi;
gap> a;
4

5.2 Laço for

Usamos for quando queremos executar a mesma peça de código várias vezes (manual). Por exemplo, queremos calcular o quadrado dos números entre 1 e 10.

gap> for i in [1..10] do
> Print( "O quadrado de ", i, " é ", i^2, "\n" );
> od;
O quadrado de 1 é 1
O quadrado de 2 é 4
O quadrado de 3 é 9
O quadrado de 4 é 16
O quadrado de 5 é 25
O quadrado de 6 é 36
O quadrado de 7 é 49
O quadrado de 8 é 64
O quadrado de 9 é 81
O quadrado de 10 é 100

Observamos os seguintes regras importantes:

  1. a palavra chave for é seguida por uma variável, neste caso i;
  2. depois temos a palavra in seguida por uma lista (Seção 3.1);
  3. depois da lista temos a palavra do;
  4. o laço está fechado pela palavra od (do reverso).

O ciclo executa o código entre as palavras do e od para todo valor i na lista que aparece depois da palavra in.

5.3 Laço while

Uma outra maneira de criar um laço é usar a construção com while (manual). Considere o seguinte exemplo.

gap> while i <= 10 do 
> Print( "O quadrado de ", i, " é ", i^2, "\n" );
> i := i + 1;
> od;
O quadrado de 0 é 0
O quadrado de 1 é 1
O quadrado de 2 é 4
O quadrado de 3 é 9
O quadrado de 4 é 16
O quadrado de 5 é 25
O quadrado de 6 é 36
O quadrado de 7 é 49
O quadrado de 8 é 64
O quadrado de 9 é 81
O quadrado de 10 é 100

As regras de construir este tipo de laço são as seguintes:

  1. a palavra while (enquanto) está seguida por uma condição lógica (booleana) e depois pela palavra do;
  2. o laço está fechado pela palavra od;
  3. a peça de código está executada entre as palavras do e od enquanto (while) a condição está verdadeira (Seção 2.2).

Note que no laço while, o valor da variável i precisa ser incrementado manualmente.

5.4 Laço repeat

O terceiro tipo de laço pode ser construído com a palavra repeat (manual). Considere o seguinte exemplo:

gap> i := 0;
0
gap> repeat
> Print( "O quadrado de ", i, " é ", i^2, "\n" );
> i := i + 1;
> until i = 10;
O quadrado de 0 é 0
O quadrado de 1 é 1
O quadrado de 2 é 4
O quadrado de 3 é 9
O quadrado de 4 é 16
O quadrado de 5 é 25
O quadrado de 6 é 36
O quadrado de 7 é 49
O quadrado de 8 é 64
O quadrado de 9 é 81

Observamos que

  1. O laço está iniciado pela palavra repeat;
  2. na última linha, temos a palavra until seguida por uma condição lógica;
  3. o sistema termina a execução do laço assim que a condição depois de repeat vira verdadeira (Seção 2.2).

A diferença entre os laços construídos por while e repeat é que no caso de while, a decisão está feita antes da execução do código, enquanto no caso de repeata mesma decisão está feita depois da execução. Em particular, o laço repeat sempre será executado pelo menos uma vez. Considere o seguinte código e explique o comportamento.

gap> i := 11;
11
gap> while i <= 10 do
> Print( "O quadrado de ", i, " é ", i^2, "\n" );
> i := i + 1;
> od;

gap> i := 11;
11
gap> repeat
> Print( "O quadrado de ", i, " é ", i^2, "\n" );
> i := 11;
> until i >= 10;
O quadrado de 11 é 121

5.5 Tarefa

  1. Calcule os cubos dos números entre \(10\) e \(20\) usando laço for, while e repeat.
  2. Modifique os laços no item anterior, usando if, em tal forma que apenas os cubos dos números pares serão calculados.

5.6 Funções

Funções em GAP podem ser usadas para realizar computações que queremos fazer várias vezes com diferentes objetos. Por exemplo a função que, dado x, computa 3*x+1 pode ser definida de duas maneiras diferentes.

gap> f1 := function( x )
> return 3*x+1;
> end;
function( x ) ... end
gap> f1( 5 );
16

Observe que o valor da função é devolvido pela expressão return.

gap> f2 := x -> 3*x + 1;
function( x ) ... end
gap> f2( -2 );
-5
gap> f1 = f2;
false
# As funções f1 e f2 fazem a mesma coisa, mas são objetos distintos

Nos exemplos anteriores, a função não precisa de variáveis adicionais para executar sua tarefa. Isso pode acontecer com funções simples, mas uma função mais complicada pode precisar introduzir as suas próprias variáveis, chamadas de variáveis locais. Considere a seguinte implementação do exemplo em cima.

gap> f3 := function( x )
> local y;
> y := 3*x;
> return y+1;
> end;
function( x ) ... end
gap> f3(4);
13
gap> y;
Error, Variable: 'y' must have a value
not in any function at *stdin*:44

As últimas linhas do exemplo anterior mostram que o variável y não está visível fora da função. Isso permite que variáveis com o mesmo nome sejam usadas em várias funções sem que sejam confundidas. O mesmo também impede que a varíavel local y definida dentro da função f3 seja confundida com uma possível variável global também chamada de y. O seguinte código mostra que o nome y tem significado diferente dentro e fora da função.

gap> f3 := function( x )
> local y;
> y := 3*x;
> Print( "O valor de y é ", y, "\n" );
> return y+1;
> end;
function( x ) ... end
gap> y := -5;
-5
gap> f3(10);
O valor de y é 30
31
gap> y;
-5

Funções podem receber mais que um argumento. Considere o seguinte exemplo.

gap> my_sum := function( x, y )
> return x+y;
> end;
function( x, y ) ... end
gap> my_sum( 4, -1 );
3

GAP permite também um número varíavel de argumentos como no seguinte exemplo.

gap> f4 := function( a, args... )
> local b;
> if Length( args ) >  0 then
> b := args[1];
> else
> b := true;
> fi;
> if b then
> return 3*a-1;
> else
> return a/2;
> fi;
> end;
function( a, args... ) ... end

Esta função devolve \(3a-1\) se tem apenas um argumento. No entanto, o usuário pode fornecer um segundo argumento que pode ser true ou false. No caso de true, a função devolve \(3a-1\), mas no caso de false, a função devolve \(a/2\).

gap> f4(4);
11
gap> f4(3);
8
gap> f4(3, true);
8
gap> f4(3, false);
3/2
gap> f4(4, false);
2

5.7 Tarefa

  1. Escreva uma função collatz( n ) que devolve \(3n-1\) quando \(n\) for ímpar e \(n/2\) quando \(n\) for par.
  2. Escreva uma função sum_divisors( n ) que devolve a soma dos divisores entre \(1\) e \(n-1\) de um número natural \(n\).

No item 1., precisa testar, usando if, (Seção 5.1) se o input é par ou ímpar e para isso pode usar a operação mod (Seção 2.1). Se precisar de ajuda com funções, consulte a Seção 5.6.

No item 2., pode usar as funções DivisorsInt (manual) e Sum (manual). Também pode precisar de if para decidir se n=0, pois DivisorsInt( 0 ) não funciona (Seção 5.1).

5.8 Break e continue

As expressões break (manual) e continue (manual) podem ser usadas em laços.

Quando a expressão break está executada, o sistema sai do laço. No caso de continue, o sistema sai da iteração atual e passa para a próxima iteração.

gap> for i in [1..10] do
> if i = 5 then break; fi;
> Print( i, "\n" );
> od;
1
2
3
4
gap> for i in [1..10] do
> if i = 5 then continue; fi;
> Print( i, "\n" );
> od;
1
2
3
4
6
7
8
9
10

5.9 Tarefa

  1. Resolve o item 2. da tarefa na Seção 5.5 usando a expressão continue.
  2. Usando while true do e break ache o menor primo que seja maior que \(10000000\) (use IsPrimeInt).