Поведение больше чем (и эквивалент) для Варианта - PullRequest
0 голосов
/ 07 сентября 2018

Я работаю над чем-то для задачи обработки изображений (здесь это не очень актуально), и я наткнулся на поведение типа Option F #, которое меня удивило в отношении выполнения сравнений больше (>). Я не смог найти ничего, что прямо объясняло бы то, что я должен ожидать (подробнее об этом ниже) в переполнении стека, документах F # или более широкой сети.

Конкретная часть, на которую я смотрю, выглядит примерно так:

let sort3Elems (arr: byte option []) = 
    if arr.[0] > arr.[1] then swap &arr.[0] &arr.[1]
    if arr.[1] > arr.[2] then swap &arr.[1] &arr.[2]
    if arr.[0] > arr.[1] then swap &arr.[0] &arr.[2]

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

let arr: byte option [] = Array.zeroCreate 4;;
val arr : byte option [] = [|None; None; None; None|]

> arr.[0] <- Some(127uy);;
val it : unit = ()

> arr.[2] <- Some(55uy);;
val it : unit = ()

> arr.[0] > arr.[2];;
val it : bool = true

> arr.[0] < arr.[2];;
val it : bool = false

> arr.[0] < arr.[1];;
val it : bool = false

> arr.[0] > arr.[1];;
val it : bool = true

> arr.[2] > arr.[1];;
val it : bool = true

> arr.[3] > arr.[1];;
val it : bool = false

> arr.[3] < arr.[1];;
val it : bool = false

> arr.[3] > arr.[1];;
val it : bool = false

Мне кажется, что, по сути, операторы сравнения всегда должны возвращать true (false), когда спрашивают, является ли Some больше (меньше), чем None, два None всегда возвращают false, а два Some одного и того же содержащегося типа сравнивают содержащий значения (при условии, что их можно сравнить, я представляю). Это имеет смысл, хотя я был удивлен.

Желая подтвердить это, я попытался отследить что-то, что могло бы объяснить поведение, которое я должен ожидать, но я не смог найти ничего, что отвечало бы этой теме. Страница опций в Руководстве по MS F # не упоминает об этом, и я не смог найти ничего в таких местах, как F #, для развлечения и получения прибыли. Мне даже не удалось найти страницу о Option где-либо в документации MS API ... Просмотр источника для Option в репозитории F # GitHub ничего мне не говорит. Лучшее, что я смог найти, было сообщение в блоге Дона Сайма от лет назад, которое фактически не отвечало на мой вопрос. Было несколько вопросов о переполнении стека, в которых обсуждались темы, относящиеся к операторам сравнения или типам Option, но я не нашел ничего, что касалось комбинации этих двух.

Итак, мой вопрос заключается в том, дает ли выполнение сравнений типа «больше» или «меньше» тип Option, результаты, о которых я размышлял выше? Я предполагаю, что это достаточно распространенное знание среди программистов на F #, но для меня это было новостью. Как вспомогательный / связанный вопрос, знает ли кто-нибудь, куда я мог бы / должен был обратиться за дополнительной информацией? Спасибо.

1 Ответ

0 голосов
/ 07 сентября 2018

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

8.15.4 Поведение сгенерированных сравнений для реализаций

Для типа T поведение сгенерированной реализации System.IComparable.CompareTo имеет вид следующим образом:

  • Преобразовать аргумент y в тип T Если преобразование не выполнено, вызовите InvalidCastException.
  • Если T является ссылочным типом, а y равен нулю, вернуть 1.
  • Если T является структурой или типом записи, вызывайте FSharp.Core.Operators.compare для каждой соответствующей пары полей x и y в порядке объявления и возврата первого ненулевого результата.
  • Если T является типом объединения, сначала вызовите FSharp.Core.Operators.comp в индексе случаев объединения для двух значений, а затем на каждой соответствующей паре полей х и у для данных, переносимых случай объединения. Вернуть первый ненулевой результат.

Как задокументировано в последнем случае, случай для варианта сначала сравнивает случаи. У None индекс меньше Some, поэтому значение None всегда будет меньше любого значения Some. Если случаи совпадают, то None = None и Some n с Some m сравниваются на основе n и m.

...