tiempo proyectos proyecto pmbok plantilla plan gestion exclusiones ejemplos ejemplo basados alcance scope rebol red

scope - proyectos - plan de gestion de alcance pmbok ejemplo



¿Hay una explicación general sobre el alcance de definición en Rebol y Red? (2)

Esta es una vieja pregunta, y la respuesta de @ BrianH aquí es muy completa sobre la mecánica. Pero pensé que le daría uno con un enfoque ligeramente diferente, como un poco más de una "historia".

En Rebol, hay una categoría de tipos llamados palabras . Estos son esencialmente símbolos, por lo que su contenido de cadena se escanea y entran en una tabla de símbolos. Entonces, mientras que "FOO" sería una cadena, y <FOO> sería otro "sabor" de la cadena conocida como etiqueta ... FOO , ''FOO , FOO: y :FOO son todos "sabores" de palabras con el mismo ID del símbolo. (Una "palabra", una "palabra iluminada", una "palabra establecida" y una "palabra escrita" respectivamente).

Al estar contraído en un símbolo, es imposible modificar el nombre de una palabra una vez cargada. Están atascados, en comparación con las cadenas que cada una tiene sus propios datos y son mutables:

>> append "foo" "bar" == "foobar" >> append ''foo ''bar ** Script error: append does not allow word! for its series argument

La inmutabilidad tiene una ventaja, ya que como símbolo es rápido comparar una palabra con otra. Pero hay otra pieza del rompecabezas: cada instancia de una palabra puede tener opcionalmente una propiedad invisible en ella llamada enlace . Ese enlace le permite "señalar" una entidad clave / valor conocida como un contexto donde se puede leer o escribir un valor.

Nota: A diferencia de @BrianH, no creo que llamar "contextos" a esta categoría de objetivos vinculantes sea tan malo, al menos no lo creo hoy. Pregúntame más tarde, podría cambiar de opinión si surgen nuevas pruebas. Basta con decir que es una cosa parecida a un objeto, pero no siempre un objeto ... podría ser una referencia en el marco de una función en la pila, por ejemplo.

Quienquiera que introduzca una palabra en el sistema tiene la primera oportunidad de decir a qué contexto está vinculado. Gran parte de ese tiempo es CARGA, así que si dijiste load "[foo: baz :bar]" y recuperaste el bloque de 3 palabras [foo: baz :bar] , estarían vinculados al "contexto de usuario", con un retroceso al "contexto del sistema".

Seguir el enlace es cómo funciona todo, y cada "sabor" de la palabra hace algo diferente.

>> print "word pointing to function runs it" word pointing to function runs it >> probe :print "get-word pointing to function gets it" make native! [[ "Outputs a value followed by a line break." value [any-type!] "The value to print" ]] == "get-word pointing to function gets it"

Nota: El segundo caso no imprimió esa cadena. Probó la especificación de la función, luego la cadena fue lo último en la evaluación, por lo que se evaluó a eso.

Pero una vez que tenga un bloque de datos con palabras en sus manos, los enlaces son el juego de cualquiera. Siempre que un contexto tenga el símbolo de una palabra, puede redirigir esa palabra a ese contexto. (Suponiendo también que el bloque no ha sido protegido o bloqueado contra modificaciones ...)

Esta cadena en cascada de oportunidades de encuadernación es el punto importante. Dado que FUNC es un "generador de funciones" que toma una especificación y un cuerpo que le das, tiene la capacidad de tomar la "materia prima" del cuerpo con sus ataduras y anular las que decida. Misterioso quizás, pero mira esto:

>> x: 10 >> foo: func [x] [ print x x: 20 print x ] >> foo 304 304 20 >> print x 10

Lo que sucedió fue que FUNC recibió dos bloques, uno representando una lista de parámetros y el segundo representando un cuerpo. Cuando recibió el cuerpo, ambas print estaban vinculadas a la función de impresión nativa (en este caso, y es importante señalar que cuando se obtiene material de otros lugares que no sean la consola, ¡cada una podría estar unida de manera diferente!) . x estaba vinculado al contexto del usuario (en este caso) que tenía el valor 10. Si FUNC no hiciera nada para cambiar la situación, las cosas seguirían siendo así.

Pero combinó la imagen y decidió que, dado que la lista de parámetros tenía una x, miraría a través del cuerpo y sobreescribiría las palabras con el ID de símbolo para x con un nuevo enlace ... local a la función. Esa es la única razón por la que no sobrescribió el global con x: 20 . Si hubiera omitido la [x] en la especificación, FUNC no habría hecho nada, y se habría sobrescrito.

Cada pieza en la cadena de definición obtiene una oportunidad antes de pasar cosas. De ahí la definición del alcance .

DATO DE DIVERSIÓN: Dado que si no suministra parámetros a la especificación de FUNC, no volverá a enlazar nada en el cuerpo, esto llevó a impresiones erróneas de que "todo en Rebol está en el ámbito global" . Pero eso no es cierto en absoluto porque, como dice @BrianH: "Rebol en realidad no tiene alcance (...) Rebol lo falsifica". De hecho, eso es lo que hace FUNCTION (a diferencia de FUNC): va a buscar en el cuerpo las palabras de configuración como x:, y cuando las ve, las agrega al marco local y las une. El efecto se parece al alcance local, pero de nuevo, ¡no lo es!

