Resolução de nomes (linguagens de programação) - Name resolution (programming languages)

Em linguagens de programação , a resolução de nomes é a resolução dos tokens dentro das expressões do programa para os componentes pretendidos do programa.

Visão geral

Expressões em programas de computador fazem referência a variáveis, tipos de dados, funções, classes, objetos, bibliotecas, pacotes e outras entidades por nome. Nesse contexto, a resolução de nomes refere-se à associação desses nomes não necessariamente exclusivos com as entidades pretendidas do programa. Os algoritmos que determinam a que esses identificadores se referem em contextos específicos fazem parte da definição da linguagem.

A complexidade desses algoritmos é influenciada pela sofisticação da linguagem. Por exemplo, a resolução de nomes em linguagem assembly geralmente envolve apenas uma única consulta de tabela simples , enquanto a resolução de nomes em C ++ é extremamente complicada, pois envolve:

  • namespaces , que possibilitam que um identificador tenha significados diferentes dependendo de seu namespace associado;
  • escopos , que possibilitam que um identificador tenha diferentes significados em diferentes níveis de escopo e que envolvem várias regras de anulação e ocultação de escopo. No nível mais básico, a resolução de nomes geralmente tenta encontrar a ligação no menor escopo envolvente, de modo que, por exemplo, as variáveis ​​locais substituam as variáveis ​​globais; isso é chamado de sombreamento .
  • regras de visibilidade , que determinam se os identificadores de namespaces ou escopos específicos são visíveis no contexto atual;
  • sobrecarga , que possibilita que um identificador tenha diferentes significados dependendo de como é usado, mesmo em um único namespace ou escopo;
  • acessibilidade , que determina se os identificadores de um escopo visível de outra forma estão realmente acessíveis e participam do processo de resolução de nomes.

Estático versus dinâmico

Em linguagens de programação , a resolução de nomes pode ser realizada em tempo de compilação ou em tempo de execução . O primeiro é chamado de resolução de nomes estática , o último é chamado de resolução de nomes dinâmica .

Um equívoco um tanto comum é que a tipificação dinâmica implica resolução dinâmica de nomes. Por exemplo, Erlang é digitado dinamicamente, mas tem resolução de nome estática. No entanto, a digitação estática implica em resolução de nome estática.

A resolução de nomes estáticos captura, em tempo de compilação, o uso de variáveis ​​que não estão no escopo; evitando erros do programador. Linguagens com resolução dinâmica de osciloscópio sacrificam essa segurança por mais flexibilidade; eles normalmente podem definir e obter variáveis ​​no mesmo escopo em tempo de execução.

Por exemplo, no REPL interativo do Python :

>>> number = 99
>>> first_noun = "problems"
>>> second_noun = "hound"
>>> # Which variables to use are decided at runtime
>>> print(f"I got {number} {first_noun} but a {second_noun} ain't one.")
I got 99 problems but a hound ain't one.

No entanto, confiar na resolução de nomes dinâmica no código é desencorajado pela comunidade Python. O recurso também pode ser removido em uma versão posterior do Python.

Exemplos de linguagens que usam resolução de nome estática incluem C , C ++ , E , Erlang , Haskell , Java , Pascal , Scheme e Smalltalk . Exemplos de linguagens que usam resolução de nomes dinâmica incluem alguns dialetos Lisp , Perl , PHP , Python , REBOL e Tcl .

Mascaramento de nome

O mascaramento ocorre quando o mesmo identificador é usado para diferentes entidades em escopos lexicais sobrepostos. No nível de variáveis ​​(ao invés de nomes), isso é conhecido como sombreamento de variável . Um identificador I '(para a variável X') mascara um identificador I (para a variável X) quando duas condições são atendidas

  1. I 'tem o mesmo nome que eu
  2. I 'é definido em um escopo que é um subconjunto do escopo de I

Diz-se que a variável externa X é sombreada pela variável interna X '.

Por exemplo, o parâmetro "foo" sombreia a variável local "foo" neste padrão comum:

private int foo;  // Name "foo" is declared in the outer scope

public void setFoo(int foo) {  // Name "foo" is declared in the inner scope, and is function-local.
    this.foo = foo;  // Since "foo" will be first found (and resolved) in the ''innermost'' scope,
                     // in order to successfully overwrite the stored value of the attribute "foo"
                     // with the new value of the incoming parameter "foo", a distinction is made
                     // between "this.foo" (the object attribute) and "foo" (the function parameter). 
}

public int getFoo() {
    return foo;
}

O mascaramento de nomes pode causar complicações na sobrecarga de função , devido ao fato de a sobrecarga não acontecer entre os escopos em algumas linguagens, notadamente C ++, exigindo, assim, que todas as funções sobrecarregadas sejam declaradas novamente ou importadas explicitamente para um determinado namespace.

Renomeação de alfa para tornar a resolução de nomes trivial

Em linguagens de programação com escopo léxico que não reflete sobre nomes de variáveis, α-conversão (ou α-renomeação) pode ser usada para tornar a resolução de nome fácil, encontrando uma substituição que garante que nenhum nome de variável mascare outro nome em um escopo contido. A renomeação alfa pode tornar a análise de código estático mais fácil, pois apenas o renomeador alfa precisa entender as regras de escopo da linguagem.

Por exemplo, neste código:

class Point {
private:
  double x, y;

public:
  Point(double x, double y) {  // x and y declared here mask the privates
    setX(x);
    setY(y);
  }

  void setX(double newx) { x = newx; }
  void setY(double newy) { y = newy; }
}

dentro do Ponto construtor, as variáveis de classe x e y são sombreadas por variáveis locais com o mesmo nome. Isso pode ser renomeado para alfa:

class Point {
private:
  double x, y;

public:
  Point(double a, double b) {
    setX(a);
    setY(b);
  }

  void setX(double newx) { x = newx; }
  void setY(double newy) { y = newy; }
}

Na nova versão, não há mascaramento, então é imediatamente óbvio quais usos correspondem a quais declarações.

Veja também

Referências