Чому прив’язка не є рідною особливістю для більшості мов?


11

ІМХО, прив'язування змінної до іншої змінної чи виразу, є дуже поширеним сценарієм у математиці. Насправді на початку багато студентів вважають, що оператор присвоєння (=) є якоюсь прив'язкою. Але в більшості мов прив'язка не підтримується як рідна особливість. У деяких мовах, таких як C #, прив'язка підтримується в деяких випадках з виконанням деяких умов.

Але реалізувати це як основну функцію IMHO було так само просто, як і змінити наступний код -

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;

Значення розміщення інструкції зв’язування як присвоєння після кожної інструкції змінює значення будь-якої змінної, що міститься у виразі праворуч. Після цього буде зроблено обрізання зайвих інструкцій (або оптимізація в зборі після компіляції).

Отож, чому вона не підтримується в основному на більшості мов. Спеціально до сімейства мов С?

Оновлення:

З різних думок, я думаю, я мав би визначити це запропоноване "обов'язкове" точніше -

  • Це один із способів зв’язування. Тільки сума прив’язана до + b, а не навпаки.
  • Область зв’язування локальна.
  • Після встановлення прив'язки її неможливо змінити. Значить, як тільки сума прив’язана до + b, сума завжди буде + b.

Сподіваюся, ідея зараз зрозуміліша.

Оновлення 2:

Я просто хотів цю функцію P # . Сподіваюся, це буде там і надалі.


14
Можливо, тому, що будь-який розробник компілятора, який намагався додати цю функцію до C, був застрелений та застрелений.
Піт Вілсон

Я говорю про одностороннє (праворуч ліворуч) прив’язка, а не двостороння. Зв'язування завжди впливатиме лише на одну змінну.
Гульшан

2
Для чого це варто, ця програмна точка зору піднімається: реактивне програмування. Те, що ви описуєте, також втілюється програмами електронних таблиць, такими як Excel, які по суті є зв'язуванням даних (або реактивним програмуванням) на стероїдах.
Майк Розенблум

11
Це не може бути ознакою «більшість» мов програмування, але це особливість найбільш популярних мов програмування: Excel.
Йорг W Міттаг

2
Завдяки деревам виразів це вже можливо в C #. Я вчора про це блогував
HappyNomad

Відповіді:


9

Ви плутаєте програмування з математикою. Навіть функціональне програмування не є повністю математичним, хоча воно запозичує багато ідей і перетворює їх у те, що можна виконати і використовувати для програмування. Імперативне програмування (яке включає в себе більшість мов, натхнених С, винятком яких є винятки JavaScript та новіші доповнення до C #) майже нічого спільного з математикою, тож чому ці змінні повинні вести себе як змінні в математиці?

Ви повинні врахувати, що це не завжди те, що ви хочете. Так багато людей кусаються замиканнями, створеними в циклах саме тому, що закриття зберігають змінну, а не копію її значення в якийсь момент, тобто for (i = 0; i < 10; i++) { var f = function() { return i; }; /* store f */ }створює десять закриттів, які повертаються 9. Тому вам потрібно буде підтримувати обидва способи - це означає, що вдвічі більше витрат на "складність бюджету" та ще одного оператора. Можливо також нездатності між кодом, що використовує цей код, і кодом, який не використовує його, якщо тільки система типів не є достатньо складною (більше складності!).

Також реалізувати це досить важко. Наївна реалізація додає постійних витрат на кожне завдання, що може швидко доповнити необхідні програми. Інші реалізації можуть затримати оновлення, поки змінна не буде прочитана, але це значно складніше і все ще має накладні витрати, навіть коли змінна ніколи не читається знову. Досить розумний компілятор може оптимізувати і те, і інше, але досить розумні компілятори є рідкісними і вимагають великих зусиль для створення (зауважте, що це не завжди так просто, як у вашому прикладі, особливо коли змінні мають широку сферу застосування та багатогранність читає!).

Зауважте, що реактивне програмування - це в основному про це (наскільки я можу сказати), тому воно існує. Це просто не так часто в традиційних мовах програмування. І я сумніваюся, що деякі проблеми з реалізацією, які я перелічував у попередньому пункті, вирішені.


Я думаю, у вас є 3 бали. 1) Це не обов'язкове програмування стилю. У наші дні більшість мов не обмежені деякими парадигмами. Я не думаю, що це не в парадигмі - це гарна логіка. 2) Складність. Ви показали, що багато складніших речей вже підтримуються. Чому б не цей? Я знаю, що оператори, які майже не користуються, підтримуються. І це абсолютно нова особливість. Отже, як може виникнути проблема сумісності. Я щось не змінюю і не витираю. 3) Важка реалізація. Я думаю, що компілятори сучасності вже мають можливість оптимізувати це.
Гульшан

1
@Gulshan: (1) Жодна парадигма програмування не є математикою. FP близький, але FP відносно рідкісний, і нечисті мови FP з змінними змінними не відносяться до цих змінних так, як ніби вони також з математики. Єдиною парадигмою, де це існує, є реактивне програмування, але реактивне програмування не відомо чи широко використовується (в традиційних мовах програмування). (2) Все має певну складність і цінність. Це має досить високу вартість і IMHO порівняно невелике значення, за винятком кількох доменів, тому це не перше, що я додав би до своєї мови, якщо це спеціально не спрямовано на ці домени.

