По сравнению с Math.Min или Math.Max ​​короткое замыкание?

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

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

и

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

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

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

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

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

Ответ 1

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

Ответ 2

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

Ответ 3

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

Ответ 4

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

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

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

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

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

Ответ 5

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

Порядок оценки кода будет: z(), Math.Max, x > ...

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