Jump to content
thoga31

Conversa acerca de assuntos diversos - variáveis locais vs globais, units, etc

Recommended Posts

thoga31

Uma falha gravíssima no ensino de programação, e neste caso em especial da linguagem Pascal, é a distinção entre variáveis globais e variáveis locais aquando do ensino de variáveis e constantes.

Ontem eu e o @pwseo falámos sobre o assunto no IRC, e concordo com uma coisa: neste aspecto, Pascal está atrás do C. Em C é facílimo distinguir estes conceitos, e em Pascal é facílimo fazer trampa da grossa.

O bloco principal do programa deverá ser o único a aceder às variáveis globais, isso sim é uma boa prática. Se necessitarmos que um procedimento ou uma função mude uma variável global, nunca lhe acedemos directamente - passamo-la por referência num argumento. É para isto que há a passagem por referência, não foi apenas um delírio do Sr. Niklaus Wirth que se lembrou que seria fixe haver uma coisa destas e com este nome pomposo. A utilidade é séria.

Se FimJogo precisa de alterar a variável global turno, passamo-la por referência assim:

procedure FimJogo(var t : integer);
begin
   // ...
   t := 0;
end;

(* Onde se chamar o procedimento... *)
FimJogo(turno);
if turno = 0 then // whatever

Em C o bloco principal é uma função em si mesma, pelo que nem sequer há este conceito tão perigoso de variáveis globais - elas existem, mas não geram tanta "vontade" de as usar como em Pascal, em parte pela mera existência do Main. Isto seria simulado em Pascal assim:

// não há variáveis declaradas fora de nenhum procedimento, vamos evitar isso

procedure Main;
// aqui ficam aquelas que seriam as ditas "variáveis globais".
// mas não podem ser acedidas por mais ninguém - terão de ser passadas por referência
begin
   // aqui fica o que seria o corpo principal do programa.
end;

begin (* Bloco principal *)
   Main;
end.

Enquanto estes conceitos e a importância avassaladora que têm não forem compreendidos, então as probabilidade de haver erros à conta de variáveis globais continuará a ser muito alta. Aliás, qualquer probabilidade, por mais baixa que seja, em estragar um programa à conta disto é alarmante.

Edited by thoga31

Knowledge is free!

Share this post


Link to post
Share on other sites
nunopicado

Já eu não sou contra o facto de ser possível usar (assim como não sou contra existir o GoTo, ainda que ache uma abominação usar).

Continuo com a ideia que o problema está nos professores que se põem a ensinar programação sem ter um pingo de pensamento lógico.

Conheço professores de C que ensinam de imediato as funções, sendo que a função main é logo indicada como uma simples forma de chamar as demais.

Em Pascal, com o raio da ideia de que é fraco e é só para ensinar aos iniciantes, empurram o verdadeiro ensinamento para a frente, e dão só o básico, de forma tão atabalhoada que só criam maus vicios de programação, e má ideia sobre o que é o Pascal.

Mas daqui a nada vamos noutro offtopic do tamanho do mundo, para dizer o que já temos dito em tantos outros! Não vale a pena.

Cabe aos alunos que realmente se interessem por programação seleccionar a informação que recebem, avaliar a qualidade desta, e procurar evoluir por sua própria conta - se eu me tinha limitado ao que me ensinaram na escolha, ainda hoje andava a calcular áreas de figuras geométricas.


"A humanidade está a perder os seus génios... Aristóteles morreu, Newton já lá está, Einstein finou-se, e eu hoje não me estou a sentir bem!"

> Não esclareço dúvidas por PM: Indica a tua dúvida no quadro correcto do forum.

Share this post


Link to post
Share on other sites
pwseo

Eu também não sou contra o uso de variáveis globais (são muitas das vezes a solução mais elegante e eficiente para certos problemas). Já agora, também defendo que o goto e os seus primos disfarçados break e continue são bastante úteis em determinadas situações (sendo muito mais legíveis que mais um ou dois ifs).

A pré-condição para fazer uso destas coisas é, obviamente, saber o que se está a fazer (e é aqui que reside o problema).

