Jump to content

|| vs && em getchar()


Recommended Posts

Posted

Andei á martelada para fazer este programa do Livro C Programming Language, mais tempo do que o que eu expectava.

Problema: escreve um programa que faça o print de uma palavra por linha.

condições: Não usar arrays e trabalhar apenas com: condições (if, else, else if); ciclos(for ou while); operadores(||,&&,!=,==)... enfim toda a matéria que se tem dada até aquela secção do primeiro capitulo do livro do conhecidissímo livro do Dennis Ritchie e Brian Kerningham.

#include <stdio.h>

#define IN  1	/*inside a word*/
#define OUT 0	/*outside a word*/

/*Write a program that prints its input one word per line*/

int main(void)
{
	int c,state;
	state=OUT;
	while((c=getchar())!=EOF)
	{
		if(c!=' ' && c!='\t' && c!='\n')
		{
			state=IN;
			putchar(c);
		}
		else if(state==IN)
		{
			state=OUT;
			putchar('\n');
		}
	}
return 0;
}

Porreiro o código corre bem, mas e para lá chegar!?!

Andei á porra e á massa com aquele primeiro if:

		if(c!=' ' && c!='\t' && c!='\n')
	

...porque eu estava naquela de utilizar o operador || em vez do && que fez finalmente o programa correr bem! Mas sinceramente, e esta é a razão deste post ainda não percebi a razão de o operador || em

		if(c!=' ' || c!='\t' || c!='\n')

dar um comportamento inesperado! Alguém me pode dizer o que diferencia estes dois operadores para este caso especifico? Ou seja se estamos a analisar caracter a caracter em c com o getchar() qual é a diferença?

Obrigado desde já e um agradecimento especial ao HHH que tanta ajuda tem dado, muitas vezes (eu incluído) sem o pessoal merecer...  

Posted (edited)

Não sei se ajuda mas se fizeres o seguinte exercício:

2 == 3 || 3 == 2

consegues perceber qual o resultado?

Ou seja, por extenso é algo como:

2 é igual a 3 OU 3 é igual a 2 ???

Ambas as afirmações são falsas, logo o resultado é falso, portanto o 'if' não é executado

 

Agora:

2 == 3 || 3 == 3

Consegues perceber qual é o resultado?

Ou seja, por extenso, é algo como:

2 é igual a 3 OU 3 é igual a 3?

Bom aqui a da esquerda é falsa e a da direita é verdadeira. Se souveres o operador OU, sabes que desde que uma das verificações seja verdadeira, o resultado final é verdadeiro, logo o 'if' é executado!

 

No caso específico é igual, ou seja, desde que apenas uma seja verdadeira, o 'if' será executado. Contrariamente, se usares o operador '&&', o 'if' só será executado, se e só se a totalidade das condições comparadas for verdadeira. Basta uma ser falsa para o resultado final ser falso.

 

Outro exemplo que podes tomar é o equivalente mas com operadores binários:

se considerarmos o '0' como falso, e o '1' como verdadeiro, temos:

AND

0 0 --> 0

0 1 --> 0

1 0 --> 0

1 1 --> 1

Como vez, o resultado final só é 1, quando ambas as variáveis são verdadeiras.

No OR

0 0 --> 0

0 1 -->1

1 0 --> 1

1 1 --> 1

Ou seja, basta apenas uma delas ser verdadeira para o resultado final ser verdadeiro.

No caso do 'if' que apresentas, se eu o "disser" por extenso, era algo do género:

Só salta (falha) o 'if' caso a variável 'c' não seja nenhum daqueles 3 caractéres. Fora isso, o 'if' é sempre executado

Ajudei ou compliquei?

Edited by PsySc0rpi0n
  • Vote 1

Kurt Cobain - Grunge misses you

Nissan GT-R - beast killer

Posted
29 minutos atrás, PsySc0rpi0n disse:

No caso específico é igual, ou seja, desde que apenas uma seja verdadeira, o 'if' será executado. Contrariamente, se usares o operador '&&', o 'if' só será executado, se e só se a totalidade das condições comparadas for verdadeira. Basta uma ser falsa para o resultado final ser falso.

