Один хороший общий подход к таким вопросам состоит в том, чтобы выписать ваш алгоритм в форме слова или псевдокода, а затем, как только вы выясните свой алгоритм, преобразуйте его в F #. В этом случае, когда вы хотите суммировать списки, это будет выглядеть так:
Первым шагом в выяснении алгоритма является тщательное определение спецификаций проблемы. Я хочу, чтобы алгоритм суммировал мой пользовательский тип списка. Что точно это значит? Или, чтобы быть более конкретным, что именно это означает для двух различных типов значений (A и B), которые могут иметь мой тип настраиваемого списка? Что ж, давайте посмотрим на них по одному. Если список имеет тип A, то это представляет пустой список, поэтому мне нужно решить, какой должна быть сумма пустого списка. Наиболее разумным значением для суммы пустого списка является 0, поэтому правило гласит: «Если список имеет тип A, то сумма равна 0». Теперь, если список имеет тип B, то что означает сумма этого списка? Ну, сумма списка типа B будет его значением int плюс сумма подсписка.
Итак, теперь у нас есть правило "суммы" для каждого из двух типов, которые может иметь list1
. Если A, сумма равна 0. Если B, сумма равна (значение + сумма подсписка). И это правило переводит почти дословно в код F #!
let rec sum (lst : list1) =
match lst with
| A -> 0
| B (value, sublist) -> value + sum sublist
Несколько вещей, которые я хочу отметить об этом коде. Во-первых, одна вещь, которую вы могли или не могли видеть раньше (поскольку вы, кажется, начинающий F #), это ключевое слово rec
. Это необходимо, когда вы пишете рекурсивную функцию: из-за внутренних деталей того, как реализован синтаксический анализатор F #, если функция собирается вызывать себя, вы должны объявить это заранее, когда вы объявляете имя и параметры функции. Во-вторых, это не лучший способ написания sum
функции, потому что она на самом деле не является хвостовой рекурсивной, что означает, что она может вызвать исключение StackOverflowException, если вы попытаетесь суммировать действительно действительно длинный список. На этом этапе изучения F # вам, возможно, пока не стоит беспокоиться об этом, но в конечном итоге вы освоите полезную технику для превращения не хвостовой рекурсивной функции в хвостовую рекурсивную. Он включает в себя добавление дополнительного параметра, обычно называемого «аккумулятор» (и иногда пишется acc
для краткости), и правильно рекурсивная версия вышеуказанной функции sum
выглядела бы так:
let sum (lst : list1) =
let rec tailRecursiveSum (acc : int) (lst : list1) =
match lst with
| A -> acc
| B (value, sublist) -> tailRecursiveSum (acc + value) sublist
tailRecursiveSum 0 lst
Если вы уже в точке, где вы можете это понять, прекрасно! Если вы еще не достигли этой точки, добавьте этот ответ в закладки и вернитесь к нему, как только вы изучите хвостовую рекурсию, потому что эта техника (превращение не хвостовой рекурсивной функции в хвостовую рекурсивную с использованием внутренней функция и параметр аккумулятора) - очень ценный инструмент, имеющий множество приложений в программировании на F #.