O problema que discuti com o thoga31 foi o facto de que em Pascal há um bloco de código implícito e sem nome (o par begin .. end.). Como a maioria dos iniciados começa por gigantescos programas monolíticos contidos nesse bloco, todas as variáveis que usam são obrigatoriamente globais, e quando começam a abstrair a funcionalidade para dentro de procedimentos e funções, tudo o que é global continua a funcionar e não precisa de ser alterado.

Em C isto não é tão imediato porque as variáveis são declaradas dentro do bloco de código das funções e além disso o código principal não está num bloco sem nome mas sim numa função como qualquer outra, a função main(). Por este motivo, quando o principiante começa a modularizar o seu programa, o código não funciona porque as novas funções não conseguem usar as variáveis declaradas na main; assim, os programadores são forçados a decidir se querem usar variáveis globais ou passar as variáveis por referência. Claro que 95% escolhe as variáveis globais (mas isso é outra questão, e é aqui que entram os professores), mas o facto de em C sermos obrigados a tomar uma decisão consciente sobre tal coisa é muito bom (ainda que isso não tenha sido intencional).

Quanto ao offtopic, por vezes é saudável e importante :)

Share this post


Link to post
Share on other sites
nunopicado

Quanto ao offtopic, por vezes é saudável e importante :)

Oh, bem... Perdido por 100... lol

Eu também não sou contra o uso de variáveis globais (são muitas das vezes a solução mais elegante e eficiente para certos problemas). Já agora, também defendo que o goto e os seus primos disfarçados break e continue são bastante úteis em determinadas situações (sendo muito mais legíveis que mais um ou dois ifs).

A pré-condição para fazer uso destas coisas é, obviamente, saber o que se está a fazer (e é aqui que reside o problema).

É legítimo dizer que concordo a 110%?

O problema que discuti com o thoga31 foi o facto de que em Pascal há um bloco de código implícito e sem nome (o par begin .. end.). Como a maioria dos iniciados começa por gigantescos programas monolíticos contidos nesse bloco, todas as variáveis que usam são obrigatoriamente globais, e quando começam a abstrair a funcionalidade para dentro de procedimentos e funções, tudo o que é global continua a funcionar e não precisa de ser alterado.

Em C isto não é tão imediato porque as variáveis são declaradas dentro do bloco de código das funções e além disso o código principal não está num bloco sem nome mas sim numa função como qualquer outra, a função main(). Por este motivo, quando o principiante começa a modularizar o seu programa, o código não funciona porque as novas funções não conseguem usar as variáveis declaradas na main; assim, os programadores são forçados a decidir se querem usar variáveis globais ou passar as variáveis por referência. Claro que 95% escolhe as variáveis globais (mas isso é outra questão, e é aqui que entram os professores), mas o facto de em C sermos obrigados a tomar uma decisão consciente sobre tal coisa é muito bom (ainda que isso não tenha sido intencional).

Sem dúvida que em C há "mais um entrave" a usar globais por quem não sabe o que está a fazer, num lembrete que fulano deve abrir a pestana antes de mexer.

Mas lá está, se fossem correctamente ensinados (e por seu lado, quisessem aprender também) essa decisão consciente seria tomada logo à partida.

Logo desde que comecei a trabalhar com procedimentos e funções (e antes de mas terem ensinado nas escola) que evito ao máximo variáveis globais, ainda que não me detenha se achar que realmente é a melhor solução.

Ex.: Uma variável de arquivo, que vai ser usada durante todo o programa --> Global

Uma variável de um array, onde se guardam dados durante o programa --> Global

Uma variável de controlo de um ciclo --> Decididamente local (com a menção honrosa para quem espetar uma agulha debaixo das unhas do gajo que use uma variável global para este efeito).


"A humanidade está a perder os seus génios... Aristóteles morreu, Newton já lá está, Einstein finou-se, e eu hoje não me estou a sentir bem!"

> Não esclareço dúvidas por PM: Indica a tua dúvida no quadro correcto do forum.

Share this post


Link to post
Share on other sites
thoga31

