¿Por qué el enlace no es una característica nativa en la mayoría de los idiomas?

11

En mi humilde opinión, vincular una variable a otra variable o una expresión es un escenario muy común en matemáticas. De hecho, al principio, muchos estudiantes piensan que el operador de asignación (=) es algún tipo de enlace. Pero en la mayoría de los idiomas, el enlace no es compatible como una característica nativa. En algunos lenguajes como C #, el enlace es compatible en algunos casos con algunas condiciones cumplidas.

Pero en mi humilde opinión, implementar esto como una característica nativa fue tan simple como cambiar el siguiente código:

int a,b,sum;
sum := a + b;
a = 10;
b = 20;
a++;

a esto-

int a,b,sum;
a = 10;
sum = a + b;
b = 20;
sum = a + b;
a++;
sum = a + b;

Significa colocar la instrucción de enlace como asignaciones después de cada instrucción que cambia los valores de cualquiera de las variables contenidas en la expresión en el lado derecho. Después de esto, se recortarán las instrucciones redundantes (u optimización en el ensamblaje después de la compilación).

Entonces, ¿por qué no se admite de forma nativa en la mayoría de los idiomas? ¿Especialmente en la familia C de lenguajes?

Actualizar:

Desde diferentes opiniones, creo que debería definir esta propuesta "vinculante" más precisamente:

  • Esta es una forma vinculante. Solo la suma está vinculada a a + b, no al revés.
  • El alcance del enlace es local.
  • Una vez que se establece el enlace, no se puede cambiar. Es decir, una vez que la suma está vinculada a a + b, la suma siempre será a + b.

Espero que la idea sea más clara ahora.

Actualización 2:

Solo quería esta función P # . Espero que esté allí en el futuro.

Gulshan
fuente
14
Posiblemente porque cualquier desarrollador de compiladores que ha intentado agregar esta característica a C ha sido perseguido y fusilado.
Pete Wilson
Estoy hablando del enlace unidireccional (de derecha a izquierda), no bidireccional. El enlace siempre afectará solo a una variable.
Gulshan
2
Por lo que vale, este tipo de punto de vista programático está en aumento: la programación reactiva. Lo que está describiendo también está incorporado en programas de hojas de cálculo como Excel, que son esencialmente enlaces de datos (o programación reactiva) en esteroides.
Mike Rosenblum
11
Puede que no sea una característica de "la mayoría" de los lenguajes de programación, pero es una característica del lenguaje de programación más popular : Excel.
Jörg W Mittag
2
Gracias a los árboles de expresión, esto ya es posible en C #. Ayer escribí un
HappyNomad

Respuestas:

9

