Pregunta

¿Qué son y para qué sirven?

No tengo un título en informática y mi experiencia es VB6 -> ASP -> ASP.NET/C#.¿Alguien puede explicarlo de manera clara y concisa?

¿Fue útil?

Solución

Imagínese si cada línea de su programa fuera una función separada.Cada uno acepta, como parámetro, la siguiente línea/función a ejecutar.

Con este modelo, puede "pausar" la ejecución en cualquier línea y continuarla más tarde.También puede hacer cosas ingeniosas como saltar temporalmente la pila de ejecución para recuperar un valor o guardar el estado de ejecución actual en una base de datos para recuperarlo más tarde.

Otros consejos

Probablemente los comprenda mejor de lo que cree.

Las excepciones son un ejemplo de continuaciones "sólo hacia arriba".Permiten que el código que se encuentra en lo más profundo de la pila llame a un controlador de excepciones para indicar un problema.

Ejemplo de Python:

try:
    broken_function()
except SomeException:
    # jump to here
    pass

def broken_function():
    raise SomeException() # go back up the stack
    # stuff that won't be evaluated

Los generadores son ejemplos de continuaciones "sólo hacia abajo".Permiten que el código vuelva a entrar en un bucle, por ejemplo, para crear nuevos valores.

Ejemplo de Python:

def sequence_generator(i=1):
    while True:
        yield i  # "return" this value, and come back here for the next
        i = i + 1

g = sequence_generator()
while True:
    print g.next()

En ambos casos, estos tuvieron que agregarse específicamente al lenguaje, mientras que en un lenguaje con continuaciones, el programador puede crear estas cosas donde no están disponibles.

Un aviso: este ejemplo no es conciso ni excepcionalmente claro.Esta es una demostración de una poderosa aplicación de continuaciones.Como programador de VB/ASP/C#, es posible que no esté familiarizado con el concepto de pila del sistema o estado de guardado, por lo que el objetivo de esta respuesta es una demostración y no una explicación.

Las continuaciones son extremadamente versátiles y son una forma de guardar el estado de ejecución y reanudarlo más tarde.A continuación se muestra un pequeño ejemplo de un entorno cooperativo de subprocesos múltiples que utiliza continuaciones en Scheme:

(Supongamos que las operaciones poner y quitar la cola funcionan como se esperaba en una cola global no definida aquí)