Eu conheço genericamente as regras que apresentastes, o que não quer dizer que a tua explicação não seja em certa medida necessária, a qual agradeço: obrigado.

No entanto neste caso vamos ver: no programa se entrares com uma stream de caracteres pelo teclado, por exemplo: abc[ ]abc[ ] [enter](abc, espaço,abc,espaço,enter),

temos os seguintes comportamentos:

para o programa com o :

if(c!=' ' && c!='\t' && c!='\n')

output:

abc

abc

Porreiro... Já para o programa com os OU "||"  para o mesmo input abc[ ]abc[ ]:

if(c!=' ' || c!='\t' || c!='\n')

temos o output:

abc abc

... que não é o que se pretende!

Quando passo a stream ao :

while((c=getchar())!=EOF)

cada caracter é processado 1 a 1 certo? Então qual a diferença entre usar && ou || se estou a "analisar" dentro do ciclo caracter a caracter e não um conjunto de caracteres?

É esta a minha dúvida! Mas reconheço que pode haver aqui algo que eu não esteja teimosamente a ver, por isso o meu pedido de ajuda.

Posted

Boas acho que já percebi e tem a ver com  a explicação dada pelo PsySc0rpi0n que de tão óbvia que era eu menosprezei confesso.

Eu não quero que apareçam espaços em branco, o objectivo aqui seria converte-los em '\n'. Isto para o objectivo superiror de apresentar uma palavra em cada linha. Com uma palavra a querer dizer um ou mais caracteres... Tenho mesmo que usar o operador condicional && e não ||.

Naquele exemplo do  abc[ ]abc[ ], eu não compreendia porque é que o espaço [ ] não era convertido em '\n' (no bloco else if) quando estava lá expresso no if, que só poderia imprimir qualquer caracter que não fosse '\n','\t' ou ' '. Ora ao chegar ao espaço ' ', o caracter é diferente de '\t' e de '\n', o que é o bastante para a condição ser verdadeira e por isso imprimir o espaço!

Ás vezes desmereço estes pormenores que são o suficiente, por não serem assimilados de me empancar mais tempo do que aquele que previa.

Obrigado PsySc0rpi0n!

Posted

Se eu percebi bem o que disseste no post anterior, acho que falaste em "converter". Mas o 'if' não está a converter espaços em caractéres de mudança de linha. Está apenas a verificar se o caractér presente na variável 'c' naquele momento é diferente de um espaço ' ', de uma tabulação '\t' ou de um caractér de mudança de linha '\n'.

Kurt Cobain - Grunge misses you

Nissan GT-R - beast killer

Posted
47 minutos atrás, PsySc0rpi0n disse:

Se eu percebi bem o que disseste no post anterior, acho que falaste em "converter". Mas o 'if' não está a converter espaços em caractéres de mudança de linha. Está apenas a verificar se o caractér presente na variável 'c' naquele momento é diferente de um espaço ' ', de uma tabulação '\t' ou de um caractér de mudança de linha '\n'.

Não está directamente, mas obriga a entrar no próximo else if que vai fazer isso.

Isto tem a ver com o facto de eu ter feito outra versão deste programa, esta:

#include <stdio.h>

/*write a program that prints its input one word per line*/

int main(void)
{
	int c,spacePrev,charPrev;
	c=spacePrev=charPrev=0;

	while((c=getchar())!=EOF)
	{
		if(c==' '||c=='\t'||c=='\n'&&charPrev==0)
			;
		if(spacePrev==0)
		{
			if(c==' '||c=='\t'&&charPrev==1)
			{
				c='\n';
				spacePrev=1;
				putchar(c);
			}
			else
			{
				putchar(c);
				charPrev=1;
			}
		}
		else if(c==' '||c=='\t'&&spacePrev==1)
			;
		else 
		{
			putchar(c);
			spacePrev=0;
		}
			
	}
return 0;
}

/* NOTA: Não estou nada orgulhoso deste programa, é código a mais para
o problema que é.*/

