Pular para o conteúdo principal

Clojure - Símbolos globais e locais

Até agora, sempre que foi necessário manter um valor em memória nós usamos o conceito de "variável", realizando a "definição de um símbolo" com o comando "def". Porém essa abordagem corresponde ao que conhecemos nas linguagens mais tradicionais como "definição de variável global".

Como é de conhecimento, as definições de "variáveis globais" podem trazer uma série de problemas as aplicações, uma vez que é quase impossível ter conhecimento de todas, com isso podemos acabar gerando problemas com a substituição de valores durante seu uso, podendo causar erros em diversos pontos da aplicação e tornando a aplicação instável e de difícil manutenção.

Dito isso, veremos como trabalhar com símbolos dentro de um escopo limitado, seguiremos os próximos exemplos dentro de nossas funções.

Escopo global

Como vimos até aqui, os símbolos de escopo global podem ser acessados de qualquer parte da aplicação, vamos ao exemplo, considerando uma aplicação hipotética de uma escola, temos duas rotinas distintas que mostram a média dos alunos em duas condições distintas:

  • Uma que mostra e média "padrão" acrescida de um ponto extra;
  • Outra que mostra a média do aluno em uma condição de "recuperação", que acresce a média dois pontos extras;

Enquanto a rotina que mostra a média "padrão" olha para um símbolo global para fazer seu cálculo sem fazer qualquer alteração a rotina que mostra a média dos alunos em condição de "recuperação" também utiliza o mesmo símbolo global, porém ela sempre altera seu valor para "dois".

O problema que teremos é o seguinte, enquanto a rotina que mostra as médias de alunos em "recuperação" não for executada a rotina de média "padrão", quando executada, apresentará um resultado correto, porém após primeira execução da rotina de "recuperação" a "padrão" começará a mostrar um resultado errado:

(def ponto-extra 1)

(defn verifica-situacao-aluno-padrao
  "Retorna a media do aluno somada a seu ponto extra"
  [nota1 nota2]
  (+ (/ (+ nota1 nota2) 2) ponto-extra)
)
(defn verifica-situacao-aluno-recuperacao
  "Retorna a media do aluno somada a seu ponto extra (em caso de recuperação)"
  [nota1 nota2]
  (def ponto-extra 2)
  (+ (/ (+ nota1 nota2) 2) ponto-extra)
)

ponto-extra

(verifica-situacao-aluno-padrao 5 5)

(verifica-situacao-aluno-recuperacao 5 5)

(verifica-situacao-aluno-padrao 5 5)

Exemplificando um problema com símbolo global

Sobre a imagem:

  • Na linha três definimos nosso símbolo global "ponto-extra";
  • Da linha cinco até a linha dezesseis definimos nossas duas funções, "padrão" e "recuperação" respectivamente;
  • Na linha dezenove checamos o valor do nosso símbolo global "ponto-extra", que é "1";
  • Na linha vinte e um consultamos a situação de um aluno com a rotina "padrão" e vemos que sua média é "6";
  • Na linha vinte e três consultamos a situação de um aluno com a rotina de "recuperação" e vemos que sua média é "7";
  • Na linha vinte e cinco consultamos a situação de um aluno com a rotina "padrão" novamente e agora vemos um resultado diferente, sua média passou a ser "7", mesmo valor que a rotina de "recuperação";
  • Na linha vinte e sete checamos o valor do nosso símbolo global "ponto-extra" novamente, e conforme esperado ele agora é "2";