(define (fork)
  (display "forking\n")
  (call-with-current-continuation
   (lambda (cc)
     (enqueue (lambda ()
                (cc #f)))
     (cc #t))))

(define (context-switch)
  (display "context switching\n")
  (call-with-current-continuation
   (lambda (cc)
     (enqueue
      (lambda ()
        (cc 'nothing)))
     ((dequeue)))))

(define (end-process)
  (display "ending process\n")
  (let ((proc (dequeue)))
    (if (eq? proc 'queue-empty)
        (display "all processes terminated\n")
        (proc))))

Esto proporciona tres verbos que una función puede usar: bifurcación, cambio de contexto y finalización del proceso.La operación fork bifurca el hilo y devuelve #t en una instancia y #f en otra.La operación de cambio de contexto cambia entre subprocesos y el proceso final finaliza un subproceso.

A continuación se muestra un ejemplo de su uso:

(define (test-cs)
  (display "entering test\n")
  (cond
    ((fork) (cond
              ((fork) (display "process 1\n")
                      (context-switch)
                      (display "process 1 again\n"))
              (else (display "process 2\n")
                    (end-process)
                    (display "you shouldn't see this (2)"))))
    (else (cond ((fork) (display "process 3\n")
                        (display "process 3 again\n")
                        (context-switch))
                (else (display "process 4\n")))))
  (context-switch)
  (display "ending process\n")
  (end-process)
  (display "process ended (should only see this once)\n"))

La salida debe ser

entering test
forking
forking
process 1
context switching
forking
process 3
process 3 again
context switching
process 2
ending process
process 1 again
context switching
process 4
context switching
context switching
ending process
ending process
ending process
ending process
ending process
ending process
all processes terminated
process ended (should only see this once)

Aquellos que han estudiado bifurcaciones y subprocesos en una clase a menudo reciben ejemplos similares a este.El propósito de esta publicación es demostrar que con las continuaciones se pueden lograr resultados similares dentro de un solo hilo guardando y restaurando su estado (su continuación) manualmente.

PD- Creo recordar algo similar a esto en On Lisp, así que si quieres ver código profesional deberías consultar el libro.

Una forma de pensar en una continuación es como una pila de procesador.Cuando "llamas con la continuación actual c", llama a tu función "c", y el parámetro pasado a "c" es tu pila actual con todas tus variables automáticas (representada como otra función más, llámala "k ").Mientras tanto, el procesador comienza a crear una nueva pila.Cuando llamas a "k", ejecuta una instrucción de "retorno desde subrutina" (RTS) en la pila original, lo que te lleva de regreso al contexto de la "llamada-con-continuación-actual" original ("llamada-cc" a partir de ahora encendido) y permitiendo que su programa continúe como antes.Si pasó un parámetro a "k", este se convierte en el valor de retorno de "call-cc".

Desde el punto de vista de su pila original, "call-cc" parece una llamada de función normal.Desde el punto de vista de "c", su pila original parece una función que nunca regresa.

Hay un viejo chiste sobre un matemático que capturó un león en una jaula trepando a la jaula, cerrándola y declarando que estaba fuera de la jaula mientras todo lo demás (incluido el león) estaba dentro.Las continuaciones son un poco como la jaula y "c" es un poco como el matemático.Su programa principal piensa que "c" está dentro de él, mientras que "c" cree que su programa principal está dentro de "k".

Puede crear estructuras de flujo de control arbitrarias utilizando continuaciones.Por ejemplo, puede crear una biblioteca de subprocesos."yield" usa "call-cc" para colocar la continuación actual en una cola y luego salta a la que está al principio de la cola.Un semáforo también tiene su propia cola de continuaciones suspendidas, y un hilo se reprograma sacándolo de la cola del semáforo y colocándolo en la cola principal.

Básicamente, una continuación es la capacidad de una función de detener la ejecución y luego continuar donde lo dejó en un momento posterior.En C#, puedes hacer esto usando la palabra clave de rendimiento.Puedo entrar en más detalles si lo deseas, pero querías una explicación concisa.;-)

Todavía me estoy "acostumbrando" a las continuaciones, pero una forma de pensar en ellas que encuentro útil es como abstracciones del concepto de Contador de Programas (PC).Una PC "apunta" a la siguiente instrucción para ejecutar en la memoria, pero, por supuesto, esa instrucción (y casi todas las instrucciones) apunta, implícita o explícitamente, a la instrucción que sigue, así como a cualquier instrucción que deba atender las interrupciones.(Incluso una instrucción NOOP hace implícitamente un SALTO a la siguiente instrucción en la memoria.Pero si ocurre una interrupción, generalmente implicará un SALTO a alguna otra instrucción en la memoria).

Cada punto potencialmente "vivo" de un programa en la memoria al que el control podría saltar en cualquier punto dado es, en cierto sentido, una continuación activa.Otros puntos que se pueden alcanzar son continuaciones potencialmente activas, pero, más concretamente, son continuaciones que se "calculan" potencialmente (tal vez dinámicamente) como resultado de alcanzar una o más de las continuaciones actualmente activas.

Esto parece un poco fuera de lugar en las introducciones tradicionales a las continuaciones, en las que todos los hilos de ejecución pendientes se representan explícitamente como continuaciones en código estático;pero tiene en cuenta el hecho de que, en las computadoras de uso general, la PC apunta a una secuencia de instrucciones que potencialmente podría cambiar el contenido de la memoria que representa una parte de esa secuencia de instrucciones, creando así esencialmente una nueva (o modificada, por así decirlo). ) continuación sobre la marcha, una que realmente no existe a partir de las activaciones de continuaciones que preceden a esa creación/modificación.

Por lo tanto, la continuación puede verse como un modelo de alto nivel de la PC, razón por la cual conceptualmente subsume la llamada/retorno a procedimiento ordinario (tal como el hierro antiguo hacía la llamada/retorno a procedimiento a través de JUMP de bajo nivel, también conocido como GOTO, instrucciones más grabación del proceso). PC de guardia y restauración de la misma al regresar), así como excepciones, subprocesos, corrutinas, etc.

Entonces, así como la PC señala que los cálculos sucederán en "el futuro", una continuación hace lo mismo, pero en un nivel más alto y más abstracto.La PC se refiere implícitamente a la memoria más todas las ubicaciones de la memoria y los registros "ligados" a cualquier valor, mientras que una continuación representa el futuro a través de abstracciones apropiadas para el lenguaje.

Por supuesto, si bien normalmente puede haber solo una PC por computadora (procesador central), de hecho hay muchas entidades "activas" similares a las de una PC, como se mencionó anteriormente.El vector de interrupción contiene un montón, la pila un montón más, ciertos registros pueden contener algunos, etc.Se "activan" cuando sus valores se cargan en el hardware de la PC, pero las continuaciones son abstracciones del concepto, no PC o su equivalente preciso (no existe un concepto inherente de una continuación "maestra", aunque a menudo pensamos y codificamos en esos términos). para mantener las cosas bastante simples).

En esencia, una continuación es una representación de "qué hacer a continuación cuando se invoca" y, como tal, puede ser (y, en algunos lenguajes y en programas de estilo de paso de continuación, a menudo lo es) un objeto de primera clase que es instanciado, transmitido y descartado como cualquier otro tipo de datos, y de forma muy parecida a cómo una computadora clásica trata las ubicaciones de memoria frente a frente la PC, casi intercambiable con los números enteros ordinarios.

En C#, tienes acceso a dos continuaciones.Uno, al que se accede a través return, permite que un método continúe desde donde fue llamado.El otro, al que se accede a través throw, permite que un método continúe en la coincidencia más cercana catch.

Algunos lenguajes te permiten tratar estas declaraciones como valores de primera clase, para que puedas asignarlas y pasarlas en variables.Lo que esto significa es que puedes esconder el valor de return o de throw y llámalos más tarde cuando estés en realidad listo para regresar o tirar.

Continuation callback = return;
callMeLater(callback);

Esto puede resultar útil en muchas situaciones.Un ejemplo es como el anterior, donde desea pausar el trabajo que está haciendo y reanudarlo más tarde cuando sucede algo (como recibir una solicitud web o algo así).

Los estoy usando en un par de proyectos en los que estoy trabajando.En uno, los estoy usando para poder suspender el programa mientras espero IO a través de la red y luego reanudarlo más tarde.En el otro, estoy escribiendo un lenguaje de programación donde le doy al usuario acceso a continuaciones como valores para que pueda escribir return y throw para ellos mismos - o cualquier otro flujo de control, como while bucles, sin que yo tenga que hacerlo por ellos.

Piensa en hilos.Se puede ejecutar un hilo y obtener el resultado de su cálculo.Una continuación es un hilo que puedes copiar, por lo que puedes ejecutar el mismo cálculo dos veces.

Las continuaciones han despertado un interés renovado en la programación web porque reflejan muy bien el carácter de pausa/reanudación de las solicitudes web.Un servidor puede construir una continuación que represente la sesión de un usuario y reanudarla si el usuario continúa la sesión.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top