在学习了大约半年的OCaml之后,我仍然在functional programming和imperative programming上苦苦挣扎。
它不是关于的,而是关于using list or array的设计。
例如,如果我要为用户编写stack,应该以functional或imperative的方式呈现它吗?
stack应该有一个名为pop的函数,这意味着将最后一个元素返回给用户并从堆栈中删除它。所以,如果我以stack的方式设计functional,那么对于pop,我应该返回一个元组(last_element, new_stack),对吗?但我觉得很难看。
同时,我觉得functional方式在函数式编程中更自然。
那么,我该如何处理这种设计问题呢?
编辑
我看到了stack的源代码,它们定义的类型如下:
type 'a t = { mutable c : 'a list }好的,在内部标准库使用不可变的list,但是将它封装在一个可变的记录中。
我理解这一点,因为这样,对于用户来说,它总是一个堆栈,因此不需要一个元组返回到客户端。
但是,这并不是一种功能性的方式,对吗?
发布于 2013-07-10 11:20:40
可变结构有时更有效,但它们不是持久的,这在各种情况下都很有用(主要用于回溯失败的计算)。如果不可变接口在可变接口上没有或很少的性能开销,那么您绝对应该更喜欢不变的接口。
发布于 2013-07-10 12:58:04
在功能上(即不发生变化),可以使用头/尾而不是pop来像List一样定义它,也可以像建议的那样,让API通过返回一个元组来处理状态更改。这与州一级的单元组的建立方式相比较。
因此,处理堆栈的状态(例如通过递归)是父作用域的责任,在这种情况下,堆栈与列表完全一样,或者部分责任通过元组加载到API中。
下面是一个快速尝试(假装知道O‘’Caml语法):
module Stack =
struct
type 'a stack = 'a list
let empty _ = ((), [])
let push x stack = ((), x::stack)
let pop (x::stack) = (x, stack)
| pop _ = raise EmptyStack
end然后,一个用例将是:
let (_, st) = Stack.empty ()
let (_, st) = Stack.push 1 Stack.empty
let (_, st) = Stack.push 2 st
let (_, st) = Stack.push 3 st
let (x, st) = Stack.pop st与其显式处理元组,您可能希望一直隐藏在st上传递,并发明一个操作符,使以下语法成为可能:
let (x, st) = (Stack.empty >>= Stack.push 1 >>=
Stack.push 2 >>= Stack.push 3 >>= Stack.pop) []如果你能让这个操作员,你已经重新发明了国家单元化。:)
(因为上面的所有函数都将状态作为它的最后一个参数,所以它们可以部分应用。要对此进行扩展,请参阅下面的重写,这样可以更清楚地了解正在发生的事情,但更难理解。
let (x, st) = (fun st -> Stack.empty st >>= fun st -> Stack.push 1 st
>>= fun st -> Stack.push 2 st
>>= fun st -> Stack.push 3 st
>>= fun st -> Stack.pop) []至少,这是处理状态和不可变数据结构的一种惯用方法。
https://stackoverflow.com/questions/17569219
复制相似问题