Conforme já explicado, a função de "recuperação" altera o valor do símbolo global que é usado por outras funções, essa mudança não era esperada em outras partes da aplicação e isso gerou um problema de divergência de valores, e pior, a momentos onde o problema não ocorre (já que ele só ocorre a partir da primeira execução da rotina de "recuperação") e a rotinas que continuam a funcionar (a rotina "recuperaçãosempre funciona). Acabamos de gerar um bug em nossa aplicação que não será nada fácil de simular/encontrar/corrigir.

Escopo local

Como visto anteriormente, o uso inadequado de um símbolo global gerou um bug em nossa aplicação, para resolver esse problema o desenvolvedor que criou a função de "recuperação" deveria ter usado um símbolo de escopo local na rotina, uma vez que o valor de "ponto-extra" usado ali é uma regra especifica dessa rotina.

Para trabalharmos com o escopo local utilizaremos a função "let", com ele criamos um escopo local com símbolos e funções:

  • O "let" deve ser usado entre parênteses;
  • Ele recebe um vetor de símbolos/valores;
  • Ele recebe uma ou mais funções;
  • Ele retorna o resultado da execução de sua última função;
(let [idade 40] (+ idade 1))

Usando o let pela primeira vez

Na imagem acima:

  • Na linha um criamos nosso escopo local com o "let", nesse escopo temos o símbolo "idade" com o valor "40" dentro do vetor e temos uma função que soma a valor "1" ao nosso símbolo "idade";
  • Na linha dois temos o resultado da função de soma;
  • Na linha três tentamos usar o símbolo "idade" e recebemos um erro na sequencia dizendo que não foi possível resolver o símbolo;

Bom, agora que já sabemos como resolver o problema de escopo vamos aplicar uma correção em nossa função "recuperação", para que ela deixe de usar o símbolo global, primeiro vamos reiniciar o símbolo global para seu valor correto, em seguida vamos corrigir a função e ne sequencia executar os testes:

(def ponto-extra 1)

(defn verifica-situacao-aluno-recuperacao
  "Retorna a media do aluno somada a seu ponto extra (em caso de recuperação)"
  [nota1 nota2]
  (let [ponto-extra-recuperacao 2]
    (+ (/ (+ nota1 nota2) 2) ponto-extra-recuperacao))  
)

(verifica-situacao-aluno-padrao 5 5)

(verifica-situacao-aluno-recuperacao 5 5)

(verifica-situacao-aluno-padrao 5 5)

ponto-extra

Refatorando a função para uso do escopo local

Na imagem acima:
  • Na linha 1 redefinimos nosso símbolo global para seu valor correto;
  • Da linha três até a oito temos a refatoração de nossa função com o uso do "let";
  • Nas linhas dez, doze e catorze repetimos os testes, e agora os seus respectivos resultados estão corretos;

Conclusão

Nesse post entendemos e exemplificamos os problemas causados pelo uso do escopo global, vimos como trabalhar com escopo local com o "let" e refatoramos nossa função problemática para que nossa aplicação voltasse a funcionar da forma esperada.

Saiba mais

Comentários

Mais visitadas

Funções de data Oracle

  Com o Oracle é possível obter uma série de resultados a partir de uma ou mais datas, como por exemplo verificar o último dia do mês ou verificar a quantidade de meses entre duas datas, então vamos a alguns exemplos:   Data atual do sistema: SYSDATE Remover meses de uma data: ADD_MONTHS(SYSDATE, -1) Adicionar meses de uma data: ADD_MONTHS(SYSDATE, +1) Buscar o último dia do mês: LAST_DAY(SYSDATE) Primeiro dia do mês: TRUNC(SYSDATE, ‘MONTH’) Quantidade de meses entre duas datas: MONTHS_BETWEEN(SYSDATE, ‘27/07/1982’) Primeiro dia do ano: TRUNC(SYSDATE, ‘YEAR’) Dias da semana: DECODE( TO_NUMBER( TO_CHAR          (SYSDATE, ‘D’) ) ,1, ‘domingo’ ,2, ‘segunda-feira’ ,3, ‘terça-feira’ ,4, ‘quarta-feira’ ,5, ‘quinta-feira’ ,6, ‘sexta-feira’ ,7,’sábado’ )

Aplicação Flask usando Nginx e Gunicorn

Aplicação Flask usando Nginx e Gunicorn Se você já desenvolveu uma aplicação Flask básica, sabe que o servidor de desenvolvimento embutido não é ideal para produção. Ele não é projetado para lidar com altos volumes de tráfego ou conexões simultâneas. Para tornar sua aplicação Flask mais robusta e pronta para produção, podemos usar o Gunicorn como servidor de aplicação e o Nginx como proxy reverso. Neste artigo, vamos adaptar o exemplo anterior ( Criando uma Aplicação CRUD com Flask, PostgreSQL e Docker ) para incluir o Nginx e o Gunicorn. O que são Nginx e Gunicorn? Gunicorn O Gunicorn (Green Unicorn) é um servidor de aplicação WSGI que roda aplicações Python como o Flask. Ele é eficiente e simples de configurar, lidando com múltiplas requisições ao mesmo tempo, algo que o servidor embutido do Flask não faz bem. Nginx O Nginx é um servidor web que atua como um proxy reverso. Ele recebe requisições HTTP e as encaminha ao Gunicorn. Além disso, o Nginx pode: Servir arquivos ...

Iniciar e Parar Serviços do Windows (Delphi)

Em certas ocasiões nos deparamos com a necessidade de manipular determinadas atividades do SO, como iniciar ou parar um banco de dados, ou qualquer outro serviço que esteja funcionando no momento. Segue abaixo um código que encontrei na Internet para tal finalidade (não me recordo à fonte, assim que eu a encontrar colocarei). Iniciar Serviço: uses WinSvc; // // start service // // return TRUE if successful // // sMachine: //   machine name, ie: \SERVER //   empty = local machine // // sService //   service name, ie: Alerter // function ServiceStart(   sMachine,   sService : string ) : boolean; var   //   // service control   // manager handle   schm,   //   // service handle   schs   : SC_Handle;   //   // service status   ss     : TServiceStatus;   //   // te...