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


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 # . Надеюсь, что это будет там в будущем.


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

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


Тогда я бы предложил создать правило, что связанная переменная, то есть левая часть, не будет изменена никаким другим способом. И то, о чем я говорю, это просто односторонний, а не двусторонний. Если вы следите за моим вопросом, вы можете видеть. Таким образом, никакая другая переменная не будет затронута.
— Гюльшан

Это не имеет значения. Каждый раз , когда вы пишете 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: Обновлено
— Томас Эдинг
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.