Почему язык НЕ использует оценку короткого замыкания? - PullRequest
38 голосов
/ 18 сентября 2009

Почему язык НЕ использует Оценка короткого замыкания ? Есть ли польза от его использования?

Я вижу, что это может привести к проблемам с выступлениями ... это правда? Почему?


Соответствующий вопрос: Преимущества использования оценки короткого замыкания

Ответы [ 18 ]

40 голосов
/ 18 сентября 2009

Причины НЕ использовать оценку короткого замыкания:

  1. Потому что он будет вести себя по-разному и давать разные результаты, если ваши функции, свойства Gets или методы оператора имеют побочные эффекты. И это может вступать в противоречие с: A) Языковыми стандартами, B) предыдущими версиями вашего языка или C) предположениями по умолчанию для типичных пользователей ваших языков. Вот причины, по которым VB отказывается от короткого замыкания.

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

Я предполагаю, что вы спрашиваете об «автоматическом, неявном коротком замыкании, зависящем от порядка», что большинство разработчиков ожидают от C, C ++, C #, Java и т. Д. И VB, и SQL имеют способы явного сила короткое замыкание для конкретного заказа. Однако обычно, когда люди задают этот вопрос, это вопрос «Делай, что я имел в виду»; то есть они имеют в виду «почему это не делает то, что я хочу?», как, например, автоматически замыкается накоротко в том порядке, в котором я это написал.

7 голосов
/ 18 сентября 2009

Одно преимущество, о котором я могу подумать, - это то, что некоторые операции могут иметь побочные эффекты, которые вы можете ожидать.

Пример:

if (true || someBooleanFunctionWithSideEffect()) {
    ...
}

Но это, как правило, осуждается.

5 голосов
/ 18 сентября 2009

Ада не делает это по умолчанию. Чтобы вызвать оценку короткого замыкания, вы должны использовать and then или or else вместо and или or.

Проблема в том, что есть некоторые обстоятельства, когда это действительно тормозит. Если второе условие быстро вычисляется и первое условие почти всегда верно для «и» или «ложно» для «или», то дополнительная команда проверки ветвления является пустой тратой. Однако я понимаю, что с современными процессорами с предикторами ветвления это не так. Другая проблема заключается в том, что компилятор может знать, что половина second дешевле или может потерпеть неудачу, и может захотеть соответствующим образом изменить порядок проверки (чего не может быть сделано, если определено поведение при коротком замыкании) .

Я слышал возражения, что это может привести к неожиданному поведению кода в случае, когда второй тест имеет побочные эффекты. ИМХО, это только «неожиданно», если вы не очень хорошо знаете свой язык, но некоторые будут спорить об этом.

Если вам интересно, что дизайнеры языка говорят об этой проблеме, вот выдержка из Ada 83 (язык оригинала) Обоснование :

Операнды логического выражения такие как A и B могут быть оценены в Любой заказ. В зависимости от сложности термина B, это может быть больше эффективный (на некоторых, но не на всех машины) оценивать Б только тогда, когда Термин А имеет значение ИСТИНА. это Однако это решение по оптимизации взято компилятором, и это будет неверно полагать, что это оптимизация всегда делается. В других ситуации, которые мы можем выразить сочетание условий, где каждый состояние должно быть оценено (имеет смысл) только если предыдущий состояние удовлетворено. Обе эти все может быть сделано с коротким замыканием контрольные формы ...

В Алголе 60 можно добиться эффекта оценки короткого замыкания только использование условных выражений, так как полная оценка выполнена иначе. Это часто приводит к конструкции, которым надо следовать ...

Несколько языков не определяют, как логические условия должны быть оценены. Как следствие программы на основе оценки короткого замыкания не быть портативным Это явно иллюстрирует необходимость разделения логические операторы от короткого замыкания контрольные формы.

4 голосов
/ 18 сентября 2009

Билл сослался на вескую причину не использовать короткое замыкание, а прописать его более подробно: у высокопараллельных архитектур иногда возникают проблемы с разветвленными путями управления.

Возьмем, к примеру, архитектуру NVIDIA CUDA. Графические чипы используют архитектуру SIMT, что означает, что один и тот же код выполняется во многих параллельных потоках. Однако этот only работает, если все потоки каждый раз принимают одну и ту же условную ветвь. Если разные потоки принимают разные пути кода, оценка сериализуется - это означает, что преимущество распараллеливания теряется, потому что некоторые потоки должны ждать , в то время как другие выполняют альтернативную ветвь кода.