Чому б у нашому ящику не було ще одного інструменту? Як тільки інструмент знайдеться, люди скористаються ним. Питання. Якщо якась мова, як C або C ++, хоче реалізувати цю функцію і запитати вашу думку, ви б сказали: "Не давайте цього. Тому що вам буде занадто важко, і люди зіпсуються з цим".
Гульшан

@Gulshan: Щодо (3): Це не завжди (не кажучи, рідко) так просто, як у вашому прикладі. Розмістіть його в глобальному масштабі, і вам раптом знадобляться оптимізації часу зв'язку. Потім додайте динамічне посилання, і ви навіть не можете зробити нічого під час компіляції. Вам потрібен дуже розумний компілятор і дуже розумний час виконання, включаючи JIT, щоб зробити це добре. Також врахуйте суму завдань у кожній нетривіальній програмі. Ні ви, ні я не можете написати ці компоненти. Є щонайбільше кілька людей, які можуть і цікавляться цією темою. І вони можуть мати кращі справи.

@Gulshan: Я б попросив їх не додавати функції до надзвичайно складного звіра C ++ вже є, або я б попросив їх не намагатися використовувати C для речей поза системним програмуванням (що не є одним із доменів, де це дуже корисно ). Крім того, я все для нових цікавих функцій, але лише тоді, коли у вас достатньо бюджету на складність (багато мов завжди вичерпали свій), і ця функція корисна для того, що мова призначена робити - як я вже говорив раніше, там є лише декілька доменів, де це корисно.

3

Він дуже погано підходить для більшості моделей програмування. Це буде представляти собою якусь повністю неконтрольовану дію на відстані, в якій можна було знищити значення сотень чи тисяч змінних та об'єктних полів, зробивши єдине завдання.


Тоді я б запропонував скласти правило про те, що зв'язана змінна, тобто ліва частина, не буде змінена іншим способом. І те, про що я говорю, - це лише один спосіб зобов’язання, а не двосторонній. Якщо ви стежите за моїм запитанням, ви можете побачити. Отже, жодна інша змінна не вплине.
Гульшан

Це не має значення. Кожен раз, коли ви пишете на aабо b, вам потрібно враховувати його вплив на кожне місце, яке ви sumвикористовуєте, і кожне прочитане sumвами місце, вам слід враховувати, що aі що bви робите. Для нетривіальних випадків це може ускладнитися, особливо якщо фактичний вираз, зв'язаний з, sumможе змінитися під час виконання.
jprete

Я рекомендую правило, що вираз зв'язування не може бути змінений, як тільки буде зроблено зв'язування. Навіть призначення буде неможливим. Це буде як напівконстанта, що колись пов'язана з виразом. Це означає, що коли сума прив’язана до + b, вона завжди буде + b, під час решти програми.
Гульшан

3

Я знаю, я відчуваю, що реактивне програмування може бути здоровим у середовищі Web2.0. Чому почуття? Ну, у мене є одна сторінка, яка в основному являє собою таблицю, яка весь час змінюється у відповідь на події onClick-комірки таблиці. А кліки клітинок часто означають зміну класу всіх комірок у комірці чи рядку; а це означає нескінченні петлі getRefToDiv () тощо) для пошуку інших споріднених комірок.

IOW, багато з написаних ~ 3000 рядків JavaScript, які я написав, не займаються лише пошуком об'єктів. Можливо, реактивне програмування могло б зробити це все за невеликі витрати; і при величезному скороченні рядків коду.

Що ви думаєте про це? Так, я помічаю, що мій стіл має багато функцій, що нагадують електронну таблицю.


3

Я думаю, що те, що ви описуєте, називається електронною таблицею:

A1=5
B1=A1+1
A1=6

... потім оцінюється B1повернення 7.

EDIT

Мова С іноді називають "портативною збіркою". Це імперативна мова, тоді як електронні таблиці тощо - це декларативні мови. Висловлювання B1=A1+1та очікування B1переоцінки при зміні A1, безумовно, є декларативним. Декларативні мови (які функціональні мови є підмножиною), як правило, вважаються мовами вищого рівня, оскільки вони далекі від того, як працює апаратне забезпечення.

У відповідній примітці мови автоматизації, як логіка сходів, зазвичай є декларативними. Якщо ви пишете ланцюжок логіки, який говорить про output A = input B OR input Cте, що збирається постійно переоцінювати це твердження, і Aможе змінюватися, коли Bабо Cзмінюється. Інші мови автоматизації, такі як діаграма функціональних блоків (які, можливо, вам знайомі, якщо ви використовували Simulink), також є декларативними та виконують постійно.

Деяке (вбудоване) обладнання для автоматизації запрограмоване на C, і якщо це система в режимі реального часу, вона, ймовірно, має нескінченний цикл, який повторно виконує логіку знову і знову, подібно до того, як виконується логіка сходів. У цьому випадку всередині вашого основного циклу ви можете написати:

A = B || C;

... і оскільки він виконується весь час, він стає декларативним. Aбуде постійно переоцінюватися.


3

C, C ++, Objective-C:

Блоки надають функцію прив’язки, яку ви шукаєте.

У вашому прикладі:

сума: = a + b;

ви встановлюєте sumвираз a + bу контексті, де aі bє існуючі змінні. Це можна зробити саме за допомогою "блоку" (він же закриття, також вираз лямбда) в C, C ++ або Objective-C з розширеннями 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

Це встановлює sumблок, який повертає суму a і b. __blockКлас зберігання специфікатор вказує на те, що aі bможе змінитися. З огляду на вищесказане, ми можемо запустити наступний код:

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: Оновлено
Thomas Eding
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.