Claro que há excepções, mas regras geral a regra de ouro é não aceder às variáveis globais dentro de procedimentos e funções. Regra geral isto torna-se ainda mais verdade quando pretendemos criar procedimentos e funções que possam ser reutilizados noutros programas - aqui já não quero entrar nas unit's, que é um assunto "avançado" para principiantes, apesar da sua simplicidade.

A meu ver os professores deveriam mostrar que a principal intenção dos procedimentos e funções não é apenas modular as coisas: é também conseguir criar trechos de código reutilizável. Portanto, se nos metemos a aceder a variáveis globais como se não houvesse amanhã, amanhã não haverá reutilização dos procedimentos :D

Tudo passa por se saber o que se está a fazer e ter consciência de uma mão cheia de coisas.

Portanto, se estamos a criar um procedimento específico para o programa de modo a não encher o bloco principal com um menir (fazendo minhas as palavras do @pwseo xD), devemos ter a consciência de 1) se devemos aceder às globais, 2) se o fizermos, analisar o que lhe acontece quando sair, e 3) ter consciência que usar não é abusar.

Funções e procedimentos como "FileExists" ou "IsNumeric" que nós podemos fazer nunca, mas nunca, deverão aceder a variáveis globais (é óbvio).

Regra de Ouro da Programação: entre usar e abusar, a diferença está muito além de duas letras. ;)

Entendem agora onde queria chegar? Hoje estou num dia em que acabo por deixar o raciocínio a metade... :P


Knowledge is free!

Share this post


Link to post
Share on other sites
nunopicado

Entendem agora onde queria chegar? Hoje estou num dia em que acabo por deixar o raciocínio a metade... :P

Estou a ver que sim...

Então, para mudar isso, e usando um pouco de outra linguagem numa metáfora programável:

SELECT EXPLAIN 'Funções e procedimentos como "FileExists" ou "IsNumeric" que nós podemos fazer nunca, mas nunca, deverão aceder a variáveis globais (é óbvio).'

Edited by nunopicado

"A humanidade está a perder os seus génios... Aristóteles morreu, Newton já lá está, Einstein finou-se, e eu hoje não me estou a sentir bem!"

> Não esclareço dúvidas por PM: Indica a tua dúvida no quadro correcto do forum.

Share this post


Link to post
Share on other sites
thoga31

Queres que explique essa frase, é? Pensei que fosse óbvia...


Knowledge is free!

Share this post


Link to post
Share on other sites
thoga31

Por acaso não... :)

Porquê que é "Nunca, mas nunca" se podem usar variáveis globais com funções que não alteram os seus parâmetros?

Ou estás a referir-te a usar para guardar o resultado da função?

Isto é correcto? Já vi este género de pérolas, meu filho...

var s : string;

  function IsNumeric : boolean;
  var r : real;
      e : word;
  begin
      val(s, r, e);
      IsNumeric := e = 0;
  end;

Edited by thoga31

Knowledge is free!

Share this post


Link to post
Share on other sites
Rui Carlos

Eu também não sou contra o uso de variáveis globais (são muitas das vezes a solução mais elegante e eficiente para certos problemas). Já agora, também defendo que o goto e os seus primos disfarçados break e continue são bastante úteis em determinadas situações (sendo muito mais legíveis que mais um ou dois ifs).

A pré-condição para fazer uso destas coisas é, obviamente, saber o que se está a fazer (e é aqui que reside o problema).

Um dos problemas de usar goto, break e continue (ou até o return), é que te impedem de ler o código localmente. De certa forma, vejo isto de uma forma similar ao encapsulamento.

Se tu tens um ciclo sem esse tipo de instruções, olhas para a instrução do ciclo e sabes de imediato a condição de paragem. Mas se essas instruções forem usadas, então vais ter que ler o código do ciclo todo para saberes qual é a condição de paragem.

No caso das variáveis globais, é a diferença entre olhares apenas para a assinatura da função, ou teres que olhar para o código todo da função.

Em códigos pequenos, isto tem um impacto pouco significativo. Ainda assim, numa linguagem minimamente bem desenhada, acho que em 99.(9)% pode ser evitado. (Mas também admito que por vezes não cumpro totalmente as regras :D .)

