Os conjuntos iniciam vazios e cada linha da entrada descreve uma operação a ser realizada sobre um ou entre os dois conjuntos. Cada operação deve ser realizada através de uma função chamada pela main, que se encontra em um arquivo separado; deste modo, funções em outro arquivo deverão ser criadas e deverão ser capazes de realizar operações referentes a: pertence, contido, adição, subtração, união, interseção, diferença e complemento, todas obedecendo a descrição referente dada no comentário que a antecede.
As operações na entrada são:
C: Não faça nada no conjunto, só imprima o seu conteúdo
C = {x1, x2, x3, ... ,xn}: substitui o conteúdo do conjunto C com os N elementos.
x e C: verifica se o elemento x pertence ao conjunto C.
C1 c C2: verifica se o conjunto C1 está contido no conjunto C2.
C += x: adiciona o elemento x ao conjunto C.
C -= x: remove o elemento x do conjunto C.
C1 u C2: exibe a união entre C1 e C2.
C1 ^ C2: exibe a interseção entre C1 e C2.
C1 \ C2: exibe a diferença entre C1 e C2.
-C: exibe o complemento do conjunto C.
Q: Encerra a execução do programa
Onde:
C é um dos conjuntos A ou B
C1 e C2 são conjuntos distintos A e B em qualquer ordem.
x é um elemento
x1, x2, x3, ... , xn são N elementos.
Q é a letra Q
Saída: cada linha da saída do programa contém o resultado da execução de cada operação dada na entrada, de forma que a saída possui uma linha a menos que a quantidade de linhas da entrada.
Arquivo com a função main:
#include <stdio.h>
int pertence(char conj[], int tam, char letra);
int contido(char conj1[], char conj2[], int tam1, int tam2);
int adicao(char conj[], int tam, char letra);
int subtracao(char conj[], int tam, char letra);
int uniao(char destRes[], char conj1[], char conj2[], int tam1, int tam2);
int intersecao(char destRes[], char conj1[], char conj2[], int tam1, int tam2);
int diferenca(char destRes[], char conj1[], char conj2[], int tam1, int tam2);
int complemento(char conjRes[], char conj[], int tam);
#define CONJ_TAM 30
/* Funcao: bubble_sort
*
* Parametros:
* vet: vetor de inteiros a ser ordenado
* tam: tamanho/quantidade de elementos do vetor 'vet'
*
* Retorno:
* O vetor e ordenado "in place", no proprio vetor de entrada.
* Nao ha retorno da funcao.
*
* Descricao:
* Essa e uma simples funcao para ordenar os elementos do vetor de entrada
* 'vet'. Ela usa o algoritmo "bubble sort".
*/
void bubble_sort(char vet[], int tam) {
int i, j;
char aux;
for (i = tam - 1; i > 0; i--) {
for (j = 0; j < i; j++) {
if (vet[j] > vet[j + 1]) {
aux = vet[j];
vet[j] = vet[j + 1];
vet[j + 1] = aux;
}
}
}
}
/* Funcao: copia_conjunto
*
* Parametros:
* conjRes: vetor contendo o conjunto de saida/resultado da operacao
* conj: vetor contendo o conjunto de entrada
* tam: tamanho do conjunto 'conj'
*
* Retorno:
* A copia e retornada no parametro 'conjRes'.
* Nao ha retorno da funcao.
*
* Descricao:
* Essa é uma simples funcao que copia o conteudo do conjunto 'conj' para
* conjunto 'conjRes'. No final conjRes tem tamanho 'tam'.
*/
void copia_conjunto(char conjRes[], char conj[], int tam) {
int i = 0;
for (i = 0; i < tam; i++) {
conjRes[i] = conj[i];
}
}
/* Funcao: imprime_conjunto
*
* Parametros:
* conj: vetor contendo o conjunto de entrada
* tam: tamanho do conjunto 'conj'
* nome: nome/descricao do conjunto a ser impresso
*
* Retorno:
* nao ha retorno.
*
* Descricao:
* Essa funcao imprime (na saida padrao) um conjunto no formato
* "<nome> = {x1, x2, ... , xn}", onde nome e o valor do parametro 'nome' e
* x1, x2, ..., xn sao os elementos do conjunto 'conj' de tamanho 'tam'. Note
* na impressao nao ha espacos entre as chaves e o primeiro e ultimo elemento.
* Tambem nao ha virgula apos o ultimo elemento. Caso o vetor esteja vazio,
* com 'tam == 0', o conjunto resultado e vazio, sem espaco entre os
* conjuntos, assim: "<nome> = {}".
* Essa funcao sempre imprime uma marca de final de linha '\n' ao final da
* impressao.
* A impressao sempre imprime os valores do conjunto em ordem crescente, porem
* nao altera os valores do conjunto original. Para isso, o conjunto a ser
* impresso e copiado para um vetor temporario 'conj_temp' e depois ordenados,
* antes de realizar a impressao.
*/
void imprime_conjunto(char conj[], int tam, char nome[]) {
int i = 0;
char conj_temp[CONJ_TAM];
copia_conjunto(conj_temp, conj, tam);
conj = conj_temp;
bubble_sort(conj, tam);
printf("%s = {", nome);
for (i = 0; i < tam - 1; i++) {
printf("%c, ", conj[i]);
}
if (tam > 0) {
printf("%c", conj[tam - 1]);
}
printf("}\n");
}
/* Funcao: analisa_atribuicao
*
* Parametros:
* conj: vetor contendo o conjunto de saida
*
* Retorno:
* 'conj' contem o conjunto reinicializado e incluido todos os elementos
* atribuidos
* Retorna o tamanho do novo conjunto
*
* Descricao:
* Na atribuicao o conteudo de um dos conjuntos e substituido pelos valores
* entre chaves da leitura.
* Essa funcao apaga o conteudo do conjunto 'conj' reiniciando o seu tamanho.
* Em seguida le da entrada padrao o conteudo a ser atribuido ao conjunto, um
* valor por vez, adicionando os valores ao conjunto.
*/
int analisa_atribuicao(char conj[]) {
char letra = ' ';
int tam = 0;
scanf(" { ");
while (0 < scanf(" %c ,", &letra)) {
if(letra=='}')return tam;
tam = adicao(conj, tam, letra);
}
return tam;
}
/* Funcao: analisa_add_sub
*
* Parametros:
* op: qual operacao realizar, valores: adicao '+' ou subtracao '-'
* conj: vetor contendo o conjunto de entrada
* tam: tamanho do conjunto 'conj'
*
* Retorno:
* 'conj' contem o conjunto atualizado com a adicao ou remocao de um elemento
* retorna o novo tamanho
*
* Descricao:
* Le da entrada padrao o numero a ser adicionado ou subtraido do conjunto
* 'conj' e chama a funcao correspondente indicada pela operacao 'op'.
*/
int analisa_add_sub(char op, char conj[], int tam) {
char letra = ' ';
scanf(" %c", &letra);
if (op == '+') {
return adicao(conj, tam, letra);
} else {
return subtracao(conj, tam, letra);
}
}
/* Funcao: analisa_operacao
*
* Parametros:
* conj1: vetor contendo o conjunto de entrada do primeiro operando
* conj2: vetor contendo o conjunto de entrada do segundo operando
* tam1: tamanho do conjunto 'conj1'
* tam2: tamanho do conjunto 'conj2'
* nome1: nome do primeiro conjunto. valores: 'A' ou 'B'
* nome2: nome do segundo conjunto. valores: o complementar de 'nome1'
*
* Retorno:
* Se a operacao for sobre modificar 1 conjunto (+=, -=, =), entao 'conj1' e
* alterado com os novos valores assim como 'tam1' para refletir o novo
* tamanho. Se for qualquer outra operacao, nenhum dos conjuntos de entrada
* sao modificados.
* Retorna o tamanho do conjunto 1.
*
* Descricao:
* Le da entrada padrao qual operacao deve ser realizada e chama a funcao
* correspondente. Essa funcao analiza todas as operacoes menos a de pertence,
* pois assume que as operacoes sao uma das que comecam com a definicao de um
* conjunto, e qual o primeiro conjunto e indicado pelo 'conj1', sendo 'conj2'
* o segundo conjunto. Todas as operacoes, com excessao do pertence 'e'
* comecam com um conjunto, seguido da operacao (com excessao da de impressao)
* e o segundo conjunto.
* Ela le da entrada padrao a operacao, chama as funcoes correspondente de
* cada operacao e no final imprime o resultado. Para operacoes sobre um unico
* conjunto, o resultado a ser impresso e o proprio conjunto 'conj1'. Para
* operacoes sobre os dois conjuntos, o resultado a ser impresso esta em um
* conjunto novo 'conjRes' e o nome a ser impresso e a operacao com os
* conjuntos na ordem dada, assim como na entrada.
*/
int analisa_operacao(char conj1[], char conj2[], int tam1, int tam2, char nome1, char nome2) {
char nomeRes[CONJ_TAM] = "\0";
char C[CONJ_TAM];
int tam_C = 0;
scanf("%*[\t ]");
char b[CONJ_TAM] = "\0";
scanf("%29[-=+cu^\\]", b);
char op = b[0];
switch (op) {
case '+':
case '-':
tam1 = analisa_add_sub(op, conj1, tam1);
break;
case '=':
tam1 = analisa_atribuicao(conj1);
break;
case 'u':
scanf(" %c", &nome2);
tam_C = uniao(C, conj1, conj2, tam1, tam2);
break;
case '^':
scanf(" %c", &nome2);
tam_C = intersecao(C, conj1, conj2, tam1, tam2);
break;
case '\\':
scanf(" %c", &nome2);
tam_C = diferenca(C, conj1, conj2, tam1, tam2);
break;
case 'c':
scanf(" %c", &nome2);
if (0 == contido(conj1, conj2, tam1, tam2)) {
printf("falso\n");
} else {
printf("verdadeiro\n");
}
return tam1;
}
if (op == '+' || op == '-' || op == '=' || op == '\0') {
nomeRes[0] = nome1;
nomeRes[1] = '\0';
imprime_conjunto(conj1, tam1, nomeRes);
}
if (op == 'u' || op == '^' || op == '\\') {
sprintf(nomeRes, "%c %c %c", nome1, op, nome2);
imprime_conjunto(C, tam_C, nomeRes);
}
return tam1;
}
/* Funcao: analisa_pertence
*
* Parametros:
* palavra: valor ja lido da entrada, que sera analizado novamente, deve
* conter o numero a ser realizado a operacao de pertence.
* A: vetor contendo o conjunto de nome 'A'
* B: vetor contendo o conjunto de nome 'B'
* tam_A: tamanho do conjunto 'A'
* tam_B: tamanho do conjunto 'B'
*
* Retorno:
* Nao ha retorno da funcao.
*
* Descricao:
* Le da entrada padrao o conjunto a se aplicar a funcao pertente e tambem le
* o numero a ser utillizado a partir da string 'palavra'. Ao final ela
* imprime o resultado da operacao.
*/
void analisa_pertence(char palavra[], char A[], char B[], int tam_A, int tam_B) {
int res = 0;
char letra = ' ';
char c = ' ';
sscanf(palavra, "%c", &letra);
scanf(" e %c", &c);
if (c == 'A') {
res = pertence(A, tam_A, letra);
} else {
res = pertence(B, tam_B, letra);
}
if (res == 0) {
printf("falso\n");
} else {
printf("verdadeiro\n");
}
}
/* Funcao: analisa_complemento
*
* Parametros:
* conj: vetor contendo o conjunto para calcular o complemento
* tam: tamanho do conjunto conj
* nome: nome do conjunto conj
*
* Retorno:
* Nao ha retorno da funcao.
*
* Descricao:
* Imprime o complemento do conjunto conj
*/
void analisa_complemento(char conj[], int tam, char nome) {
char nomeRes[CONJ_TAM] = "\0";
int tamRes = 0;
char conjRes[CONJ_TAM];
tamRes = complemento(conjRes, conj, tam);
nomeRes[0] = '-';
nomeRes[1] = nome;
imprime_conjunto(conjRes, tamRes, nomeRes);
}
/* Funcao: main
*
* Descricao:
* Esse programa le as operacoes sobre os conjuntos 'A' e 'B', uma por linha e
* as realiza em ordem. Os conjuntos estao inicialmente vazios.
*
* A logica da main e a seguinte. Ler da entrada padrao a primeira palavra de
* cada linha, o que indica se uma operacao comeca com a letra de um conjunto
* ('A' ou 'B'), o simbolo -, o comando 'q' para terminar o programa ou a operacao de
* pertence, que comeca com uma letra. Apos ela chama funcoes para cada um
* desses 3 tipos (ou termina o programa) e antes de ler uma nova linha, le da
* entrada padrao o resto da linha que nao tenha sido lido.
*/
int main() {
char palavra[CONJ_TAM] = "\0";
char A[CONJ_TAM];
char B[CONJ_TAM];
int tam_A = 0;
int tam_B = 0;
while (EOF != scanf("%29s", palavra)) {
char op = palavra[0];
// printf("\n");
// int i=0;
// printf("A = ");
// for(i=0; i<tam_A; ++i){
// printf("%c, ", A[i]);
// }
// printf("\n");
// printf("B = ");
// for(i=0; i<tam_B; ++i){
// printf("%c, ", B[i]);
// }
// printf("\n");
// printf("\n");
switch (op) {
case 'A':
tam_A = analisa_operacao(A, B, tam_A, tam_B, 'A', 'B');
break;
case 'B':
tam_B = analisa_operacao(B, A, tam_B, tam_A, 'B', 'A');
break;
case 'Q':
return 0;
case '-':
if(palavra[1]=='A')
analisa_complemento(A, tam_A, 'A');
else
analisa_complemento(B, tam_B, 'B');
break;
default:
analisa_pertence(palavra, A, B, tam_A, tam_B);
}
scanf("[^\n]");
}
return 0;
}
(Obs.: o código/arquivo acima foi criado por PEDs do IC-Unicamp.)
O objetivo deste arquivo abaixo, criado por mim, é implementar as funções requeridas pela função main acima, que, novamente, se encontra em outro arquivo.
/* Funcao: pertence
*
* Parametros:
* conj: vetor contendo o conjunto de entrada
* tam: tamanho do conjunto
* letra: elemento a ser verificado pertinencia
*
* Retorno:
* 1 se letra pertence a conj e 0 caso contrario
*/
int pertence(char conj[], int tam, char letra) {
//Percorro o conjunto comparando os elementos com a minha letra. Se encontrar uma que seja igual, retorna 1 e fim. Se percorri o conjunto todo e não retornei nada, é porque não encontrei elemento igual à letra, logo retorno 0.
for (int i = 0; i < tam; i++)
if (conj[i] == letra) return 1;
return 0;
}
/* Funcao: contido
*
* Parametros:
* conj1: vetor contendo um conjunto de entrada
* conj2: vetor contendo um conjunto de entrada
* tam1: tamanho do conjunto conj1
* tam2: tamanho do conjunto conj2
*
* Retorno:
* 1 se conj1 esta contido em conj2 e 0 caso contrario
*/
int contido(char conj1[], char conj2[], int tam1, int tam2) {
if (tam1 > tam2) //Se tamanho do conj1 for maior que o do conj2, conj1 não pode estar contido em conj2.
return 0;
for (int i = 0; i < tam1; i++) //Percorro todos os elementos do conj1. Se algum não pertencer ao conj2, ele não está contido.
if (!pertence(conj2, tam2, conj1[i]))
return 0;
//Retorna 1 também caso conj1 seja {}, pois o programa não entra no for anterior no entanto deve retornar verdadeiro para conj1 estar contido em conj2. E, caso entrou no for anterior e não encontrou elemento em conj1 que não pertencente a conj2, deve também retornar 1.
return 1;
//Este retorno também funcionaria se fosse permitido o uso da biblioteca string:
//return (strspn(conj1, conj2) == strlen(conj1));
}
/* Funcoes: adicao e subtracao
*
* Parametros:
* conj: vetor contendo o conjunto que tera incluso ou removido o elemento
* tam: tamanho do conjunto
* letra: elemento a ser adicionado ou removido
*
* Retorno:
* tamanho do conjunto apos a operacao.
*/
int adicao(char conj[], int tam, char letra) {
//Se já houver essa letra no conjunto, não a adicionarei de novo, logo retorno o tamanho sem alteração.
if (pertence(conj, tam, letra))
return tam;
//Se não entrei no if, significa que ainda não há esse elemento no conjunto, logo o adiciono:
conj[tam] = letra; //...na última posição vai a letra e na próxima o \n.
conj[tam + 1] = '\0';
return tam + 1; //E então retorno o novo tamanho.
}
int subtracao(char conj[], int tam, char letra) {
for (int i = 0; conj[i] != '\0'; i++) //Percorro o vetor procurando pelo elemento a ser removido.
if (conj[i] == letra) { //Quando o encontro, sigo com outro for para "trazer para frente" os elementos seguintes.
for (int j = i; j < tam; j++)
conj[j] = conj[j + 1];
tam--; //Registro que um elemento foi removido.
break;
}
return tam; //Retorna o tamanho.
}
/* Funcoes: uniao, intersecao e diferenca
*
* Parametros:
* conjRes: vetor contendo o conjunto de saida/resultado da operacao
* conj1: vetor contendo o conjunto de entrada do primeiro operando
* conj2: vetor contendo o conjunto de entrada do segundo operando
* tam1: tamanho do conjunto conj1
* tam2: tamanho do conjunto conj2
*
* Retorno:
* tamanho do conjunto de saida conjRes.
*/
int uniao(char destRes[], char conj1[], char conj2[], int tam1, int tam2) {
int tamRes = 0;
for (int i = 0; i < tam1; i++) //Percorro o conj1 e vejo se ele ainda não foi adicionado ao conjunto resultado.
if (!pertence(destRes, tamRes, conj1[i])) //Se ainda não foi adicionado, o adiciono:
destRes[tamRes] = conj1[i], tamRes++;
for (int i = 0; i < tam2; i++) //A mesma operação realizada anteriormente, agora com o conj2.
if (!pertence(destRes, tamRes, conj2[i]))
destRes[tamRes] = conj2[i], tamRes++;
return tamRes;
}
int intersecao(char destRes[], char conj1[], char conj2[], int tam1, int tam2) {
int tamRes = 0;
for (int i = 0; i < tam1; i++) //A mesma operação feita em união, no entanto a condição é, para cada elemento do conj1, <não já estar em conjunto resultado> e <pertencer ao segundo conjunto>.
if (!pertence(destRes, tamRes, conj1[i]) && pertence(conj2, tam2, conj1[i]))
destRes[tamRes] = conj1[i], tamRes++;
return tamRes;
}
int diferenca(char destRes[], char conj1[], char conj2[], int tam1, int tam2) {
int tamRes = 0;
for (int i = 0; i < tam1; i++) //A mesma operação feita em interseção, no entanto com a condição é, para cada elemento do conj1, <não já pertencer ao conjunto resultado> e <não pertencer ao segundo conjunto (conj2)>.
if (!pertence(destRes, tamRes, conj1[i]) && !pertence(conj2, tam2, conj1[i]))
destRes[tamRes] = conj1[i], tamRes++;
return tamRes;
}
/* Funcao: complemento
* Guarda em conjRes o resultado da operação U-conj, onde U é o conjunto
* de todas as letras minusculas do alfabeto ingles
*
* Parametros:
* conjRes: vetor contendo o conjunto de saida/resultado da operacao
* conj: vetor contendo o conjunto de entrada do primeiro operando
* tam: tamanho do conjunto conj
*
* Retorno:
* tamanho do conjunto de saida conjRes.
*/
int complemento(char conjRes[], char conj[], int tam) {
char U[] = {'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'}; //Defino os elementos do "conjunto universo" e seu tamanho.
int tamU = 26, tamRes = 0;
for (int i = 0; i < tamU; i++) //Percorro o conjunto U e adiciono ao conjunto resultado aqueles que não pertencem ao conjunto de entrada.
if (!pertence(conj, tam, U[i]))
conjRes[tamRes] = U[i], tamRes++;
return tamRes;
}