Estás confundiendo la programación con las matemáticas. Ni siquiera la programación funcional es completamente matemática, a pesar de que toma prestadas muchas ideas y las convierte en algo que puede ejecutarse y usarse para la programación. La programación imperativa (que incluye la mayoría de los lenguajes inspirados en C, con excepciones notables como JavaScript y las adiciones más recientes a C #) no tiene casi nada que ver con las matemáticas, entonces, ¿por qué estas variables deberían comportarse como variables en matemáticas?

Tienes que tener en cuenta que esto no siempre es lo que quieres. Muchas personas son mordidas por cierres creados en bucles específicamente porque los cierres mantienen la variable, no una copia de su valor en algún momento, es decir, for (i = 0; i < 10; i++) { var f = function() { return i; }; /* store f */ }crean diez cierres que regresan 9. Por lo tanto, necesitaría soporte en ambos sentidos, lo que significa el doble del costo en el "presupuesto de complejidad" y otro operador. Posiblemente también las incompatibilidades entre el código que usa esto y el código que no usa esto, a menos que el sistema de tipos sea lo suficientemente sofisticado (¡más complejidad!).

Además, implementar esto eficientemente es muy difícil. La implementación ingenua agrega una sobrecarga constante a cada tarea, que puede sumarse rápidamente en los programas imprescindibles. Otras implementaciones pueden retrasar las actualizaciones hasta que se lea la variable, pero eso es significativamente más complejo y aún tiene sobrecarga incluso cuando la variable nunca se vuelve a leer. Un compilador suficientemente inteligente puede optimizar ambos, pero los compiladores suficientemente inteligentes son raros y requieren mucho esfuerzo para crear (tenga en cuenta que no siempre es tan simple como en su ejemplo, ¡especialmente cuando las variables tienen un alcance amplio y entra en juego el subprocesamiento múltiple!).

Tenga en cuenta que la programación reactiva se trata básicamente de esto (por lo que puedo decir), por lo que existe. Simplemente no es tan común en los lenguajes de programación tradicionales. Y apuesto a que algunos de los problemas de implementación que enumeré en el párrafo anterior están resueltos.


fuente
Creo que tienes 3 puntos: 1) No es una programación de estilo imperativo. En la actualidad, la mayoría de los idiomas no se limitan a algunos paradigmas. No creo que esto esté fuera de este estilo de paradigma es una buena lógica. 2) Complejidad. Has demostrado que muchas cosas más complejas ya son compatibles. ¿Por qué no este? Sé que los operadores que casi no tienen uso son compatibles. Y esta es una característica totalmente nueva. Entonces, ¿cómo puede haber un problema de compatibilidad? No estoy cambiando o borrando algo. 3) Implementación dura. Creo que los compiladores actuales ya tienen la capacidad de optimizar esto.
Gulshan
1
@Gulshan: (1) Ningún paradigma de programación es matemático. FP está cerca, pero FP es relativamente raro y los lenguajes impuros de FP con variables mutables tampoco tratan estas variables como si fueran de matemáticas. El único paradigma donde esto existe es la programación reactiva, pero la programación reactiva no se conoce ni se usa ampliamente (en lenguajes de programación tradicionales). (2) Todo tiene un costo de complejidad y un valor. Esto tiene un costo bastante alto y, en mi humilde opinión, relativamente poco valor, excepto en algunos dominios, por lo que no es lo primero que agregaría a mi idioma a menos que se dirija específicamente a estos dominios.
¿Por qué no tener una herramienta más en nuestra caja? Una vez que haya una herramienta allí, la gente la usará. Una pregunta. Si algún lenguaje como C o C ++ quiere implementar esta función y le pide su opinión, ¿diría: "No lo dé. Porque será demasiado difícil para usted y la gente se equivocará con esto"?
Gulshan
@Gulshan: En cuanto a (3): No siempre es (por no decir, rara vez) tan fácil como en su ejemplo. Póngalo en el ámbito global y de repente necesita optimizaciones de tiempo de enlace. Luego agregue enlaces dinámicos, y ni siquiera puede hacer nada en tiempo de compilación. Necesita un compilador muy inteligente y un tiempo de ejecución muy inteligente que incluya JIT para hacerlo bien. También considere la cantidad de tareas en cada programa no trivial. Ni tú ni yo podemos escribir estos componentes. Hay a lo sumo algunas personas que pueden y están interesadas en este tema. Y podrían tener mejores cosas que hacer.
@Gulshan: les pediría que no agreguen características a la bestia increíblemente compleja que C ++ ya es, o les pediría que no intenten usar C para cosas más allá de la programación del sistema (que no es uno de los dominios donde esto es muy útil ) Aparte de eso, estoy a favor de nuevas características interesantes, pero solo cuando queda suficiente presupuesto de complejidad (muchos idiomas siempre agotan el suyo) y la función es útil para lo que el idioma pretende hacer, como dije antes, hay son solo unos pocos dominios donde esto es útil.
3

Se adapta muy mal con la mayoría de los modelos de programación. Representaría una especie de acción a distancia completamente descontrolada, en la que uno podría destruir el valor de cientos o miles de variables y campos de objetos haciendo una sola asignación.

jprete
fuente
Entonces sugeriría hacer una regla que, la variable ligada, es decir, el lado izquierdo no se cambiará de ninguna otra manera. Y de lo que estoy hablando es de una sola vía vinculante, no de dos vías. Si sigues mi pregunta, puedes ver. Por lo tanto, ninguna otra variable se verá afectada.
Gulshan
Eso no importa Cada vez que escribe ao bnecesita tener en cuenta su impacto en cada lugar que sumse utiliza, y cada lugar que lee sumdebe tener en cuenta qué ay qué bestá haciendo. Para casos no triviales, eso podría complicarse, especialmente si la expresión real vinculada a sumpuede cambiar en tiempo de ejecución.
jprete
Recomendaría la regla de que la expresión de enlace no se puede cambiar una vez que se realiza el enlace. Incluso la asignación será imposible. Será como una semi-constante una vez vinculada a una expresión. Eso significa que, una vez que la suma está vinculada a a + b, siempre será a + b, durante el resto del programa.
Gulshan
3

Ya sabes, tengo este presentimiento persistente de que la programación reactiva podría ser genial en un entorno Web2.0. ¿Por qué el sentimiento? Bueno, tengo esta página que es principalmente una tabla que cambia todo el tiempo en respuesta a los eventos onClick de tabla-celda. Y los clics en las celdas a menudo significan cambiar la clase de todas las celdas en una columna o fila; y eso significa bucles sin fin de getRefToDiv (), y similares, para encontrar otras celdas relacionadas.

IOW, muchas de las ~ 3000 líneas de JavaScript que he escrito no hacen más que localizar objetos. Quizás la programación reactiva podría hacer todo eso a un bajo costo; y con una gran reducción en las líneas de código.

