我一直在玩函数式语言(特别是F#),我真的很喜欢整个不可变/概念。但是,我对你如何用函数式语言表示有状态的东西感到有些迷茫。
例如,如何在函数式语言中重写以下内容? (任何功能语言都很好......只需要绕过它)
class state
{
int current_time;
bool is_completed() {
return current_time() - start_time > 30 seconds
}
double get_progress() {
return (current_time() - start_time) / 30 seconds
}
void start() {
start_time = current_time();
}
}
void main() {
state s;
s.start();
while(s.is_completed() == false) {
print s.get_progress();
}
print "finished";
}
答案 0 :(得分:8)
我对你如何用函数式语言表示有状态的东西感到有些迷茫。
最终,我们常用的计算机是有状态的东西。编程语言必须在某种程度上应对这一事实,或者放弃其主机的某些功能。
FP语言通过以下方式处理这一事实:
至于您的代码,您需要查看其中的第一个选项。重写你的函数以接受当前时间为参数。
答案 1 :(得分:8)
您的示例包含一些在函数式语言中有所不同的方法:
current_time
来获取当前时间,但这不是一个纯函数(它取决于某些全局变化状态)。在纯函数式语言(Haskell)中,这是不允许的(并且你必须使用monad),但大多数不纯的函数式语言(F#,OCaml)允许这样做。main
函数使用循环 - 通常不鼓励在函数式语言中使用循环(尽管有些人支持它们)。惯用的F#解决方案将处理第一个和最后一个点:
let currentTime() =
System.DateTime.Now
type State(startTime) =
static member Start() =
State(currentTime())
member x.IsCompleted =
(currentTime() - startTime).TotalSeconds > 30.0
member x.Progress =
(currentTime() - startTime).TotalSeconds / 30.0
let main() =
let s = State.Start()
let rec loop () =
if not s.IsCompleted then
printf "%A" s.Progress
loop ()
loop ()
printf "finished"
类型State
在永不改变其本地字段值的意义上是不可变的。它不是纯粹的功能,因为它取决于(改变)当前时间,但这不是F#中的问题(你只需要知道它)。如果你需要一些修改状态的方法(你不需要),那么该方法将返回State
的新实例(就像.NET string
)。
main
函数是使用递归而不是循环编写的 - 在这种情况下,它并不重要(循环在F#中也可以)。使用递归的关键是你可以将当前状态作为参数传递,并在进行递归调用时使用新实例(这实际上会在计算过程中改变当前状态)。
答案 2 :(得分:2)
我对F#了解不多,但据我所知,它与OCaml非常接近。所以这里有一些OCaml:
记录类型非常擅长。这是OCaml中的一些等效代码:
#load "unix.cma" ;;
type state = { start : float } ;;
let mystate = { start = Unix.time () } in
let rec check () =
if Unix.time () -. mystate.start > 30. then
print_endline "finished"
else
check ()
in
check () ;;
(由于某种原因,Unix时间是OCaml中的浮点数;如果它让你感觉更好,你可以转换为int32。)