...que fazia isso mais directamente. No entanto um dos objectivos principáis era sempre esse. Peço desculpa pela imprecisão!

Posted (edited)

Qual é a versão do livro e qual é a página? Ou qual o número do exercício?

Bom, assim sem ver o contexto do problema no livro, deixo aqui uma possível solução já que é mais ou menos como a que tens mas sem o else if e sem a variável state nem os defines.

Não testei o teu código nem sei se faz o mesmo que o meu mas até acho que sim, pelo que não percebo bem qual a necessidade de teres usado a tal variável state e os defines.

#include <stdio.h>

int main(void){
   char c;

   while((c = getchar()) != EOF){
      if(c != ' ' && c != '\t' && c != '\n')
         putchar(c);
      else
         putchar('\n');
   }
   return 0;
}
Edited by PsySc0rpi0n

Kurt Cobain - Grunge misses you

Nissan GT-R - beast killer

Posted (edited)
1 hora atrás, PsySc0rpi0n disse:

Qual é a versão do livro e qual é a página? Ou qual o número do exercício?

Bom, assim sem ver o contexto do problema no livro, deixo aqui uma possível solução já que é mais ou menos como a que tens mas sem o else if e sem a variável state nem os defines.

Não testei o teu código nem sei se faz o mesmo que o meu mas até acho que sim, pelo que não percebo bem qual a necessidade de teres usado a tal variável state e os defines.


#include <stdio.h>

int main(void){
   char c;

   while((c = getchar()) != EOF){
      if(c != ' ' && c != '\t' && c != '\n')
         putchar(c);
      else
         putchar('\n');
   }
   return 0;
}

Essa solução não é estrita ao problema em questão. Entra com espaços primeiro e o que tens?

O problema diz: uma palavra por linha! Se entras com espaços em primeiro, ficas com várias linhas relativas aos espaços entrados. Essas linhas têm que ser "comidas", não processadas, para que seja estritamente como diz o problema: "one word per line". E como é que fazes para não processar esses espaços? Por isso é que eu tive um trabalho dos diabos para chegar á solução, por isso é que foram precisas mais variáveis! Sem falsa modestia, ou falsa-humildade...

Edited by Inacabado
Posted (edited)

Ou seja, se for entrado um espaço antes de qualquer caracter, esse espaço tem que ser "comido"(é uma expressão minha!), não colocado qualquer '\n'.

Se o espaço entrar mas imediatamente a seguir a um caracter, ai sim pode ser substituído por um '\n'!Esta é outra: se forem entrados vários espaços, uns a  seguir aos outros, depois de uma stream de caracteres, tem que se descartar os espaços extra e só substituir o primeiro por '\n'.

Isto tudo para o "one word per line", não "word"+espaços "per line" ou espaços per line e algumas words per line a seguir ou antes...

A mim deu-me luta, este programa!

Edited by Inacabado
Posted

isto é um problema simples que está relacionado com uma coisa chamada FSM (Finite-State-Machine).
Explicar o que são ou como funcinonam, deixo para o Google explicar senão não saia daqui hoje.
No entanto, vou explicar este problema.
Tu tens 3 estados, e vou dar o número 0, 1, 2 a cada uma destes estados:
- 0 : Início
- 1 : A ler espaços
- 2 : A ler uma palavra

Quando a máquina de estados é inicializada, é "setada" a zero (estado inicial).
Neste estado, o processo de leitura de um caracter, pode levar para um dos outros dois estados : 1 ou 2 (ler espaços ou ler uma palavra).
Após o passo inicial, podes estar acontecer estar no estado 1 (a ler espaços) onde ficarás sempre que lês um espaço, passando para o estado 2 (a ler uma palavra) no momento que lês um caracter.
Se estiveres no estado 2 (a ler uma palavra), ficas sempre nesse estado até que lês um caracter que não é alpha-numérico.

gráficamente seria algo deste género:

