Ответ 1
Вы получаете этот результат, потому что это выражение:
min 1 2 -5
разбирает, как если бы это было в скобках, как это:
(min 1 2) -5
что так же, как это:
1 -5
что так же, как это:
1 - 5
что, конечно, -4.
В Haskell применение функций является наиболее жестко связанной операцией, но она не является жадной. На самом деле, даже такое, казалось бы, простое выражение, как min 1 2
фактически приводит к двум отдельным вызовам функций: функция min
сначала вызывается с одним значением, 1; возвращаемое значение этой функции - новая анонимная функция, которая будет возвращать меньшее значение между 1 и единственным аргументом. Затем эта анонимная функция вызывается с аргументом 2 и, конечно, возвращает 1. Итак, более точная версия вашего кода с круглыми скобками выглядит так:
((min 1) 2) - 5
Но я не собираюсь все разрушать так далеко; в большинстве случаев тот факт, что то, что выглядит как вызов функции с несколькими аргументами, на самом деле превращается в серию нескольких вызовов функций с одним аргументом, является деталью реализации, которая может быть использована вручную. Иногда это важно знать, но большую часть времени вы можете игнорировать это и просто представить, что функции действительно принимают несколько аргументов.
Таким образом, чтобы найти минимум три значения, вам нужно объединить два вызова в min
(на самом деле четыре вызова в соответствии с приведенной выше логикой, но опять же, размахивая рукой):
min (min 1 2) (-5)
-5
вокруг -5
необходимы для того, чтобы -
интерпретировалось как отрицание префикса вместо вычитания инфикса; без них у вас та же проблема, что и у исходного кода, только на этот раз вы попросите Haskell вычесть число из функции и получите ошибку типа.
В более общем смысле, вы можете позволить Haskell создать цепочку за вас, применив к списку складку, которая может содержать столько чисел, сколько вам нужно:
foldl1 min [1, 2, -5]
(Обратите внимание, что в синтаксисе литерального списка запятая и квадратная скобка отделяют -5
, делая это явно не операцией вычитания, поэтому здесь вам не нужны скобки.)
Вызов foldl1
списка foldl1
означает: "возьмите первые два элемента списка и вызовите забаву для них. Затем возьмите результат этого вызова и следующий элемент списка и вызовите забаву для этих двух значений. Затем возьмите результат этого вызова и следующий элемент списка... "И так далее, пока не останется списка, после чего значение последнего вызова fun возвращается исходному вызывающему.
Для конкретного случая min
, для вас уже определена сложенная версия: minimum
. Таким образом, вы могли бы также написать выше, таким образом:
minimum [1, 2, -5]
Это ведет себя так же, как мое решение foldl1
; в частности, оба выдадут ошибку, если получит пустой список, а если получит одноэлементный список, они вернут этот элемент без изменений, даже не вызывая min
.
Спасибо JohnL за напоминание о существовании minimum
.