Si suena un poco Rube-Goldberg-esque para imaginar estos símbolos con punteros invisibles que se barajan alrededor, eso es porque lo es . Para mí, personalmente, lo extraordinario es que funciona ... y he visto a gente hacer trucos con los que no pensarías intuitivamente que un truco tan simple podría ser usado para hacer.

Caso en cuestión: el COLLECT y KEEP ( versión Ren-C ) extremadamente útil:

collect: func [ {Evaluates a block, storing values via KEEP function, and returns block of collected values.} body [block!] "Block to evaluate" /into {Insert into a buffer instead (returns position after insert)} output [any-series!] "The buffer series (modified)" ][ unless output [output: make block! 16] eval func [keep <with> return] body func [ value [<opt> any-value!] /only ][ output: insert/:only output :value :value ] either into [output] [head output] ]

Esta herramienta de apariencia sencilla amplía el lenguaje en el siguiente estilo (nuevamente, la versión Ren-C ... en R3-Alpha o Rebol2 sustituye foreach para for-each y la length? Para la length of )

>> collect [ keep 10 for-each item [a [b c] [d e f]] [ either all [ block? item 3 = length of item ][ keep/only item ][ keep item ] ] ] == [10 a b c [d e f]]

El truco aquí con el alcance de definición se entiende mejor por lo que mencioné anteriormente. FUNC solo sobrescribirá los enlaces de las cosas en su lista de parámetros y dejará todo lo demás en el cuerpo sin tocar. Entonces, lo que sucede es que toma el cuerpo que pasaste a COLLECT y lo usa como el cuerpo de una nueva función, donde sobrescribe cualquier enlace de KEEP. A continuación, establece KEEP en una función que agrega datos a un agregador cuando se llama.

Aquí vemos la versatilidad de la función KEEP en el empalme de bloques en la salida recopilada o no, a través de un interruptor / ONLY (la persona que llama optó por no empalmar solo si vemos un elemento de longitud 3) . Pero esto es solo arañar la superficie. Es solo una característica del lenguaje profundamente poderosa, agregada por los usuarios después del hecho, que se origina a partir de tan poco código que casi da miedo. Ciertamente hay muchas más historias.

Aquí estoy agregando una respuesta debido a que he completado un enlace faltante crucial para el alcance de definición, un problema conocido como el "retorno de ámbito de definición":

https://codereview.stackexchange.com/questions/109443/definitional-returns-solved-mostly

Esta es la razón por la cual el <with> return está junto con KEEP en la especificación. Está ahí porque COLLECT está tratando de decirle a FUNC que quiere "usar sus servicios" como un código de encuadernador y corredor. Pero el cuerpo ya fue escrito en otra parte por alguien más. Entonces, si tiene un RETURN en él, entonces RETURN ya tiene una idea de a dónde regresar. FUNC es solo para "re-alcance" el mantenimiento, pero deja cualquier retorno solo en lugar de agregar el suyo propio. Por lo tanto:

>> foo: func [x] [ collect [ if x = 10 [return "didn''t collect"] keep x keep 20 ] ] >> foo 304 == [304 20] >> foo 10 == "didn''t collect"

Es <with> return que hace que COLLECT sea lo suficientemente inteligente como para saber que dentro del cuerpo de FOO, no quería el rebote de retorno, por lo que se pensó que regresaría de la función cuyo parámetro era simplemente [mantener] en su lugar.

Y hay un poco sobre el "por qué" del ámbito de definición, frente al "qué". :-)

Desde la Guía del usuario de REBOL / Core , y Qué es rojo , he aprendido que tanto Rebol como Rojo utilizan el ámbito de definición .

Por la guía, sé que es una forma de alcance estático , "el alcance de una variable se determina cuando se define su contexto", y también se denomina alcance léxico en tiempo de ejecución , y es una forma dinámica de alcance estático que depende de las definiciones de contexto .

Sé que en com-sci, hay dos formas de alcance: alcance léxico (alcance estático) y alcance dinámico. Esta definición de alcance me confundió.

Entonces, ¿qué es el ámbito de definición?


Rebol realmente no tiene alcance en absoluto.

Tomemos este código:

rebol [] a: 1 func-1: func [] [a] inner: context [ a: 2 func-2: func [] [a] func-3: func [/local a] [a: 3 func-1] ]

Entonces, con ese código cargado, si Rebol tuviera un alcance léxico, esto es lo que verías:

>> reduce [func-1 inner/func-2 inner/func-3] == [1 2 1]

Esto se debe a que func-1 usa la a desde el ámbito externo, la a utilizada por func-2 es desde el ámbito interno, y func-3 llama func-1 , que aún utiliza a desde el ámbito externo donde se definió independientemente de lo que está en func-3 .

