为什么绑定不是大多数语言中的本机功能?


11

恕我直言,将变量绑定到另一个变量或表达式是数学中非常普遍的情况。实际上,在一开始,许多学生认为赋值运算符(=)是某种绑定。但是在大多数语言中,不支持将绑定作为本机功能。在某些语言(例如C#)中,在满足某些条件的情况下,某些情况下支持绑定。

但是恕我直言,将其作为本机功能实现就像更改以下代码一样简单-

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

为此-

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

意味着将绑定指令作为赋值放置在每条指令之后,以更改右侧表达式中包含的任何变量的值。之后,修剪多余的指令(或在编译后进行汇编中的优化)即可。

因此,为什么大多数语言本身都不支持它。特别是在C语言家族中?

更新:

从不同的观点来看,我认为我应该更精确地定义这个提议的“绑定”-

  • 这是绑定的一种方式。只有总和绑定到a + b,反之亦然。
  • 绑定的范围是本地的。
  • 绑定一旦建立,就无法更改。意思是,一旦将总和绑定到a + b,总和将永远是a + b。

希望这个想法现在更加清晰。

更新2:

我只是想要这个P#功能。希望将来会出现。


14
可能是因为任何试图将此功能添加到C中的编译器开发人员都被猎杀了。
皮特·威尔逊

我说的是单向(从右到左)绑定,而不是双向。绑定将始终只影响一个变量。
Gulshan

2
就其价值而言,这种编程观点正在兴起:反应式编程。您所描述的内容还可以通过电子表格程序(例如Excel)来体现,该程序本质上是对类固醇的数据绑定(或反应式编程)。
Mike Rosenblum

11
它可能不是“大多数”编程语言的功能,但它最流行的编程语言的功能:Excel。
约尔格W¯¯米塔格

2
感谢表达式树,这在C#中已经可以实现。我昨天的博客上讲述它:happynomad121.blogspot.com/2013/01/...
HappyNomad

Answers:


9

您将编程与数学混淆了。即使函数式编程借用了许多想法并将它们转变为可以执行并用于编程的东西,它甚至也不是完全数学的。命令式编程(包括大多数受C语言启发的语言,著名的例外是JavaScript和C#的最新添加)与数学几乎没有关系,那么为什么这些变量在数学上应该表现得像变量?

您必须考虑到这并不总是您想要的。如此之多的人被循环创建的闭包所咬住,特别是因为闭包保留变量,而不是在某个时候复制其值,即for (i = 0; i < 10; i++) { var f = function() { return i; }; /* store f */ }创建十个返回的闭包9。因此,您需要同时支持这两种方式-这意味着“复杂性预算”的成本要翻倍,而另一位运营商则需要。除非类型系统足够复杂(更复杂!),否则使用此代码的代码与不使用此代码的代码之间可能还会出现不兼容性。

而且,有效地实现这一点非常困难。天真的实现会给每个分配增加一个固定的开销,这会在命令式程序中迅速增加。其他实现可能会将更新延迟到读取变量之前,但这要复杂得多,并且即使不再读取变量也仍然有开销。足够智能的编译器可以同时优化两者,但是足够智能的编译器很少见,并且需要花很多精力来创建(请注意,它并不总是像示例中那样简单,特别是当变量具有广泛的作用域并且多线程正在发挥作用时!)。

请注意,反应式编程基本上是关于此的(据我所知),因此它确实存在。这在传统编程语言中并不常见。我敢打赌,我在上一段中列出的一些实现问题已解决。


我认为您有3点-1)不是命令式编程。如今,大多数语言都不受某些范式的限制。我不认为这种范式不是很好的逻辑。2)复杂性。您已经展示了已经支持了许多更复杂的功能。为什么不这样呢?我知道支持几乎没有用的运算符。这是一个全新的功能。那么怎么可能存在兼容性问题。我没有更改或擦除任何东西。3)硬实施。我认为当今的编译器已经具有优化此功能的能力。
Gulshan

1
@Gulshan:(1)没有编程范例是数学。FP接近,但FP相对较少,具有可变变量的不纯FP语言也不会将这些变量视为来自数学。存在这种情况的一种范例是反应式编程,但是反应式编程并不为人所知或广泛使用(在传统编程语言中)。(2)一切都有一定的复杂性成本和价值。这有除少数领域相当高的成本,恕我直言,相对价值不大,所以它不是我想补充的第一件事,我的语言,除非它专门针对这些领域。

为什么我们的包装盒中没有其他工具?一旦有了工具,人们就会使用它。一个问题。如果某些语言(例如C或C ++)想要实现此功能并征求您的意见,您会说:“不要放弃。因为这对您来说太难了,人们会对此感到困惑”。
Gulshan