¿Qué piensan acerca de eso, chicos? Sí, noto que mi tabla tiene muchas características de hoja de cálculo.

Pete Wilson
fuente
3

Creo que lo que estás describiendo se llama Hoja de cálculo:

A1=5
B1=A1+1
A1=6

... luego evaluando B1retornos 7.

EDITAR

El lenguaje C a veces se llama "ensamblaje portátil". Es un lenguaje imperativo, mientras que las hojas de cálculo, etc., son lenguajes declarativos. Decir B1=A1+1y esperar B1volver a evaluar cuando cambias A1es definitivamente declarativo. Los lenguajes declarativos (de los cuales los lenguajes funcionales son un subconjunto) generalmente se consideran idiomas de nivel superior, porque están más lejos de cómo funciona el hardware.

En una nota relacionada, los lenguajes de automatización como la lógica de escalera suelen ser declarativos. Si se escribe un renglón de lógica que dice output A = input B OR input Cque va a volver a evaluar esta afirmación constantemente, y Apuede cambiar cada vez Bo Ccambios. Otros lenguajes de automatización como el Diagrama de bloques de funciones (con el que puede estar familiarizado si ha utilizado Simulink) también son declarativos y se ejecutan continuamente.

Algunos equipos de automatización (integrados) se programan en C, y si se trata de un sistema en tiempo real, probablemente tenga un bucle infinito que vuelva a ejecutar la lógica una y otra vez, de forma similar a cómo se ejecuta la lógica de escalera. En ese caso, dentro de su bucle principal podría escribir:

A = B || C;

... y como se está ejecutando todo el tiempo, se vuelve declarativo. Aserá constantemente reevaluado.

Scott Whitlock
fuente
3

C, C ++, Objetivo-C:

Los bloques proporcionan la función de encuadernación que estás buscando.

En tu ejemplo:

suma: = a + b;

está configurando sumla expresión a + ben un contexto donde ay bson variables existentes. Puede hacer exactamente eso con un "bloque" (también conocido como cierre, también conocido como expresión lambda) en C, C ++ u Objective-C con las extensiones de Apple (pdf):

__block int a = 0, b = 0;           // declare a and b
int (^sum)(void);                   // declare sum
sum = ^(void){return a + b;};       // sum := a + b

Esto se establece sumen un bloque que devuelve la suma de a y b. El __blockespecificador de clase de almacenamiento indica eso ay bpuede cambiar. Dado lo anterior, podemos ejecutar el siguiente código:

printf("a=%d\t b=%d\t sum=%d\n", a, b, sum());
a = 10;
printf("a=%d\t b=%d\t sum=%d\n", a, b, sum());
b = 32;
printf("a=%d\t b=%d\t sum=%d\n", a, b, sum());
a++;
printf("a=%d\t b=%d\t sum=%d\n", a, b, sum());

y obtener la salida:

a=0      b=0     sum=0
a=10     b=0     sum=10
a=10     b=32    sum=42
a=11     b=32    sum=43

La única diferencia entre usar un bloque y el "enlace" que propone es el par de paréntesis vacío sum(). La diferencia entre sumy sum()es la diferencia entre una expresión y el resultado de esa expresión. Tenga en cuenta que, como con las funciones, los paréntesis no tienen que estar vacíos: los bloques pueden tomar parámetros al igual que las funciones.

Caleb
fuente
2

C ++

Actualizado para ser genérico. Parametrizado en retorno y tipos de entrada. Puede suministrar cualquier operación binaria que satisfaga los tipos parametrizados. El código calcula el resultado a pedido. Intenta no volver a calcular los resultados si puede salirse con la suya. Elimine esto si no es deseable (debido a los efectos secundarios, porque los objetos contenidos son grandes, por lo que sea).

#include <iostream>

template <class R, class A, class B>
class Binding {
public:
    typedef R (*BinOp)(A, B);
    Binding (A &x, B &y, BinOp op)
        : op(op)
        , rx(x)
        , ry(y)
        , useCache(false)
    {}
    R value () const {
        if (useCache && x == rx && y == ry) {
            return cache;
        }
        x = rx;
        y = ry;
        cache = op(x, y);
        useCache = true;
        return cache;
    }
    operator R () const {
        return value();
    }
private:
    BinOp op;
    A &rx;
    B &ry;
    mutable A x;
    mutable B y;
    mutable R cache;
    mutable bool useCache;
};

int add (int x, int y) {
    return x + y;
}

int main () {
    int x = 1;
    int y = 2;
    Binding<int, int, int> z(x, y, add);
    x += 55;
    y *= x;
    std::cout << (int)z;
    return 0;
}
Thomas Eding
fuente
Aunque no responde la pregunta, me gusta la idea que presentaste. ¿Puede haber una versión más genérica?
Gulshan
@Gulshan: Actualizado
Thomas Eding,