Si Rebol tuviera un alcance dinámico, esto es lo que verías:

>> reduce [func-1 inner/func-2 inner/func-3] == [1 2 3]

Esto se debe a que func-3 redefine a , luego llama func-1 , que solo usa la definición activa más reciente de a .

Ahora para Rebol, obtienes ese primer resultado. Pero Rebol no tiene alcance léxico. ¿Entonces por qué?

Rebol lo falsifica. Así es como funciona.

En lenguajes compilados, tienes ámbitos. A medida que el compilador recorre el archivo, realiza un seguimiento del alcance actual, luego cuando ve un alcance anidado que se convierte en el alcance actual. Para el ámbito léxico, el compilador mantiene una referencia al ámbito externo, y luego busca las palabras que no se definieron en el ámbito actual siguiendo los enlaces a los ámbitos externos, hasta que encuentra la palabra, o no. Los lenguajes de ámbito dinámico hacen algo similar, pero en tiempo de ejecución, suben en la pila de llamadas.

Rebol no hace nada de eso; en particular, no se compila, se construye, en tiempo de ejecución. Lo que piensas como código es en realidad datos, bloques de palabras, números y demás. Las palabras son estructuras de datos que tienen un puntero en ellas llamado "enlace".

Cuando esa secuencia de comandos se carga por primera vez, todas las palabras de la secuencia de comandos se agregan al objeto de entorno de la secuencia de comandos (que llamamos un "contexto" de manera inapropiada, aunque no lo es). Mientras se están reuniendo las palabras, se cambian los datos del script. Cualquier palabra que se encuentre en el "contexto" del script está vinculada al "contexto", o "enlazado". Esos enlaces significan que solo puede seguir ese enlace y llegar al objeto donde se almacena el valor de esa palabra. Es muy rapido

Luego, una vez hecho esto, comenzamos a ejecutar el script. Y luego llegamos a este bit: func [] [a] . Eso no es realmente una declaración, es una llamada a una función llamada func que toma un bloque de especificaciones y un bloque de código y los usa para construir una función. Esa función también obtiene su propio objeto de entorno, pero con palabras declaradas en la especificación de la función. En este caso, no hay palabras en la especificación, por lo que es un objeto vacío. Entonces el bloque de código está vinculado a ese objeto. Pero en este caso no hay a en ese objeto, por lo que no se hace nada a la a , se mantiene el enlace que ya tenía cuando estaba atado antes .

Lo mismo ocurre con la llamada de context [...] sí, es una llamada a una función denominada de forma inapropiada, que construye un objeto llamando a make object! . La función de context toma un bloque de datos y busca palabras definidas (aquellas cosas con los dos puntos al final, como a: :), luego construye un objeto con esas palabras, luego une todas las palabras en ese bloque y todos los bloques anidados a las palabras que están en el objeto, en este caso a , func-2 y func-3 . Y eso significa que las a ''s en ese bloque de código han cambiado sus enlaces, para apuntar a ese objeto en su lugar.

Cuando se define func-2 , el enlace de la a en su bloque de código no se anula. Cuando se define func-3 , tiene una a en su especificación, por lo que a: tiene su enlace invalidado.

Lo gracioso de todo esto es que no hay ningún alcance en absoluto . Que el primer código de a: y el de a en func-1 solo se unen una vez, por lo que mantienen su primer enlace. El a: en inner bloque de código inner y la a en func-2 están vinculados dos veces, por lo que mantienen su segundo enlace. El código a a: in func-3 está enlazado tres veces, por lo que también mantiene su último enlace. No se trata de ámbitos, es solo un código que se está enlazando y, a continuación, se vuelven a enlazar fragmentos de código más pequeños, y así sucesivamente hasta que se hace.

Cada ronda de enlace se realiza mediante una función que "define" algo (en realidad, lo crea), y luego, cuando ese código se ejecuta y llama a otras funciones que definen otra cosa, esas funciones realizan otra ronda de enlace a su pequeño subconjunto de código. . Por eso lo llamamos "definición del alcance"; Si bien realmente no es el alcance, es lo que sirve al propósito del alcance en Rebol, y es lo suficientemente parecido al comportamiento del alcance léxico que a primera vista no se puede notar la diferencia.

Realmente se vuelve diferente cuando te das cuenta de que estos enlaces son directos, y puedes cambiarlos ( más o menos, puedes hacer nuevas palabras con el mismo nombre y un enlace diferente). Esa misma función que llaman esas funciones de definición, puede llamarse a sí mismo: se denomina bind . Con bind puede romper la ilusión de alcance y hacer que las palabras se unan a cualquier objeto al que tenga acceso. Puedes hacer trucos maravillosos con bind , incluso hacer tus propias funciones de definición. ¡Es muy divertido!

En cuanto a Red, Red es compilable, pero también incluye un intérprete similar a Rebol, vinculante y todos los beneficios. Cuando se trata de definir cosas con el intérprete, también se hace un alcance de definición.

¿Eso ayuda a aclarar las cosas?