+----------+                     +----------+
| estado 0 |--(leu um espaço)--->| estado 1 |<--------------------+
+----------+                     +----------+  --(leu um espaço)--+
      |                           |        A
      |                           |        |
      |              (leu um caracter)   (leu um espaço)
      |                           |        |
      |                           V        |
      |                          +----------+
      +-------(leu um caracter)->| estado 2 |<--------------------+
                                 +----------+  -(leu um caracter)-+

A única coisa que não está especificada neste gráfico é a necessidade de fazer o output de nova linha quando mudas do estado 2 (ler palavra) para o estado 1 (ler caracter)conclusão, o código seria algo este género (coded on-the-fly):

#include <stdio.h>
#include <ctype.h>

#define STATE_START  0
#define STATE_SPACE  1
#define STATE_WORD   2

int main(void)
{
    int c, state = STATE_START;
    while((c = getchar()) != EOF)
    {
        switch (state)
        {
            case STATE_START:
            case STATE_SPACE:
                state = isalpha(c) ? STATE_WORD : STATE_SPACE;
                break;
            case STATE_WORD:
                state = isalpha(c) ? putchar(c), STATE_WORD : putchar('\n'), STATE_SPACE;
                break;
        }
    }
    return 0;
}

ps : no final nem foi preciso os && ou os ||, mas se queres perceber como isso funciona na realidade, é só dizer

  • Vote 1
IRC : sim, é algo que ainda existe >> #p@p
Posted

Certo, agora tente ler este pdf até ao capitulo 1.6(exclusive), e tente de novo fazer "on the fly", só com a matéria dada até essa altura! Acredito que consegue...

Obrigado pela ilustração dos estados, não conhecia!

Posted (edited)

Desculpem lá ser picuinhas, confesso que sou um pouco teimoso as vezes, seja bom ou seja mau...

Estive a trabalhar naquela minha primeira versão do problema a que usa as variáveis charPrev e spacePrev, e aprimorei o código de maneira a ficar mais perto da perfeição:

#include <stdio.h>

/*write a program that prints its input one word per line*/

int main(void)
{
	int c,charPrev;
	c=charPrev=0;

	while((c=getchar())!=EOF)
	{
		if(charPrev==0) 		
		{
			if(c!=' '&&c!='\t'&&c!='\n')	
			{
				putchar(c);
				charPrev=1;
			}
		}
		else 
		{
			if(c!=' '&&c!='\t'&&c!='\n')	
				putchar(c);
			else				
			{
				putchar('\n');
				charPrev=0;	
			}
		}
	}
return 0;
}

...muito menos linhas de código, menos variáveis, muito mais económico que a primeira versão, desta versão! Tenho agora duas versões para o mesmo problema:

"write a program that prints its input one word per line".

Abraço a todos

Edited by Inacabado
Posted

estou triste ... qual o problema da minha solução ?

#include <stdio.h>
#include <ctype.h>

#define STATE_START  0
#define STATE_SPACE  1
#define STATE_WORD   2

int main(void)
{
    int c, state = STATE_START;
    while((c = getchar()) != EOF)
    {
        switch (state)
        {
            case STATE_START:
            case STATE_SPACE:
                state = isalpha(c) ? (putchar(c), STATE_WORD) : STATE_SPACE;
                break;
            case STATE_WORD:
                state = isalpha(c) ? (putchar(c), STATE_WORD) : (putchar('\n'), STATE_SPACE);
                break;
        }
    }
    return 0;
}
IRC : sim, é algo que ainda existe >> #p@p
Posted
4 horas atrás, HappyHippyHippo disse:

estou triste ... qual o problema da minha solução ?

vc está a ser sarcástico...hum?

O que eu quis dizer foi, a ser rigoroso, não poderemos usar os elementos de programação que ainda não foram dados no primeiro capitulo deste

livro! Por exemplo o switch não foi dado até aquele capitulo (capitulo 1), nem o operador ternário...

Obviamente que fora disso a sua solução é no mínimo bastante elegante. Além do mais, para o seu nível de conhecimento, não estou apto a fazer-lhe reparos, obviamente...

Abraço

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
×
×
  • Create New...

Important Information

By using this site you accept our Terms of Use and Privacy Policy. We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.