Сравнивает ли Math.Min или Math.Max ​​короткое замыкание? - PullRequest
11 голосов
/ 18 января 2012

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

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

и

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

Поскольку Math.Max(y, z()) будет возвращать значение, по крайней мере, такое же, как y, если x Math.Min.

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

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

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

Ответы [ 5 ]

18 голосов
/ 18 января 2012

Как уже отмечали другие, компилятор ничего не знает о семантике 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))) 
10 голосов
/ 18 января 2012

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

5 голосов
/ 18 января 2012

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

3 голосов
/ 19 января 2012

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

Math.Max ​​() может быть легко встроен в CLR просто-в-время компилятора и оттуда мне было любопытно, может ли он дополнительно оптимизировать код таким образом, чтобы он был закорочен.

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

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

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

2 голосов
/ 18 января 2012

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

Порядок вычисления кода будет следующим: z (), Math.Max, x>...

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

...