Pregunta ¿Qué es el alcance léxico?


¿Podría alguien darme una breve introducción al alcance léxico?


528
2018-06-26 05:10


origen


Respuestas:


Los entiendo a través de ejemplos. :)

En primer lugar, Alcance léxico (también llamado Alcance estático), en sintaxis tipo C:

void fun()
{
    int x = 5;

    void fun2()
    {
        printf("%d", x);
    }
}

Cada nivel interno puede acceder a sus niveles exteriores.

Hay otra forma, llamada Dynamic Scope utilizada por la primera implementación de Lisp, de nuevo en sintaxis tipo C:

void fun()
{
    printf("%d", x);
}

void dummy1()
{
    int x = 5;

    fun();
}

void dummy2()
{
    int x = 10;

    fun();
}

aquí fun puede acceder x en dummy1 o dummy2, o cualquier x en cualquier función que llame fun con x declarado en él.

dummy1();

imprimirá 5,

dummy2();

imprimirá 10.

El primero se llama estático porque se puede deducir en tiempo de compilación, el segundo se llama dinámico porque el alcance externo es dinámico y depende de la llamada en cadena de las funciones.

Encuentro que el alcance estático es más fácil para el ojo. La mayoría de los lenguajes fueron de esta manera eventualmente incluso Lisp (puede hacer ambos, ¿no?). El alcance dinámico es como pasar referencias de todas las variables a la función llamada.

Un ejemplo de por qué el compilador no puede deducir el alcance dinámico externo de una función, considere nuestro último ejemplo, si escribimos algo como esto:

if(/* some condition */)
    dummy1();
else
    dummy2();

La cadena de llamada depende de una condición de tiempo de ejecución. Si es verdadero, entonces la cadena de llamadas se ve así:

dummy1 --> fun()

Si la condición es falsa:

dummy2 --> fun()

El alcance externo de fun en ambos casos es quien llama más la persona que llama de la persona que llama, etc..

Solo mencionar que el lenguaje C no permite funciones anidadas ni alcance dinámico.


547
2018-06-26 05:26



Probemos la definición más corta posible:

Alcance léxico define cómo se resuelven los nombres de variables en funciones anidadas: las funciones internas contienen el alcance de las funciones principales incluso si la función principal ha regresado.

¡Eso es todo al respecto! Para ayudarme a entender lo que esto significa, escribí una publicación en profundidad sobre alcance de la función y alcance léxico en JavaScript que se puede encontrar aquí. Quizás esto podría servirle a alguien más también.


197
2018-05-24 12:20



El alcance léxico (AKA estático) se refiere a determinar el alcance de una variable basándose únicamente en su posición dentro del corpus de código textual. Una variable siempre se refiere a su entorno de nivel superior. Es bueno entenderlo en relación con el alcance dinámico.


33
2018-06-26 05:14



El alcance define el área, donde las funciones, variables y demás están disponibles. La disponibilidad de una variable, por ejemplo, se define dentro de su contexto, digamos la función, archivo u objeto en el que están definidos. Generalmente llamamos a estas variables locales.

La parte léxica significa que puede derivar el alcance de la lectura del código fuente.

El alcance léxico también se conoce como alcance estático.

El alcance dinámico define variables globales que pueden ser llamadas o referenciadas desde cualquier lugar después de ser definidas. A veces se denominan variables globales, aunque las variables globales en la mayoría de los lenguajes de programación son de alcance léxico. Esto significa que puede derivarse de la lectura del código que la variable está disponible en este contexto. Tal vez uno tiene que seguir una cláusula uses o includes para encontrar la instatiación o definición, pero el código / compilador conoce la variable en este lugar.

En el ámbito dinámico, por el contrario, primero busca en la función local, luego busca en la función que llamó a la función local, luego busca en la función que llamó a esa función, y así sucesivamente, sube la pila de llamadas. "Dinámico" se refiere al cambio, en el sentido de que la pila de llamadas puede ser diferente cada vez que se llama a una función dada, por lo que la función puede afectar diferentes variables dependiendo de dónde se llama. (ver aquí)

Para ver un ejemplo interesante para el alcance dinámico ver aquí.

Para más detalles ver aquí y aquí.

Algunos ejemplos en Delphi / Object Pascal

Delphi tiene alcance léxico.

unit Main;
uses aUnit;  // makes available all variables in interface section of aUnit

interface

  var aGlobal: string; // global in the scope of all units that use Main;
  type 
    TmyClass = class
      strict private aPrivateVar: Integer; // only known by objects of this class type
                                    // lexical: within class definition, 
                                    // reserved word private   
      public aPublicVar: double;    // known to everyboday that has access to a 
                                    // object of this class type
    end;

implementation

  var aLocalGlobal: string; // known to all functions following 
                            // the definition in this unit    

end.

Lo más cerca que Delphi llega al alcance dinámico es el par de funciones RegisterClass () / GetClass (). Para su uso ver aquí.

