Tại sao ràng buộc không phải là một tính năng bản địa trong hầu hết các ngôn ngữ?


11

IMHO ràng buộc một biến với một biến khác hoặc một biểu thức là một kịch bản rất phổ biến trong toán học. Trong thực tế, ban đầu, nhiều sinh viên nghĩ toán tử gán (=) là một loại ràng buộc nào đó. Nhưng trong hầu hết các ngôn ngữ, ràng buộc không được hỗ trợ như một tính năng gốc. Trong một số ngôn ngữ như C #, ràng buộc được hỗ trợ trong một số trường hợp với một số điều kiện được đáp ứng.

Nhưng IMHO thực hiện điều này như một tính năng gốc cũng đơn giản như thay đổi mã sau đây-

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

đến đây-

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

Có nghĩa là đặt lệnh ràng buộc làm bài tập sau mỗi lệnh thay đổi giá trị của bất kỳ biến nào có trong biểu thức ở bên phải. Sau này, cắt xén các hướng dẫn dự phòng (hoặc tối ưu hóa trong lắp ráp sau khi biên dịch) sẽ làm.

Vì vậy, tại sao nó không được hỗ trợ nguyên bản trong hầu hết các ngôn ngữ. Đặc biệt trong gia đình ngôn ngữ C?

Cập nhật:

Từ những ý kiến ​​khác nhau, tôi nghĩ rằng tôi nên xác định "ràng buộc" được đề xuất này chính xác hơn-

  • Đây là một cách ràng buộc. Chỉ tổng được ràng buộc với a + b, không phải ngược lại.
  • Phạm vi của ràng buộc là địa phương.
  • Một khi ràng buộc được thiết lập, nó không thể thay đổi. Có nghĩa là, một khi tổng được liên kết với a + b, tổng sẽ luôn là a + b.

Hy vọng ý tưởng rõ ràng hơn bây giờ.

Cập nhật 2:

Tôi chỉ muốn tính năng P # này . Hy vọng nó sẽ ở đó trong tương lai.


14
Có thể bởi vì bất kỳ nhà phát triển trình biên dịch nào đã cố gắng thêm tính năng này vào C đã bị săn lùng và bắn.
Pete Wilson

Tôi đang nói về ràng buộc một chiều (phải sang trái), không phải hai chiều. Các ràng buộc sẽ luôn luôn chỉ ảnh hưởng đến một biến.
Gul Sơn

2
Đối với những gì nó có giá trị, loại quan điểm lập trình này đang gia tăng: lập trình phản ứng. Những gì bạn đang mô tả cũng được thể hiện bằng các chương trình bảng tính như Excel, về cơ bản là liên kết dữ liệu (hoặc lập trình phản ứng) trên steroid.
Mike Rosenblum

11
Nó có thể không phải là một tính năng của ngôn ngữ lập trình "nhất", nhưng nó một tính năng của ngôn ngữ lập trình phổ biến nhất : Excel.
Jörg W Mittag

2
Nhờ có cây biểu thức, điều này đã có thể có trong C #. Tôi đã viết về nó ngày hôm qua: happynomad121.blogspot.com/2013/01/ trên
HappyNomad

Câu trả lời:


9

