quinta-feira, 20 de fevereiro de 2014

7 - Declarando Funções e Comandos

Olá pessoal, depois de um bom tempo sem postar aqui no blog, resolvi retornar os posts sobre AutoLISP. Continuarei os conceitos básicos sobre AutoLISP, para depois entrar em temas mais avançados. Neste post irei explicar como se declara funções e comandos em AutoLISP. 


- Declarando Funções

Dentro de uma função, podemos ter uma sequencia de comandos e inclusive podemos chamar outras funções declaradas pelo programador. Quando se chama uma função, podemos querer passar para ela, determinados valores, isto pode ser feito facilmente por parâmetros, que são variáveis que servem de ligação entre as funções.
Abaixo é mostrado a sintaxe de como declarar uma função:

(defun NOME_FUNÇÃO (PARÂMETROS / VARIÁVEIS_LOCAIS)
           LISTA_DE_COMANDOS_DA_FUNÇÃO
)

, onde NOME_FUNÇÃO é um nome para a função declarada, sendo que este nome deve ser único, ou seja, não podemos ter outras funções com o mesmo nome, pois neste caso, apenas a última função carregada que será a válida. E além disso, não podemos utilizar palavras que já são nomes de comandos no AutoCAD, como line, circle, insert, etc; pois, senão as funções declaradas pelo programador não serão chamadas.
Não se deve também, utilizar palavras chaves da linguagem, como car, while, defun, if, etc;  
Basicamente, qualquer letra, número ou caractere pode ser utilizado como nome da função, sendo que não existe distinção entre maiúsculas ou minúsculas, então os seguintes nomes: funcao1, FUNCAO1, Funcao1, FuNcAo1, são consideradas a mesma coisa.
Como forma de boa programação, recomenda-se utilizar nomes chaves que resumem o que a função faz, para que toda vez que o programador ver o nome, ele já saiba do que se trata a função.

PARÂMETROS são as variáveis passadas para a função, podemos passar qualquer quantidade necessária de parâmetros, sendo que cada parâmetro pode ser um átomo, uma lista ou até mesmo uma função declarada. 
Assim, podemos passar por exemplo, os seguintes parâmetros: um número inteiro, uma string e outro com uma lista, sendo que esta ordem e a quantidade de parâmetros, deve ser respeitada na chamada da função, sendo de total responsabilidade do programador. Se o número de parâmetros passados for incorreto, ocorrerá um erro em tempo de execução do programa, porém se o tipo de parâmetro for incorreto, este pode causar diversos tipos de erros ou dependendo do código, não causar nenhum erro!!!
Para quem conhece outras linguagens de programação, deve estar perguntando se o parâmetro é passado por valor ou referência, a resposta é que só se pode passar parâmetros por valor em lisp, ou seja, se a variável passada tiver o valor modificado dentro da função que foi chamada, esta mudança de valor, não irá ser "sentida" em nenhuma variável da função que fez a chamada. No final deste post, veremos mais sobre isto.

VARIÁVEIS_LOCAIS são as variáveis que terão seu valor válido apenas dentro da função e também para todas as funções que ela chamar. Para se ter uma ideia, toda variável declarada em AutoLISP é considerada Global, ou seja, qualquer função pode ter acesso a este valor, indo mais além, uma variável global declarada no AutoCAD, só deixará de existir na memória, apenas quando o desenho corrente do AutoCAD for fechado. Para evitar que este valor fique na memória, devemos adicionar o nome da variável no campo das VARIÁVEIS_LOCAIS. Toda variável local, é inicializada com o valor nil.

LISTA_DE_COMANDOS_DA_FUNÇÃO é o corpo do programa, onde irá conter toda a lógica e o fluxo da função, como por exemplo contas aritméticas, avaliações booleanas, a iteração com o AutoCAD, chamadas para outras funções, etc.

Exemplos de declaração de função:

- Exemplo1: A função abaixo tem uma variável local chamada var, que recebe o valor 0:
(defun MinhaFuncao ( / var)
(setq var 0)
)

- Exemplo2: A função abaixo recebe dois parâmetros e faz uma somatória dos valores passados, guardando o resultado numa variável local:
(defun somaParametros (valor1 valor2 / resultado)
    (setq resultado (+ valor1 valor2 ))
)

- Exemplo3: A função abaixo,  não faz nada:
(defun funcaoNil ( / )
     nil
)



- Toda função declarada retorna um valor


Isto é uma regra geral quando se declara funções em AutoLISP, na verdade tudo que é avaliado em lisp deve retornar um valor, sendo assim, as funções também estão nesta regra. O valor retornado pela função sempre será a última avaliação feita de dentro da função, nos exemplos anteriores, a função do exemplo1, "MinhaFuncao" vai retornar o valor 0. A função do exemplo2, "somaParametros" vai retornar o valor setado na variável "resultado", que será a soma dos dois parâmetros. E a função do exemplo3, "funcaoNil" retorna sempre o valor do átomo nil.


- Chamando funções declaradas