Bem, nada como o Haskell para nos ajudar a evitar estes maus hábitos todos :D (Que ainda nos dá o extra de obrigar à identificação de funções com side-effects.)

Share this post


Link to post
Share on other sites
nunopicado

Isto é correcto? Já vi este género de pérolas, meu filho...

AHHHHHH. Eh pá, FileExists e IsNumeric eu não tenho de as fazer, já cá estão!

Logo, nem pensei nessa hipotese, de seres tu a faze-las! ;)

Nessa caso, é que NUNCA MAS NUNCA, MAS MESMO NUNCA!

Está ai o arquivo em pascal...

Postando aqui no forum a indentação não está pegando :(

dropbox: https://www.dropbox.com/s/d7clmswpwvw98qk/Remake.pas

Ideone: http://ideone.com/xcRmnX

Estava aqui a ver o teu código...

A indentação melhorou, mas ainda pode melhor um bocado.

Já agora, para usares indentação correctamente no forum, usa o editor simples (ícone superior esquerdo da tua janela de edição).

Olha aqui:

If turno = 1 then
  begin
     {Procedimentos e funções do jogador 1}
     ConverterJogada;
     RealizaJogadas;
  end
else
  If turno = 2 then
     begin
        {Procedimentos e funções do jogador 2}
        ConverterJogada;
        RealizaJogadas;
     end;      

Qual é a diferença, para este trecho do código, entre o Turno ser 1 ou 2?

E já agora este trecho:

procedure ConverterJogada;// Começa procedimento para ler a jogada do jogador e traduzi-la de ASCII para inteiro.
Begin
  if ParamCount = 0 then//Em caso do modo online.
     writeln('Entre com a sua jogada. Jogador ',turno);

  if ParamCount = 0 then//Em caso do modo online.
     begin
        readln(jg);
     end; 

Porquê dois IF's, seguidos, com a mesma condição?

Edited by nunopicado

"A humanidade está a perder os seus génios... Aristóteles morreu, Newton já lá está, Einstein finou-se, e eu hoje não me estou a sentir bem!"

> Não esclareço dúvidas por PM: Indica a tua dúvida no quadro correcto do forum.

Share this post


Link to post
Share on other sites
thoga31

AHHHHHH. Eh pá, FileExists e IsNumeric eu não tenho de as fazer, já cá estão!

Logo, nem pensei nessa hipotese, de seres tu a faze-las! ;)

Nessa caso, é que NUNCA MAS NUNCA, MAS MESMO NUNCA!

Um novato não sabe que existem, pelo que as tenta fazer. E depois dá origem àquelas pérolas graças aos ensinamentos de qualidade recebidos nas aulas. Agora imagina teres 5 strings - metem-se a fazer IsNumeric1, IsNumeric2, etc, para as 5 strings. Não estou a mentir, é a infeliz da verdade.


Knowledge is free!

Share this post


Link to post
Share on other sites
passarito

Boas

tendo acompanhado este tópico um pouco tarde, só hoje fui ver a página 2 e reparei em alguns pontos interessantes que vou comentar havendo a possibilidade de estar em off-topic e achar que este assunto até se poderia dar origem a um tópico especifico. As variveis locais e globais!

Se em vez de entermos as variaveis como locais e globais as entendermos como associadas a um determinado bloco a coisa fica muito mais simples senão vejamos:

Pegando num caso simples, um procedimento

procedure aaa
var i:interger;
begin
 ...
end;

Isto será uma variavel local, mas se alterarmos para isto

procedure aaa;
var i:interger;

 procedure bbb;
 var k:integer;
 begin
..
 end;

begin {bloco principal do procedimento aaa}
 ...
end;

A variavel i afinal é local ou global? Será local relativamente ao programa geral e global referente a todos os procedimentos, funções e bloco principal do procedimento aaa.

Estão a ver a semelhança com um programa? A variavel é global mas dentro do bloco do programa que afecta as funcões, procedimentos e bloco principal do programa, mas acima destes ainda à as Units! Programação avançada? nah esqueçam esse estereotipo, é exactamente a mesma coisa que em vez de se fazer um programa todo no bloco principal usar funções e procedimentos!

