На мой взгляд, это гораздо проще понять с помощью небольшой вспомогательной функции и анализа случаев путем сопоставления с образцом.
(Мой совет - освоиться с шаблонами и анализом случаев, избегая условных выражений и функций выбора. Это гораздо проще рассуждать об одной вещи за раз, чем помнить целую цепочку логик c и деструктуризации.)
Переписав свой код таким образом, можно получить следующее:
fun oldest_of (d, d') = if is_older (d, d') then d else d'
fun oldest [] = NONE
| oldest (d::ds) = case oldest ds of
NONE => SOME d
| SOME d' => SOME (oldest_of (d, d'))
То есть ,
- Если список пуст, самая старая дата отсутствует
- В противном случае найдите самую старую дату в хвосте ввода;
- Если его нет, первый элемент ввода должен быть самым старым
- В противном случае выберите самый старый из них и первый элемент ввода
Теперь (надеюсь) очевидно, что случай NONE
в рекурсии может произойти, только если хвост, ds
, пуст - то есть, если вход имеет ровно один элемент.
Давайте поднимем это в своем собственном случае:
fun oldest [] = NONE
| oldest [d] = SOME d
| oldest (d::ds) = SOME (oldest_of (d, valOf (oldest ds)))
Это очень похоже на определение самой старой даты:
- Если нет дат, нет самой старой даты
- Если есть только одна дата, то это самая старая дата
- Если есть хотя бы две даты, это самая старая из первой даты и самая старая из остальных
, который не требует большого индуктивного мышления.