Bạn đang nhầm lẫn lập trình với toán học. Ngay cả lập trình chức năng cũng không hoàn toàn là toán học, mặc dù nó mượn nhiều ý tưởng và biến chúng thành thứ gì đó có thể được thực thi và sử dụng để lập trình. Lập trình mệnh lệnh (bao gồm hầu hết các ngôn ngữ lấy cảm hứng từ C, ngoại lệ đáng chú ý là JavaScript và các phần bổ sung gần đây của C #) gần như không liên quan gì đến toán học, vậy tại sao các biến này lại hoạt động giống như các biến trong toán học?

Bạn phải xem xét rằng đây không phải luôn luôn là những gì bạn muốn. Vì vậy, nhiều người bị cắn bởi các bao đóng được tạo ra trong các vòng lặp đặc biệt bởi vì các bao đóng giữ biến, không phải là một bản sao của giá trị của nó tại một số điểm, tức là for (i = 0; i < 10; i++) { var f = function() { return i; }; /* store f */ }tạo ra mười bao đóng trả về 9. Vì vậy, bạn cần hỗ trợ cả hai cách - có nghĩa là gấp đôi chi phí cho "ngân sách phức tạp" và một nhà điều hành khác. Cũng có thể không tương thích giữa mã sử dụng mã này và mã không sử dụng mã này, trừ khi hệ thống loại đủ tinh vi (phức tạp hơn!).

Ngoài ra, thực hiện điều này có hiệu quả là rất khó. Việc thực hiện ngây thơ thêm một chi phí không đổi cho mỗi nhiệm vụ, có thể tăng lên nhanh chóng trong các chương trình bắt buộc. Các triển khai khác có thể trì hoãn cập nhật cho đến khi biến được đọc, nhưng điều đó phức tạp hơn đáng kể và vẫn có chi phí ngay cả khi biến không bao giờ được đọc lại. Trình biên dịch thông minh hiệu quả có thể tối ưu hóa cả hai, nhưng trình biên dịch thông minh hiệu quả rất hiếm và tốn nhiều công sức để tạo ra (lưu ý rằng nó không phải lúc nào cũng đơn giản như trong ví dụ của bạn, đặc biệt là khi các biến có phạm vi rộng và đa luồng phát huy tác dụng!).

Lưu ý rằng lập trình phản ứng về cơ bản là về điều này (theo như tôi có thể nói), vì vậy nó tồn tại. Nó chỉ không phổ biến trong các ngôn ngữ lập trình truyền thống. Và tôi đặt cược một số vấn đề triển khai tôi liệt kê trong đoạn trước đã được giải quyết.


Tôi nghĩ bạn có 3 điểm- 1) Đây không phải là chương trình theo phong cách bắt buộc. Ngày nay, hầu hết các ngôn ngữ không bị giới hạn trong một số mô hình. Tôi không nghĩ rằng điều này nằm ngoài phong cách này là một logic tốt. 2) Độ phức tạp. Bạn đã chỉ ra nhiều thứ phức tạp hơn đã được hỗ trợ. Tại sao không phải cái này? Tôi biết các nhà khai thác gần như không sử dụng đang được hỗ trợ. Và đây là một tính năng hoàn toàn mới. Vì vậy, làm thế nào có thể có vấn đề tương thích. Tôi không thay đổi hoặc xóa sạch một cái gì đó. 3) Thực hiện khó khăn. Tôi nghĩ rằng trình biên dịch ngày nay đã có khả năng tối ưu hóa điều này.
Gul Sơn

1
@Gulshan: (1) Không có mô hình lập trình nào là toán học. FP gần, nhưng FP tương đối hiếm và các ngôn ngữ FP không tinh khiết với các biến có thể thay đổi không đối xử với các biến này như thể chúng là từ toán học. Một mô hình mà điều này tồn tại là lập trình phản ứng, nhưng lập trình phản ứng không được biết đến hoặc sử dụng rộng rãi (trong các ngôn ngữ lập trình truyền thống). (2) Mọi thứ đều có một số chi phí phức tạp và một giá trị. Điều này có chi phí khá cao và IMHO tương đối ít giá trị ngoại trừ trong một vài tên miền, vì vậy đây không phải là điều đầu tiên tôi thêm vào ngôn ngữ của mình trừ khi nó nhắm mục tiêu cụ thể vào các tên miền này.

Tại sao không có thêm một công cụ trong hộp của chúng tôi? Một khi có một công cụ, mọi người sẽ sử dụng nó. Một câu hỏi. Nếu một số ngôn ngữ như C hoặc C ++ muốn thực hiện tính năng này và hỏi ý kiến ​​của bạn, bạn có nói, "Đừng cho nó. Bởi vì nó sẽ quá khó cho bạn và mọi người sẽ nhầm lẫn với điều này."?
Gul Sơn