..é também conseguir criar trechos de código reutilizável. Portanto, se nos metemos a aceder a variáveis globais como se não houvesse amanhã, amanhã não haverá reutilização dos procedimentos :D

Não concordo! Se é código para ser reutilizável colocamo-lo numa unit e aí esquece as variaveis globais, pois não as declaras no programa principal! Mais à frente explico. Se for para fazer um copy/paste para um novo programa, então terão que reajustar à nova realidade do novo programa, além de estarem a trabalhar mal tão mal como fazer um programa sem procedimentos nem funções.

Funções e procedimentos como "FileExists" ou "IsNumeric" que nós podemos fazer nunca, mas nunca, deverão aceder a variáveis globais (é óbvio).

Estes são os perfeitos exemplos de uma má programação, isto nunca deve de estar no programa mas sim numa unit e como disse, já não se tem o problema das variaveis!

Então vamos ver um programa e uma unit e fazer as diversas variações das variaveis!

Program P1;

Uses Test_Unit;

Var i:integer;

begin
 i:=5;
 writeln('i');
 Test_var;
 writeln(i);
end;

Código para a Unit

Unit Test_Unit;

Interface
 Procedure Test_Var;
Implementation

Procedure Test_Var;
begin
 i:=10;
 writeln(i);
end;

Begin
End.

A compilação vai dar erro! Porquê? Porque a variavel i não é global à totalidade do programa, apenas é global ao bloco de procedimentos, funções e bloco principal dentro de P1. Para funcionar teria de se declarar a variavel i dentro da unit Test_Unit.

Aqui, sim, há umas variações e vou indicá-las com um exemplo.

Unit Test_Unit;

Interface
 Var i1:integer; {varivel global relativamente ao programa, própria unit e a todas as que forem carregadas após a própria}
 Procedure Test_Var;
Implementation

Var i2:integer ;{varivel global unicamente à própria unit}

Procedure Test_Var;
Var i3:integer; {varivel local}
begin
 ..
end;

Begin
 {Se quizerem correr alguma coisa antes bo bloco principal do programa é aqui}
 {por exemplo uma introdução ao programa}
End.

A minha capacidade de explanação não é tão boa como a vossa, portanto se algo não tiver sido esclarecedor, façam favor de se chegarem à frente.

Edited by passarito
  • Vote 1

Share this post


Link to post
Share on other sites
nunopicado

A minha capacidade de explanação não é tão boa como a vossa, portanto se algo não tiver sido esclarecedor, façam favor de se chegarem à frente.

No que está bem, não se mexe!


"A humanidade está a perder os seus génios... Aristóteles morreu, Newton já lá está, Einstein finou-se, e eu hoje não me estou a sentir bem!"

> Não esclareço dúvidas por PM: Indica a tua dúvida no quadro correcto do forum.

Share this post


Link to post
Share on other sites
passarito

Não queria dizer que estava errado, que não está (olha a presunção!), mas isto com uns bonecos quase que duas palavras que ia lá, agora explicar sem bonecos pode-se tornar algo confuso!

Share this post


Link to post
Share on other sites
nunopicado

A minha capacidade de explanação não é tão boa como a vossa, portanto se algo não tiver sido esclarecedor, façam favor de se chegarem à frente.

Não queria dizer que estava errado, que não está (olha a presunção!), mas isto com uns bonecos quase que duas palavras que ia lá, agora explicar sem bonecos pode-se tornar algo confuso!

Eu referia-me à tua explicação... Não podia ser melhor!


"A humanidade está a perder os seus génios... Aristóteles morreu, Newton já lá está, Einstein finou-se, e eu hoje não me estou a sentir bem!"

> Não esclareço dúvidas por PM: Indica a tua dúvida no quadro correcto do forum.

Share this post


Link to post
Share on other sites
passarito

Eu referia-me à tua explicação... Não podia ser melhor!

Really? Então isso de eu achar que me expresso mal é um mito? Oh, Thank you, thank you, thank yuo very much!