Короткое замыкание на самом деле включает в себя ветвление кода, поэтому операции короткого замыкания могут быть вредными для архитектур SIMT, таких как CUDA.

- Но, как сказал Билл, это аппаратное соображение. Что касается languages ​​, я бы ответил на ваш вопрос с громким no : предотвращение короткого замыкания не имеет смысл.

4 голосов
/ 18 сентября 2009

Я бы сказал, что 99 раз из 100 я бы предпочел быстродействующие операторы короткого замыкания.

Но я нашел две веские причины, по которым я не буду их использовать. (Кстати, мои примеры в C, где && и || являются короткими замыканиями, а & и | не являются.)

1.) Если вы хотите вызвать две или более функции в операторе if независимо от значения, возвращаемого первым.

if (isABC() || isXYZ()) // short-circuiting logical operator
    //do stuff;

В этом случае isXYZ () вызывается, только если isABC () возвращает false. Но вы можете захотеть, чтобы isXYZ () вызывался несмотря ни на что.

Итак, вместо этого вы делаете это:

if (isABC() | isXYZ()) // non-short-circuiting bitwise operator
    //do stuff;

2.) Когда вы выполняете логическую математику с целыми числами.

myNumber = i && 8; // short-circuiting logical operator

не обязательно совпадает с:

myNumber = i & 8; // non-short-circuiting bitwise operator

В этой ситуации вы можете получить разные результаты, потому что оператор короткого замыкания не обязательно оценивает все выражение. И это делает его в основном бесполезным для логической математики. Так что в этом случае я бы вместо этого использовал операторы без короткого замыкания (побитовые).

Как я намекал, эти два сценария действительно редки для меня. Но вы можете видеть, что существуют реальные причины программирования для обоих типов операторов. И, к счастью, большинство популярных языков сегодня имеют оба. Даже в VB.NET есть операторы короткого замыкания AndAlso и OrElse. Если у языка сегодня нет и того, и другого, я бы сказал, что он отстал и действительно ограничивает программиста.

4 голосов
/ 18 сентября 2009

Посмотрите на мой пример на На SQL Server короткое замыкание логического оператора , которое показывает, почему определенный путь доступа в SQL более эффективен, если логическое короткое замыкание не используется. В моем примере с блогом показано, как на самом деле использование логического короткого замыкания может нарушить ваш код, если вы предполагаете короткое замыкание в SQL, но если вы прочитаете аргумент , почему - это SQL, вычисляющий сначала правую часть, вы ' Вы увидите, что это правильно, и это приведет к значительному улучшению пути доступа.

3 голосов
/ 18 сентября 2009

Как натяжка:

Если вы хотите, чтобы язык был сверхбезопасным (ценой огромности), вы бы удалили eval с коротким замыканием. Когда для чего-то «безопасного» требуется переменное количество времени, Timing Attack может использоваться, чтобы связываться с ним. Eval короткого замыкания приводит к тому, что выполнение вещей занимает разное время, следовательно, пробивается отверстие для атаки. В этом случае, даже если не разрешить eval короткого замыкания, мы надеемся помочь в написании более безопасных алгоритмов (во всяком случае, во время атак).

3 голосов
/ 18 сентября 2009

Если вы хотите оценить правую сторону:

if( x < 13 | ++y > 10 )
    printf("do something\n");

Возможно, вы хотели, чтобы значение y было увеличено независимо от того, х <13. Однако хорошим аргументом против этого является то, что создание условий без побочных эффектов обычно является лучшей практикой программирования. </p>

2 голосов
/ 18 сентября 2009

Не то, чтобы я думал, что это происходит на любом языке сейчас, но было бы довольно интересно передать обе стороны операции различным потокам. Большинство операндов могут быть заранее определены, чтобы не мешать друг другу, поэтому они будут хорошими кандидатами для передачи различным процессорам.

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

Эй, это немного натянуто, но вы спросили: "Зачем нужен язык" ... а не "Почему язык".

2 голосов
/ 18 сентября 2009

Язык Luster не использует оценку короткого замыкания. В if-then-elses ветки then и else оцениваются в каждом тике, и одна считается результатом условного в зависимости от оценки условия.

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

В Luster пишем эквивалент

if (y <> 0) then 100/y else 100

- типичная ошибка новичка. Деление на ноль не исключается, потому что выражение 100 / y вычисляется даже в циклах, когда y = 0.

...