Обычный способ записи - создать функцию с локальным состоянием, захваченным в замыкании:
let getNextAlias =
let count = ref 0
(fun () ->
count := !count + 1;
sprintf "t%d" (!count))
Тип getNextAlias
просто unit -> string
, и когда вы вызываете его несколько раз, он возвращает строки "t1", "t2", ... Это зависит от изменяемого состояния, но изменяемое состояние скрыто от пользователя .
Относительно того, можете ли вы сделать это без изменяемого состояния - простой ответ - НЕТ, потому что когда вы дважды вызываете чисто функциональную функцию с одним и тем же параметром, она должна возвращать один и тот же результат. Таким образом, вам нужно написать что-то со следующей структурой:
let alias, state1 = getNextAlias state0
printf "first alias %s" alias
let alias, state2 = getNextAlias state1
printf "second alias %s" alias
// ...
Как видите, вам нужно сохранить некоторое состояние и поддерживать его во всем коде. В F # стандартным способом решения этой проблемы является использование изменяемого состояния. В Haskell вы можете использовать State monad , которая позволяет скрыть прохождение состояния. Используя реализацию из этого вопроса , вы могли бы написать что-то вроде:
let getNextAlias = state {
let! n = getState
do! setState (n + 1)
return sprintf "t%d" n }
let program =
state {
let! alias1 = getNextAlias()
let! alias2 = getNextAlias()
// ...
}
execute progam 0 // execute with initial state
Это очень похоже на другие вычисления, такие как lazy
или seq
, на самом деле - вычисления в блоке state { .. }
имеют некоторое состояние, и вы можете выполнить их, указав начальное значение состояния. Однако, если у вас нет веских причин требовать чисто функционального решения, я бы предпочел первую версию для практического программирования на F #.