Списки (обычно, по крайней мере, я предполагаю, что может быть какой-то странный крайний случай, о котором я не знаю) хранятся непосредственно как их значения. Таким образом, ваша рекурсивная функция определенно выдаст список MyIp
с - они будут переоцениваться только в том случае, если вы сделали какую-то странную вещь, когда MyIp
переоценивается каждый раз, когда к ней обращаются. Например, когда функция вернется, у вас будет полностью оцененный список MyIp
с.
Однако есть одна небольшая проблема в том, что реализованная вами функция не особенно эффективна. Вместо этого я бы порекомендовал сделать это немного альтернативным способом:
let rec GetIpRangeRec (startIp: MyIp) (endIp: MyIp) (ipList: MyIp list) =
if startIp <= endIp then
GetIpRangeRec (startIp + 1) endIp (startIp :: ipList)
else
List.rev ipList
По сути, проблема в том, что каждый раз, когда вы используете оператор @
для добавления в конец списка, среда выполнения должна идти до конца списка, чтобы выполнить добавление. Это означает, что вы закончите итерацию по списку целую кучу раз. Вместо этого лучше просто добавить (т. Е. Добавить, но вперед), а затем перевернуть список непосредственно перед его возвратом. Это означает, что вам нужно пройти по списку только один раз, так как предварительное добавление всегда является операцией с постоянным временем (вы просто создаете новую запись списка с указателем на предыдущий фронт списка).
На самом деле, поскольку вы, вероятно, не хотите использовать предварительно предоставленный список за пределами вашей функции, я бы рекомендовал сделать это следующим образом:
let GetIpRange startIp endIp =
let rec GetIpRangeRec (start: MyIp) (end: MyIp) (ipList: MyIp list) =
if start <= end then
GetIpRangeRec (start + 1) end (start :: ipList)
else
List.rev ipList
GetIpRangeRec startIp endIp List.empty
(обратите внимание, что я не проверял это, поэтому он может работать не совсем идеально). Если вы хотите иметь возможность предварительно предоставить стартовый список, то вы можете просто придерживаться первого.
Кроме того, имейте в виду, что хотя списки обычно подходят для последовательного доступа, они ужасны для случайного доступа. Если вам нужно выполнить случайный поиск в списке, я бы порекомендовал позвонить по номеру List.toArray
, как только вы получите полный список обратно. Вероятно, не нужно беспокоиться, если вы просто будете последовательно итерировать по нему.
Я сделаю еще одно замечание: с точки зрения «полного путинского» функционального программирования ваша первая реализация может быть не полностью «функциональной», но единственная задействованная изменчивость скрыта внутри функции. То есть вы не изменяете ничего, что передается в функцию. Это прекрасно с точки зрения функциональной чистоты и может быть полезно для производительности. Помните, что F # сначала функциональный, а не ревностно-функциональный;)
РЕДАКТИРОВАТЬ: Просто подумал еще об одной вещи, которую я хотел бы добавить: я не знаю точно, как создаются ваши типы MyIp
, но если вы можете построить их из чисел, возможно, стоит взглянуть на использование понимание последовательности, например seq {1 .. 100}
, и затем добавление к map
для создания MyIp
s, например seq {1 .. 100} |> Seq.map makeIp |> Seq.toList
. Это был бы самый простой способ, но сработал бы, только если вы можете просто указать простой диапазон номеров.