@Gulshan: Về (3): Không phải lúc nào cũng vậy (không phải nói, hiếm khi) dễ dàng như trong ví dụ của bạn. Đặt nó vào phạm vi toàn cầu và bạn đột nhiên cần tối ưu hóa thời gian liên kết. Sau đó, thêm liên kết động và thậm chí bạn không thể làm bất cứ điều gì tại compXLime. Bạn cần một trình biên dịch rất thông minh một thời gian chạy bao gồm JIT rất thông minh để làm điều đó tốt. Cũng xem xét số lượng bài tập trong mỗi chương trình không tầm thường. Cả bạn và tôi đều không thể viết những thành phần này. Có nhiều nhất một vài người có thể và quan tâm đến chủ đề này. Và họ có thể có những điều tốt hơn để làm.

@Gulshan: Tôi đã yêu cầu họ không thêm tính năng cho con thú phức tạp khó tin C ++ này hoặc tôi yêu cầu họ không thử sử dụng C cho các công cụ ngoài lập trình hệ thống (không phải là một trong những lĩnh vực rất hữu ích ). Ngoài ra, tất cả tôi đều dành cho các tính năng mới thú vị, nhưng chỉ khi có đủ ngân sách phức tạp còn lại (nhiều ngôn ngữ luôn cạn kiệt ngôn ngữ của họ) và tính năng này hữu ích cho những gì ngôn ngữ dự định làm - như tôi đã nói trước đó, ở đó chỉ là một vài lĩnh vực mà điều này là hữu ích.

3

Nó phù hợp rất kém với hầu hết các mô hình lập trình. Nó sẽ đại diện cho một loại hành động hoàn toàn không được kiểm soát ở khoảng cách xa, trong đó người ta có thể phá hủy giá trị của hàng trăm hoặc hàng ngàn biến và trường đối tượng bằng cách thực hiện một nhiệm vụ duy nhất.


Sau đó, tôi sẽ đề nghị đưa ra một quy tắc rằng, biến ràng buộc tức là phía bên trái sẽ không bị thay đổi bởi bất kỳ cách nào khác. Và những gì tôi đang nói chỉ là ràng buộc một chiều, không phải hai chiều. Nếu bạn làm theo câu hỏi của tôi, bạn có thể thấy. Vì vậy, không có biến khác sẽ bị ảnh hưởng.
Gul Sơn

Điều đó không quan trọng. Mỗi khi bạn viết thư cho ahoặc b, bạn cần xem xét tác động của nó đến từng nơi sumđược sử dụng và mọi nơi bạn đọc sumbạn cần xem xét những gì abđang làm. Đối với các trường hợp không tầm thường, điều đó có thể trở nên phức tạp, đặc biệt nếu biểu thức thực tế bị ràng buộc sumcó thể thay đổi khi chạy.
jprete

Tôi muốn giới thiệu quy tắc rằng biểu thức liên kết không thể thay đổi sau khi liên kết được thực hiện. Ngay cả sự phân công sẽ là không thể. Nó sẽ giống như một bán không đổi một khi bị ràng buộc với một biểu thức. Điều đó có nghĩa là, một khi tổng được liên kết với a + b, nó sẽ luôn là a + b, trong suốt phần còn lại của chương trình.
Gul Sơn

3

Tôi biết, tôi có cảm giác ruột thịt dai dẳng rằng lập trình phản ứng có thể rất tuyệt trong môi trường Web2.0. Tại sao lại có cảm giác? Chà, tôi có một trang này chủ yếu là một bảng thay đổi mọi lúc để đáp ứng với các sự kiện trên ô của bảng. Và các nhấp chuột ô thường có nghĩa là thay đổi lớp của tất cả các ô trong một col hoặc hàng; và điều đó có nghĩa là các vòng lặp vô tận của getRefToDiv () và tương tự, để tìm các ô liên quan khác.

IOW, nhiều trong số ~ 3000 dòng JavaScript tôi đã viết không làm gì ngoài việc xác định vị trí các đối tượng. Có lẽ lập trình phản ứng có thể làm tất cả điều đó với chi phí nhỏ; và giảm rất nhiều trong các dòng mã.