@Gulshan:关于(3):并不总是(不说,很少)像您的示例中那样容易。将其放到全局范围内,您突然需要链接时优化。然后添加动态链接,您甚至无法在编译时执行任何操作。您需要一个非常聪明的编译器一个非常聪明的运行时(包括JIT)来完成它。还要考虑每个非平凡程序中的分配量。您和我都不能编写这些组件。最多有几个人可以对此主题感兴趣。他们可能还有更好的事情要做。

@Gulshan:我要他们不要向C ++已经增加的复杂野兽添加功能,或者我要他们不要尝试将C用于系统编程之外的东西(这不是非常有用的领域之一) )。除此之外,我全都希望获得令人兴奋的新功能,但是只有当剩下足够的复杂性预算(许多语言总是精疲力尽)并且该功能对于该语言的用途很有用时-正如我之前所说,只有几个有用的领域。

3

它与大多数编程模型非常不匹配。它代表了一种完全不受控制的远距离动作,其中一个动作可以通过一次分配来破坏成百上千个变量和对象字段的值。


然后,我建议制定一条规则,即绑定变量(即左侧)将不会以任何其他方式更改。我所说的只是一种方式的绑定,而不是双向的。如果您遵循我的问题,可以看到。因此,不会影响其他变量。
Gulshan

没关系 每次您写入a或时b,都需要考虑它对sum所使用的每个位置以及所读取的每个位置的影响,sum您需要考虑它们在做什么ab在做什么。对于非平凡的情况,这可能会变得复杂,特别是如果绑定到的实际表达式sum可以在运行时更改的话。
jprete 2011年

我建议以下规则:绑定完成后就不能更改绑定表达式。甚至分配也将是不可能的。一旦绑定到表达式,它将像半常量。这意味着,在程序的其余部分中,一旦将sum绑定到a + b,它将始终是a + b。
Gulshan

3

没错,我有点this不休,感觉到反应式编程在Web2.0环境中可能很酷。为什么会有这种感觉?好吧,我有一个页面,该页面主要是一个表格,该表格一直在更改以响应表格单元格onClick事件。单元格单击通常意味着更改列或行中所有单元格的类别。这意味着getRefToDiv()等无休止的循环,以寻找其他相关的单元格。

IOW,我编写的大约3000行JavaScript中,除了定位对象外,什么都不做。也许反应式编程可以以较低的成本完成所有这些工作;并大大减少了代码行。

你们对此怎么看?是的,我确实注意到我的表具有许多类似电子表格的功能。


3

我认为您所描述的称为电子表格:

A1=5
B1=A1+1
A1=6

...然后评估B1收益7。

编辑

C语言有时称为“便携式程序集”。这是命令性语言,而电子表格等是声明性语言。当您更改时说B1=A1+1并期望B1重新评估A1绝对是声明性的。声明性语言(功能语言是其中的子集)通常被认为是高级语言,因为它们与硬件的工作方式相距甚远。

值得一提的是,自动化语言(例如梯形逻辑)通常是声明性的。如果您编写了一段逻辑output A = input B OR input C语句,说它将不断地重新评估该语句,并且A可以随时更改BC更改。其他自动化语言(例如,功能块图(如果您使用过Simulink,您可能会熟悉))也是声明性的,并且会连续执行。

一些(嵌入式)自动化设备是用C编程的,如果它是实时系统,它可能具有无限循环,可以反复执行逻辑,类似于梯形逻辑的执行方式。在这种情况下,您可以在主循环中编写:

A = B || C;

...并且由于它一直在执行,因此它具有声明性。 A将不断进行重新评估。


3

C,C ++,Objective-C:

提供所需的绑定功能。

在您的示例中:

和:= a + b;

您要在和是现有变量的上下文中设置sum表达式。您可以使用带有Apple扩展名(pdf)的C,C ++或Objective-C中的“块”(aka闭包,又称为lambda表达式)来做到这一点:a + bab

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

设置sum为一个返回a和b之和的块。该__block存储类说明表示ab可能改变。鉴于以上所述,我们可以运行以下代码:

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());

并获得输出:

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

使用块和您建议的“绑定”之间的唯一区别是中的空括号对sum()sum和之间sum()的差异是表达式与该表达式的结果之间的差异。请注意,与函数一样,括号不必为空-块可以像函数一样采用参数。


2

C ++

更新为通用。参数化返回和输入类型。可以提供满足参数化类型的任何二进制运算。代码按需计算结果。如果可以摆脱结果,它将尝试不重新计算结果。如果这是不希望的,请将其删除(由于副作用,因为所包含的对象很大,因为其他原因。)

#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;
}

尽管它不能回答问题,但我喜欢您提出的想法。可以有一个更通用的版本吗?
Gulshan

@Gulshan:更新
Thomas
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.