Я бы прежде всего начал использовать сопоставление с образцом.
areListsEqual :: Eq a => [a] -> [a] -> Bool
areListsEqual [ ] [ ] = True
areListsEqual [ ] _ = False
areListsEqual _ [ ] = False
areListsEqual (x:xs) (y:ys) = x == y && areListsEqual xs ys
Обратите внимание, насколько это более читабельно, если исключить head
и tail
.
charlieSort :: Eq a => [[a]] -> [[a]]
charlieSort [ ] = []
charlieSort [x ] = [x]
charlieSort xs@(first:second:theRest)
| areListsEqual xs wip = wip
| otherwise = charlieSort wip
where
swapPairIfNeeded a b
| length a >= length b = [second,first]
| otherwise = [first,second]
modifiedPair = swapPairIfNeeded first second
wip = take 1 modifiedPair ++ charlieSort (drop 1 modifiedPair ++ theRest)
Я изменил if
- then
- else
на ограждение для немного улучшенной читаемости
(YMMV). Вместо проверки того, что в списке есть как минимум два элемента с
вызов length
мы используем сопоставление с образцом, что также позволяет нам назвать
first
, second
, theRest
напрямую. Шаблон name @ pattern
оба
сопоставляет ввод с pattern
, а называет весь ввод как name
.
Теперь я хочу не использовать take
и drop
для извлечения двух элементов.
modifiedPair
, поэтому последние две строки заменяются на
[shorter,longer] = swapPairIfNeeded first second
wip = [shorter] ++ charlieSort ([longer] ++ theRest)
где вы могли бы написать последнюю строку как
wip = shorter : charlieSort (longer : theRest)
если вы предпочитаете. Но зачем swapPairIfNeeded
возвращать shorter
и
longer
из списка first
и second
в списке ? Почему бы не использовать
пара, как
swapPairIfNeeded a b
| length a >= length b = (second,first)
| otherwise = (first,second)
(shorter,longer) = swapPairIfNeeded first second
? В большинстве случаев лучше использовать кортежи для фиксированного числа
значения (возможно, различных типов) и использовать списки для переменного числа
значения (обязательно одного типа). Но кажется странным, что
swapPairIfNeeded
сравнивает свои аргументы a
и b
, но затем возвращает
first
и second
в любом случае. В этом случае вместо того, чтобы позволить ему вернуться a
и b
в паре, вместо этого я удалю swapPairIfNeeded
:
(shorter,longer)
| length first >= length second = (second,first)
| otherwise = (first,second)
«разворачивает» тело swapPairIfNeeded
в определение
(shorter,longer)
.
Так что теперь код charlieSort
выглядит как
charlieSort :: Eq a => [[a]] -> [[a]]
charlieSort [ ] = []
charlieSort [x ] = [x]
charlieSort xs@(first:second:theRest)
| areListsEqual xs wip = wip
| otherwise = charlieSort wip
where
(shorter,longer)
| length first >= length second = (second,first)
| otherwise = (first,second)
wip = shorter : charlieSort (longer : theRest)
Наконец, я должен отметить, что charlieSort
на самом деле не реализует
пузырьковая сортировка, потому что рекурсивный вызов charlieSort
будет не только
сделать один «всплывающий» проход по списку, но также полностью отсортировать список
longer : theRest
, так что все, что нужно сделать после этого рекурсивного вызова
(перед возвратом на один уровень выше), возможно, прослужит shorter
к его
законное место.