Ответ 1
Это сложное взаимодействие между оптимизацией компилятора F # и оптимизацией стандартной библиотеки .NET.
Во-первых, F # пытается оптимизировать вашу программу. Когда типы известны во время компиляции, а типы примитивны и сопоставимы, вызов compare
компилируется только для сравнения. Поэтому сравнение символов в вашем примере будет выглядеть как if 'A' < 'G' then -1 elif 'A' > 'G' then 1 else 0
.
Но когда вы переносите вещь в общий метод, вы удаляете информацию о типе. Теперь типы являются обобщенными, компилятор не знает, что они char
. Поэтому компилятор вынужден вернуться к вызову HashCompare.GenericComparisonIntrinsic
, который в свою очередь вызывает IComparable.CompareTo
аргументы.
А теперь угадайте, как IComparable
реализован в типе char
? Он просто вычитает значения и возвращает результат. Серьезно, попробуйте это на С#:
Console.WriteLine( 'A'.CompareTo('G') ); // prints -6
Обратите внимание, что такая реализация IComparable
технически не является ошибкой. Согласно документации, он не должен возвращать только [-1,0,+1]
, он может возвращать любое значение, пока его знак правильный. Мое лучшее предположение заключается в том, что это также делается для оптимизации.
F # документация для compare
не указывает это вообще. Он просто говорит "результат сравнения" - переходите к тому, что должно быть: -)
Если вы хотите, чтобы ваша функция compute
возвращала только [-1,0,+1]
, это можно легко достичь, выполнив функцию inline
:
let inline compute xs ys = Seq.zip xs ys |> Seq.map ((<||) compare)
Теперь он будет расширяться на сайте вызова, где известны типы, и может быть вставлен оптимизированный код. Имейте в виду, что, поскольку в документах поведение [-1,0,+1]
не гарантируется, оно может исчезнуть в будущем. Поэтому я бы предпочел не полагаться на это.