Digamos que el momento en que se llama a RegisterClass ([TmyClass]) para registrar una determinada clase no puede predecirse leyendo el código (se llama en un método de clic de botón llamado por el usuario), el código que llama GetClass ('TmyClass') se obtendrá un resultado o no La llamada a RegisterClass () no tiene que estar en el alcance léxico de la unidad que usa GetClass ();

Otra posibilidad para el alcance dinámico es métodos anónimos (cierres) en Delphi 2009, ya que conocen las variables de su función de llamada. No sigue la ruta de llamada desde allí recursivamente y, por lo tanto, no es completamente dinámico.


31
2018-06-26 05:22



Me encantan las respuestas con todas las funciones, independientes del idioma, de personas como @Arak. Como esta pregunta fue etiquetada JavaScript sin embargo, me gustaría introducir algunas notas muy específicas a este idioma.

En JavaScript, nuestras opciones para el alcance son:

  • tal como está (sin ajuste de alcance)
  • léxico var _this = this; function callback(){ console.log(_this); }
  • ligado callback.bind(this)

Vale la pena señalar, creo, que JavaScript realmente no tiene alcance dinámico. .bind ajusta el this palabra clave, y eso está cerca, pero técnicamente no es lo mismo.

Aquí hay un ejemplo que demuestra ambos enfoques. Usted hace esto cada vez que toma una decisión sobre cómo escanear devoluciones de llamada, por lo que esto se aplica a promesas, controladores de eventos y más.

Léxico

Esto es lo que podrías llamar Lexical Scoping de devoluciones de llamadas en JavaScript:

var downloadManager = {
  initialize: function() {
    var _this = this; // Set up `_this` for lexical access
    $('.downloadLink').on('click', function () {
      _this.startDownload();
    });
  },
  startDownload: function(){
    this.thinking = true;
    // request the file from the server and bind more callbacks for when it returns success or failure
  }
  //...
};

Ligado

Otra forma de alcance es usar Function.prototype.bind:

var downloadManager = {
  initialize: function() {
    $('.downloadLink').on('click', function () {
      this.startDownload();
    }.bind(this)); // create a function object bound to `this`
  }
//...

Estos métodos son, hasta donde sé, conductualmente equivalentes.


29
2017-08-27 18:51



var scope = "I am global";
function whatismyscope(){
   var scope = "I am just a local";
   function func() {return scope;}
   return func;
}

whatismyscope()()

El código anterior devolverá "Solo soy un local". No volverá "Soy un global". Porque la función func () cuenta donde se definió originalmente que está bajo el alcance de la función whatismyscope.

No se molestará en lo que sea que se llame (el alcance global / desde dentro de otra función, incluso), es por eso que el valor del alcance global I global no se imprimirá.

Esto se llama alcance léxico donde "las funciones se ejecutan usando la cadena de alcance que estaba en efecto cuando se definieron"- de acuerdo con la Guía de Definición de JavaScript.

El alcance léxico es un concepto muy poderoso.

Espero que esto ayude..:)


28
2017-11-05 22:28



Alcance léxico: las variables declaradas fuera de una función son variables globales y son visibles en todas partes en un programa de JavaScript. Las variables declaradas dentro de una función tienen alcance de función y son visibles solo para el código que aparece dentro de esa función.


9
2017-09-23 10:51



IBM lo define como:

La porción de un programa o unidad de segmento en la cual una declaración   aplica Un identificador declarado en una rutina se conoce dentro de ese   rutina y dentro de todas las rutinas anidadas. Si una rutina anidada declara   un artículo con el mismo nombre, el artículo externo no está disponible en   rutina anidada

Ejemplo 1:

function x() {
    /*
    Variable 'a' is only available to function 'x' and function 'y'.
    In other words the area defined by 'x' is the lexical scope of
    variable 'a'
    */
    var a = "I am a";

    function y() {
        console.log( a )
    }
    y();

}
// outputs 'I am a'
x();

Ejemplo 2:

function x() {

    var a = "I am a";

    function y() {
         /*
         If a nested routine declares an item with the same name,
         the outer item is not available in the nested routine.
         */
        var a = 'I am inner a';
        console.log( a )
    }
    y();

}
// outputs 'I am inner a'
x();

7
2017-07-03 18:37



Hay una parte importante de la conversación que rodea al ámbito léxico y dinámico que falta: una explicación simple de la toda la vida de la variable de ámbito - o cuando la variable se puede acceder.

El alcance dinámico solo se corresponde de manera muy vaga con el alcance "global" en la forma en que tradicionalmente lo pensamos (la razón por la que menciono la comparación entre los dos es que ya ha sido mencionado - y no me gusta particularmente el vinculado explicación del artículo); probablemente sea mejor no hacer la comparación entre global y dinámico, aunque supuestamente, de acuerdo con el artículo vinculado, "... [es] útil como sustituto de las variables de alcance global".

