Это кажется вполне поддающимся рекурсивной реализации:
mergeQueues :: Queue a -> Queue a -> Queue a
mergeQueues qa qb =
merge qa qb emptyQueue
where
merge qa qb qr
| isEmpty qa = append qb qr
| otherwise = move (merge qb) qa qr
append q qr
| isEmpty q = qr
| otherwise = move append q qr
move f q qr =
let (val,q') = pop q
in f q' (push val qr)
Обратите внимание, что мы просто переворачиваем очереди взад-вперед по мере их повторения, чтобы переключаться между ними, пока одна из них не станет пустой, и в этот момент мы просто добавляем одну к другой.
Обратите внимание, что, хотя это больше, чем императивная версия, заданная ribond, она выполняет минимальное количество проверок isEmpty
. Если вы не против сделать столько проверок, сколько он делает в немного более оптимизированной версии (присвоение значений isEmpty переменным для повторного использования ниже), вы можете удалить функцию append
и просто вместо этого вызывать merge
, после добавления начального теста к merge
, который проверяет, чтобы обе очереди были пустыми, и, если это так, завершил рекурсию.
Для тех, кто не знаком с Haskell, мы переходим, чтобы переместить следующую вызываемую функцию (это функциональное программирование высшего порядка); в случае append
это просто добавление, в случае move
это «частично примененная» функция перемещения: она получает первый параметр, qb
, примененный до того, как мы вызовем move
, а затем move
применяет два других параметра.
Это звучит как разумная задача, с которой можно столкнуться в повседневном деловом программировании. Однако, если это функция домашней работы, я предлагаю вам внимательно изучить, как работает приведенный выше код, и я думаю, что вы чему-то научитесь.
Также есть вероятность, что в приведенном выше коде есть ошибка; Доказать, что это работает (или найти ошибку) было бы отличным упражнением.