2012-01-18 1 views
11

При сравнении с минимумом или максимумом двух чисел/функций, является ли короткое замыкание на C#, если случай истинен для первого и будет означать истину для второго? Конкретные примеры этих случаевПо сравнению с Math.Min или Math.Max ​​короткое замыкание?

if(x < Math.Max(y, z())) 

и

if(x > Math.Min(y, z())) 

Так как Math.Max(y, z()) возвратит значение по крайней мере, столь же большой, как у, если х < у, то нет никакой необходимости, чтобы оценить г(), который может занять некоторое время. Аналогичная ситуация с Math.Min.

Я понимаю, что они оба могли быть переписаны по линии

if(x < y || x < z()) 

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

+3

Предположим, что вы вызываете 'if (x> XYZ (y, z()))' Как компилятор может узнать результат XYZ? Макс, Мин, Среднее или что-нибудь еще? –

+0

@ L.B Отличный момент, я так не думал об этом. – yoozer8

+0

Кроме того, выражение «короткое замыкание» может быть не эквивалентным, в зависимости от того, как «Мин» и «Макс» обрабатывают NaN. – dan04

ответ

18

Как указывали другие, компилятор ничего не знает о семантике Min или Max, которая позволила бы ему нарушить правило, которое аргументы будут оцениваться до вызова метода.

Если вы хотите, чтобы написать свой собственный, вы могли бы сделать это достаточно легко:

static bool LazyLessThan(int x, int y, Func<int> z) 
{ 
    return x < y || x < z(); 
} 

, а затем вызвать его

if (LazyLessThan(x, y, z)) 

или

if (LazyLessThan(x, y,()=>z())) 

Или по этому вопросу:

static bool LazyRelation<T>(T x, T y, Func<T> z, Func<T, T, bool> relation) 
{ 
    return relation(x, y) || relation(x, z()); 
} 
... 
if (LazyRelation(x, y,()=>z, (a,b)=> a < b))) 
+0

Этот последний пример был бы замечательным, если бы можно было добавить аргумент по умолчанию: static bool LazyRelation (T x, T y, Func x, Func Отношение = (a, b) => a zmbq

+3

+1 Отличный ответ, но будем честными - все это более удобочитаемо/поддерживаемо, чем 'if (x Yuck

+0

Я бы сказал, что это менее читаемо, потому что он заменяет базовую идиому функцией, определение которой нужно читать, чтобы понять код. –

10

Нет, это не короткое замыкание, и z() всегда будет оцениваться. Если вам требуется короткое замыкание, вы должны переписать, как вы это сделали.

+4

Причина этого заключается в том, что время выполнения не имеет никакого представления о том, что такое эффекты 'Max()' и 'Min()'. ** Вы делаете **, что позволяет вам переписывать более совершенным образом. – Yuck

2

Нет, это не короткое замыкание, по крайней мере, на уровне компилятора C#. Math.Min или Math.Max - это два обычных вызова статических методов, и компилятор не будет оптимизировать в этом смысле.

Порядок оценки кода будет: г(), Math.max, х> ...

Если вы действительно хотите, чтобы убедиться, проверить код IL.

5

Math.Min() и Math.Max() - методы, подобные любым другим. Они должны быть оценены, чтобы вернуть значение, которое будет использоваться в качестве второго аргумента в сравнении. Если вы хотите короткое замыкание, вам нужно будет написать условие, используя оператор ||, как вы продемонстрировали.

3

(Ничего особенно нового не добавить, но я решил поделиться результатами теста я побежал на него.)

Math.max() может быть легко встраиваются, просто в срок компилятора СЬК и оттуда мне было любопытно, может ли она еще больше оптимизировать код таким образом, чтобы он был закорочен.

Итак, я взломал микрообъект, который оценивает два выражения по 1,000,000 раз каждый. Для z() я использовал функцию, которая вычисляет Fib (15) с использованием рекурсивного метода. Вот результаты выполнения двух:

x < Math.Max(y, z()) : 8097 ms 
x < y || x < z()  :  29 ms 

Я предполагаю, что CLR не будет преобразовывать код любым способом, который предотвращает вызовы метода от выполнения, так как он не знает (и не проверяет чтобы увидеть, если) рутина имеет какие-либо побочные эффекты.

Смежные вопросы