Mas acho que o Thoga não vai ficar muito satisfeito, pois dei-lhe ali umas 2 ou 3 machadas nos conceitos dele, mas tinha que ser...

@Thoga, sabes bem que não é nada contra ti, antes pelo contrario, mas mesmo as pessoas entendidas às vezes têm conceitos errados. Quantas vezes já não me puxaste para dentro dos trilhos correctos?

Entretanto dei um vista de olhos pela internet, e é incrivel a quantidade de manuais do Pascal com a estrutura errada! Não é só o facto de eu não concordar, mas na prática leva à falha de entendimento das units!

Isto sim, é puro off-topic

Share this post


Link to post
Share on other sites
nunopicado

Queres que explique essa frase, é? Pensei que fosse óbvia...

Por acaso não... :)

Porquê que é "Nunca, mas nunca" se podem usar variáveis globais com funções que não alteram os seus parâmetros?

Ou estás a referir-te a usar para guardar o resultado da função?

EDIT: Este post foi criado aquando da divisão de tópicos que gerou este para reflectir parte da conversa que aqui era devida, visto que o post original tinha bons motivos para continuar no local de origem.

Deve ser portanto entendido como estando, cronológicamente, entre o #7 e o #8 deste tópico.

Edited by nunopicado

"A humanidade está a perder os seus génios... Aristóteles morreu, Newton já lá está, Einstein finou-se, e eu hoje não me estou a sentir bem!"

> Não esclareço dúvidas por PM: Indica a tua dúvida no quadro correcto do forum.

Share this post


Link to post
Share on other sites
pwseo

Em códigos pequenos, isto tem um impacto pouco significativo. Ainda assim, numa linguagem minimamente bem desenhada, acho que em 99.(9)% pode ser evitado. (Mas também admito que por vezes não cumpro totalmente as regras :D .)

A questão é essa: claro que pode ser evitado, mas isso nem sempre é o mais conveniente ou o mais legível. Quanto a deixarmos de poder ler o código localmente, isso fica nas mãos do programador, como tudo o resto. A possibilidade de usar estes "atalhos" para clarificar o código existe, mas obviamente não quer dizer que isso seja a norma (não é, infelizmente!).

Ainda assim, o caso das variáveis globais parece-me pior porque qualquer função pode alterar o funcionamento de qualquer outra função. Instruções como goto, break e continue são muito mais contidas (assumindo que não usamos o goto para saltar entre funções, claro) e quando usadas em loops pequenos são muito legíveis :)

Mas concordo que tudo isto seja evitável. Eu apenas não quero evitar sempre, porque às vezes fica mais feio :P

Bem, nada como o Haskell para nos ajudar a evitar estes maus hábitos todos :D (Que ainda nos dá o extra de obrigar à identificação de funções com side-effects.)

Pois é :P Variáveis globais em Haskell... pfff xD

Share this post


Link to post
Share on other sites
nunopicado

Pois é :P Variáveis globais em Haskell... pfff xD

Mas o que se passa com as globais em Haskell? lol

Uma coisa que aprendi:

- "Programa sempre como se a pessoa que vai ler o teu código fosse um assassino em série que sabe onde moras!"


"A humanidade está a perder os seus génios... Aristóteles morreu, Newton já lá está, Einstein finou-se, e eu hoje não me estou a sentir bem!"

> Não esclareço dúvidas por PM: Indica a tua dúvida no quadro correcto do forum.

Share this post


Link to post
Share on other sites
passarito

O Pascal é tão à frente que até nos facilita a vida quando queremos programar mal, são casos claros o goto, break, continue, exit, halt e forward que conseguem ir contra as normas de programação da própria linguagem.

Não quer com isto dizer que em casos MUITO pontuais não dêm uma ajudinha e evitem um excesso de código desnecessário, mas daí até a uma má programação é uma linha tão ténue!

Eu próprio em tantos anos de programação usei o exit e o halt apenas umas 2 vezes, o forward 1 vez para experimentar e os outros apenas em exemplos tipo hello world!

Share this post


Link to post
Share on other sites

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.