Почему привязка не является встроенной функцией в большинстве языков?

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;

Это означает размещение инструкции привязки в качестве присваиваний после каждой инструкции, изменяющей значения любой переменной, содержащейся в выражении справа. После этого будет выполнено обрезание избыточных инструкций (или оптимизация сборки после компиляции).

Итак, почему это не поддерживается изначально в большинстве языков. Особенно в С-семействе языков?

Обновить:

С разных точек зрения, я думаю, что я должен определить это предложенное «обязательное» более точно -

  • Это один из способов связывания. Только сумма связана с a + b, а не наоборот.
  • Область привязки является локальной.
  • После того как привязка установлена, ее нельзя изменить. То есть, если сумма связана с a + b, сумма всегда будет a + b.

Надеюсь, идея прояснилась.

Обновление 2:

Я просто хотел эту функцию P # . Надеюсь, что это будет там в будущем.

Gulshan
источник
14
Возможно, потому что любой разработчик компилятора, который пытался добавить эту функцию в C, был пойман и расстрелян.
Пит Уилсон
Я говорю об односторонней (справа налево) привязке, а не двусторонней. Привязка всегда будет влиять только на одну переменную.
Гюльшан
2
Что бы это ни стоило, такая программная точка зрения находится на подъеме: реактивное программирование. То, что вы описываете, также воплощено в программах для работы с электронными таблицами, таких как Excel, которые, по сути, связывают данные (или реактивное программирование) на стероидах.
Майк Розенблюм
11
Это не может быть признаком «большинство» языков программирования, но это особенность наиболее популярных языков программирования: Excel.
Йорг Миттаг,
2
Благодаря деревьям выражений это уже возможно в C #. Я писал об этом вчера: happynomad121.blogspot.com/2013/01/…
HappyNomad

Ответы:

9

Вы путаете программирование с математикой. Даже функциональное программирование не является полностью математическим, хотя оно заимствует много идей и превращает их во что-то, что может быть выполнено и использовано для программирования. Императивное программирование (которое включает в себя большинство языков на основе C, примечательными исключениями являются 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) Все имеет определенную стоимость сложности и ценность. Это имеет довольно высокую стоимость и, по-моему, сравнительно небольшую ценность, за исключением нескольких доменов, так что это не первое, что я бы добавил к своему языку, если бы он не был специально предназначен для этих доменов.
Почему бы не иметь еще один инструмент в нашей коробке? Когда инструмент появится, люди будут им пользоваться. Вопрос. Если какой-либо язык, например C или C ++, захочет реализовать эту функцию и спросить ваше мнение, скажите ли вы: «Не делайте этого. Потому что это будет слишком сложно для вас, и люди будут с этим сталкиваться».
Гюльшан,
@Gulshan: Относительно (3): это не всегда (не сказать, редко) так просто, как в вашем примере. Поместите его в глобальную область, и вам вдруг понадобится оптимизация времени соединения. Затем добавить динамическое связывание, и вы даже не можете сделать что - нибудь на compiletime. Вам нужен очень умный компилятор и очень умная среда выполнения, включая JIT, чтобы сделать это хорошо. Также учитывайте количество заданий в каждой нетривиальной программе. Ни ты, ни я не можем написать эти компоненты. Есть максимум несколько человек, которые могут и заинтересованы в этой теме. И у них могут быть дела поважнее.
@Gulshan: Я бы попросил их не добавлять функции в невероятно сложного зверя, который уже есть в C ++, или я бы попросил их не пытаться использовать C для чего-то большего, чем системное программирование (что не является одной из областей, где это очень полезно ). Кроме того, я за интересные новые функции, но только тогда, когда осталось достаточно бюджета на сложность (многие языки всегда исчерпали свой), и эта функция полезна для того, для чего предназначен язык - как я уже говорил, Есть только несколько доменов, где это полезно.
3

Очень плохо подходит для большинства моделей программирования. Это будет представлять собой неконтролируемое действие на расстоянии, в котором можно уничтожить значение сотен или тысяч переменных и полей объекта, сделав одно присваивание.

jprete
источник
Тогда я бы предложил создать правило, что связанная переменная, то есть левая часть, не будет изменена никаким другим способом. И то, о чем я говорю, это просто односторонний, а не двусторонний. Если вы следите за моим вопросом, вы можете видеть. Таким образом, никакая другая переменная не будет затронута.
Гюльшан
Это не имеет значения. Каждый раз , когда вы пишете aили b, вы должны рассмотреть его влияние на каждом месте , которое sumиспользуется, и каждое место , которое вы читаете sumвы должны учитывать , что aи bделают. Для нетривиальных случаев это может осложниться, особенно если фактическое выражение, связанное с, sumможет измениться во время выполнения.
jprete
Я бы порекомендовал правило, что выражение привязки не может быть изменено, как только привязка сделана. Даже назначение будет невозможно. Это будет как полупостоянная однажды связанная с выражением. Это означает, что если сумма связана с a + b, она всегда будет a + b в течение остальной части программы.
Гюльшан
3

Вы знаете, у меня есть это мучительное чувство, что реактивное программирование может быть круто в среде Web2.0. Почему чувство? Ну, у меня есть одна страница, которая в основном представляет собой таблицу, которая постоянно меняется в ответ на события onClick ячейки таблицы. А клики по ячейкам часто означают изменение класса всех ячеек в столбце или строке; и это означает бесконечные циклы getRefToDiv () и тому подобное, для поиска других связанных ячеек.

Итак, многие из написанных мной ~ 3000 строк JavaScript ничего не делают, кроме как находят объекты. Может быть, реактивное программирование может сделать все это за небольшую плату; и при огромном сокращении строк кода.

Что вы, ребята, думаете об этом? Да, я заметил, что моя таблица имеет много функций, похожих на электронные таблицы.

Пит Уилсон
источник
3

Я думаю, что вы описываете, называется Spreadsheet:

A1=5
B1=A1+1
A1=6

... затем оценивая B1результаты 7.

РЕДАКТИРОВАТЬ

Язык C иногда называют «портативной сборкой». Это императивный язык, тогда как электронные таблицы и т. Д. Являются декларативными языками. Говорить 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:

Блоки обеспечивают обязательную функцию, которую вы ищете.

В вашем примере:

сумма: = а + б;

Вы устанавливаете 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: Обновлено
Томас Эдинг