Para chamar uma função que foi declarada é muito simples, basta escrever o nome da função dentro de parênteses, se a função tiver parâmetros, deve-se escrever logo após o nome da função os valores ou nomes das variáveis que serão passados o valor. Como exemplo, vamos voltar no caso dos três exemplos declarados anteriormente:
- O exemplo1, a chamada da função será:
(MinhaFuncao)  

- O exemplo2, na chamada da função devemos passar dois parâmetros numéricos:
(somaParametros 1 2)        ; obs: esta função retorna o valor 3.


Se tivermos duas variáveis com números, podemos fazer assim:
(setq numero1 10)
(setq numero2 15)
(somaParametros numero1 numero2)        ; obs: esta função retorna o valor 25.

- O exemplo3, a chamado da função será:
(funcaoNil)

OBS: podemos combinar o resultado gerado por uma função, setando o diretamente numa variável, como mostrado abaixo, utilizando a função do exemplo2: 
(setq valor (somaParametros 2 3))   ; assim a variável "valor", receberá o valor de 5.



- Declarando comandos para serem executados no AutoCAD

Para declarar um comando no AutoCAD, devemos declarar uma função como explicado anteriormente, porém com uma diferença na hora de declarar o NOME_FUNÇÃO. O nome da função deve conter o escrito "c:" (a letra c seguida de dois pontos, sem as aspas), antes do nome da função.
Exemplo:
- Declarando um comando com o nome: "MeuComando":
(defun c:MeuComando ( / )
    ....
)

- Chamando o comando (não precisa de parênteses):
MeuComando

obs: deve-se evitar utilizar parâmetros quando se cria comandos, pois senão a chamada do comando deverá ter o uso de parênteses, como mostrado no exemplo abaixo:
- Declarando comando chamado "MeuComando2":
(defun c:MeuComando2 (parametro1 / )
    ....
)

- Chamando o comando (precisa de parênteses):
(c:MeuComando2 10)



- Variável Global/Local, e parâmetros passados por valor


A resposta desta questões, depende basicamente se a variável for declarada localmente ou não. 
Abaixo veremos alguns casos:

- Caso1, variável local que se torna global para as funções chamadas (situação não recomendada):
 Digamos que temos duas funções chamadas Pai e Filho, e a função Pai tem um parâmetro local chamado "idade", e a função Filho utiliza também uma variável chamada "idade", mas que não foi declarada localmente, e nem passada como parâmetro, abaixo podemos ver as declarações das funções:

(defun c:Pai ( / idade)
  
  (setq idade 40)
  (alert (itoa idade))
  (Filho)
  (alert (itoa idade))
)

(defun Filho( / )
  (setq idade 15)
)

obs: no comando (alert (itoa idade)) , a chamada itoa converte o número para string, e alert exibe uma janela no AutoCAD com o valor da string, estas funções serão detalhadas melhores em futuros posts.

Valores exibidos após a chamada da função Pai:
- 40
- 15

Pelo resultado acima, podemos ver, que a variável idade é considerada global para a função Filho, pois esta pode modificar o seu valor, assim, quando se declara uma variável como local numa função, todas as funções chamadas após, poderão ter acesso de leitura e escrita na variável.


- Caso2, variável local que é local apenas na própria função:
Voltando nas duas funções declaradas anteriormente, para que a variável idade não se torne global para as funções chamadas, devemos em cada função chamada declarar como local a variável. Abaixo, um exemplo mostrando este caso:

(defun c:Pai ( / idade)
  
  (setq idade 40)
  (alert (itoa idade))
  (Filho)
  (alert (itoa idade))
)

(defun Filho( / idade)
  (setq idade 15)
)

Valores exibidos após a chamada da função Pai:
- 40
- 40

Assim, nesta situação temos como se tivéssemos duas variáveis diferentes nas duas funções, e qualquer valor declarado na variável idade da função Filho, não influencia na variável da função Pai.


- Caso3, passando parâmetros por valor:
Neste caso, criamos um parâmetro na função Filho com o mesmo nome da variável idade, e na chamada da função Filho, passamos justamente a variável idade. Foi colocado para se exibir o valor de idade na função filho assim como na função Pai, como pode ser visto nas funções abaixo:

(defun c:Pai ( / idade)
  
  (setq idade 40)
  (alert (itoa idade))
  (Filho idade)
  (alert (itoa idade))
)

(defun Filho(idade / )

        (alert (itoa idade))
        (setq idade 15)
        (alert (itoa idade))
)

Valores exibidos após a chamada da função Pai:
- 40
- 40
- 15
- 40

O que podemos notar neste caso, é que sempre que passamos um parâmetro, este será por valor, mesmo se o parâmetro tiver o mesmo nome usado na função Pai. O valor chegou na função filho com o valor de 40, este modificou o valor para 15, mas quando voltamos pra função Pai, o valor não foi modificado, continuando com o valor 40. 



O que podemos verificar com este post, é que em lisp, tudo é uma função, exceto os átomos (nil e T), assim temos como exemplo as funções: list, if, setq, +, =, >, etc.
No próximo post, eu irei falar sobre as Funções de conversão, sendo que uma delas foi mostrada neste post, a função itoa, que converte um inteiro numa string, farei uma explicação dela e das demais funções.

Nenhum comentário:

Postar um comentário