Các bạn nghĩ gì về điều đó? Có, tôi nhận thấy rằng bảng của tôi có rất nhiều tính năng giống như bảng tính.


3

Tôi nghĩ những gì bạn mô tả được gọi là Bảng tính:

A1=5
B1=A1+1
A1=6

... Sau đó đánh giá B1lợi nhuận 7.

BIÊN TẬP

Ngôn ngữ C đôi khi được gọi là "lắp ráp di động". Đó là ngôn ngữ bắt buộc, trong khi bảng tính, v.v., là ngôn ngữ khai báo. Nói B1=A1+1và mong đợi B1để đánh giá lại khi bạn thay đổi A1chắc chắn là tuyên bố. Các ngôn ngữ khai báo (trong đó các ngôn ngữ chức năng là tập hợp con) thường được coi là ngôn ngữ cấp cao hơn, vì chúng nằm cách xa phần cứng hoạt động.

Trên một lưu ý liên quan, các ngôn ngữ tự động hóa như logic thang thường là khai báo. Nếu bạn viết một chuỗi logic nói rằng output A = input B OR input Cnó sẽ đánh giá lại câu nói đó liên tục và Acó thể thay đổi bất cứ khi nào Bhoặc Cthay đổi. Các ngôn ngữ tự động hóa khác như Sơ đồ khối chức năng (mà bạn có thể quen thuộc nếu bạn đã sử dụng Simulink) cũng là khai báo và thực hiện liên tục.

Một số thiết bị tự động hóa (được nhúng) được lập trình bằng C và nếu là hệ thống thời gian thực, nó có thể có một vòng lặp vô hạn để thực hiện lại logic nhiều lần, tương tự như cách logic bậc thang thực thi. Trong trường hợp đó, bên trong vòng lặp chính của bạn, bạn có thể viết:

A = B || C;

... Và vì nó luôn luôn thực thi, nên nó trở thành khai báo. Asẽ liên tục được đánh giá lại.


3

C, C ++, Mục tiêu-C:

Các khối cung cấp các tính năng ràng buộc mà bạn đang tìm kiếm.

Trong ví dụ của bạn:

tổng: = a + b;

bạn đang đặt sumbiểu thức a + btrong ngữ cảnh trong đó ablà các biến hiện có. Bạn có thể thực hiện chính xác điều đó với một "khối" (hay còn gọi là biểu thức lambda) trong C, C ++ hoặc Objective-C với các tiện ích mở rộng của 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

Điều này đặt sumthành một khối trả về tổng của a và b. Trình __blockxác định lớp lưu trữ chỉ ra rằng abcó thể thay đổi. Với những điều trên, chúng ta có thể chạy đoạn mã sau:

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

và nhận đầu ra:

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

Sự khác biệt duy nhất giữa việc sử dụng một khối và "ràng buộc" mà bạn đề xuất là cặp dấu ngoặc rỗng sum(). Sự khác biệt giữa sumsum()là sự khác biệt giữa một biểu thức và kết quả của biểu thức đó. Lưu ý rằng, cũng như các hàm, dấu ngoặc đơn không phải trống - các khối có thể lấy tham số giống như các hàm thực hiện.


2

C ++

Cập nhật để được chung chung. Tham số hóa về các loại đầu vào và đầu vào. Có thể cung cấp bất kỳ hoạt động nhị phân thỏa mãn các loại tham số. Mã tính kết quả theo yêu cầu. Nó cố gắng không tính toán lại kết quả nếu nó có thể thoát khỏi nó. Loại bỏ điều này nếu điều này là không thể chấp nhận được (vì tác dụng phụ, vì các đối tượng chứa trong đó là lớn, vì bất cứ điều gì.)

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

Mặc dù nó không trả lời câu hỏi, tôi thích ý tưởng của bạn. Có thể có một phiên bản chung hơn?
Gul Sơn

@Gulshan: Đã cập nhật
Thomas Eding
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.