Entonces, en inglés simple, ¿cuál es la distinción importante entre los dos mecanismos de determinación del alcance?

El alcance léxico se ha definido muy bien en todas las respuestas anteriores: las variables de ámbito léxico están disponibles, o accesibles, en el nivel local de la función en la que se definió.

Sin embargo, como no es el objetivo de OP, el alcance dinámico no ha recibido mucha atención y la atención que ha recibido significa que probablemente necesite un poco más (eso no es una crítica a otras respuestas, sino un "¡oh! esa respuesta hizo desear que hubiera un poco más "). Entonces, aquí hay un poco más:

El alcance dinámico significa que una variable es accesible para el programa más grande durante el tiempo de vida de la llamada a la función, o mientras la función se está ejecutando. Realmente, Wikipedia realmente hace un buen trabajo con el explicación de la diferencia entre los dos. Para no ofuscarlo, aquí está el texto que describe el alcance dinámico:

... [I] n alcance dinámico (o alcance dinámico), si el alcance de un nombre de variable es   cierta función, entonces su alcance es el período de tiempo durante el cual   la función se está ejecutando: mientras la función se está ejecutando, la variable   nombre existe, y está ligado a su variable, pero después de la función   regresa, el nombre de la variable no existe.


4
2018-02-22 13:22



Ámbito léxico significa que una función busca variables en el contexto en el que se definió, y no en el ámbito inmediato a su alrededor.

Mira cómo funciona el alcance léxico en Lisp si quieres más detalles. La respuesta seleccionada por Kyle Cronin en Variables dinámicas y léxicas en Common Lisp es mucho más claro que las respuestas aquí.

Casualmente, solo aprendí sobre esto en una clase de Lisp, y sucede que también se aplica en JS.

Ejecuté este código en la consola de Chrome.

// javascript               equivalent Lisp
var x = 5;                //(setf x 5)
console.debug(x);         //(print x)
function print_x(){       //(defun print-x () 
    console.debug(x);     //    (print x)
}                         //)
(function(){              //(let  
    var x = 10;           //    ((x 10))
    console.debug(x);     //    (print x)
    print_x();            //    (print-x)
})();                     //)

salida:

5
10
5 

2
2017-09-15 14:38



Aquí hay un ángulo diferente en esta pregunta que podemos obtener dando un paso atrás y mirando el papel del alcance en el marco más amplio de interpretación (ejecutar un programa). En otras palabras, imagina que estás construyendo un intérprete (o compilador) para un idioma y que eres responsable de calcular el resultado, dado un programa y algunos comentarios.

La interpretación implica hacer un seguimiento de tres cosas:

1) Estado: a saber, variables y ubicaciones de memoria referenciadas en el montón y la pila.

2) Operaciones en ese estado, es decir, cada línea de código en su programa

3) El entorno en el que se desarrolla una operación determinada, a saber, la proyección del Estado sobre una operación.

Un intérprete comienza en la primera línea de código de un programa, calcula su entorno, ejecuta la línea en ese entorno y captura su efecto en el estado del programa. Luego sigue el flujo de control del programa para ejecutar la siguiente línea de código, y repite el proceso hasta que el programa finaliza.

La forma en que calcula el entorno para cualquier operación es a través de un conjunto formal de reglas definidas por el lenguaje de programación. El término "enlace" se usa con frecuencia para describir el mapeo del estado general del programa a un valor en el entorno. Tenga en cuenta que por "estado general" no nos referimos al estado global, sino a la suma total de cada definición alcanzable, en cualquier punto de la ejecución)

Este es el marco en el que se define el problema de alcance. Ahora a la siguiente parte de cuáles son nuestras opciones.

  • Como implementador del intérprete, podría simplificar su tarea al hacer que el entorno esté lo más cerca posible del estado del programa. En consecuencia, el entorno de una línea de código simplemente se definiría por el entorno de la línea de código anterior con los efectos de esa operación aplicada a él, independientemente de si la línea anterior era una asignación, una llamada de función, un retorno de una función, o una estructura de control tal como un ciclo while.

Esta es la esencia de alcance dinámico, donde el entorno en el que se ejecuta cualquier código está ligado al estado del programa según lo define su contexto de ejecución.

  • O, podría pensar en un programador que use su idioma y simplifique su tarea de hacer un seguimiento de los valores que una variable puede tomar. Hay demasiados caminos y demasiada complejidad involucrada en el razonamiento sobre el resultado, la totalidad de la ejecución pasada. Alcance léxico ayuda a hacer esto al restringir el entorno actual a la porción de estado definido en el bloque actual, la función u otra unidad de ámbito y su elemento primario (es decir, el bloque que contiene el reloj actual o la función que llamó a la función presente).

En otras palabras, con Alcance léxico el entorno que ve cualquier código está obligado a estado asociado con un ámbito definido explícitamente en el lenguaje, como un bloque o una función.


0
2018-04-25 18:46