list
уже хвост вашего ввода; вам не нужно (и не нужно) оборачивать его в другой список.
shortest (x:y:list) = shortest $ (if length x > length y then y else x) : list
На каждом шаге вопрос только в том, какой элемент, x
или y
, вы удаляете из ввода для рекурсивного вызова.
Другой способ, который не требует двух базовых случаев, - это просто сравнить заголовок списка с результатом повторения в хвосте.
shortest [] = []
shortest (x:xs) = let s = shortest xs
in if length s < length x then s else x
Наконец, кортежи сравниваются лексикографически, поэтому вы также можете обойтись без явной рекурсии, пометив каждый список его длиной, найдя наименьшее помеченное значение, а затем извлекая исходный список.
shortest = snd . minimum . map (\x -> (length x, x))
Используя Control.Arrow
, вы можете записать аргумент в map
как (length &&& id)
.
Предостережение для последнего подхода: поскольку списки также сравниваются лексикографически, конечный результат, если у вас есть несколько списков с наименьшей длиной, будет зависеть от того, как сравниваются сами значения списка. Первые два примера, напротив, стабильны; первый такой короткий список возвращается.
Даниэль Вагнер указывает на лучшее решение для использования minimum
, заключающееся в том, чтобы обернуть каждый элемент в значение Arg
, позволяющее сравнивать два списка только по их длине, без учета списков ' содержание.
import Data.Semigroup
shortest xs = x where Arg _ x = minimum [Arg (length x) x | x <- xs]
Arg
- это в основном двухэлементный тип продукта, который только использует первый элемент для своего экземпляра Ord
, в отличие от (,)
, который использует оба.