我正在尝试使用其内置泛型类型在 F#中实现一个非常简单的堆栈。从命令式范式来看,有时很难想象如何避免可变性。
到目前为止我所拥有的是一个简单的数据结构,其中包含推送和 pop 运算符:
type Stack<'a> =
| Empty
| S of 'a list
with
member L.Push x =
match L with
| Empty | S ([]) -> S ([x])
| S (V) -> S (x :: V)
member L.Pop =
match L with
| Empty | S ([]) -> failwith "Error: Stack is empty"
| S (v::_) -> v
end
我的想法是让堆栈保持S of 'a list
我们使用cons ::
运算符修改列表,以便不改变列表S
,而是将其替换为{{1} }}。截至目前,堆栈最多只能有一个元素,并且在向元素推送元素时它不会增长 - 弹出时同样不会收缩。
任何人都可以给我一个关于如何重写结构/以不同方式思考它的暗示吗?
谢谢!
答案 0 :(得分:5)
您可以通过简单地将列表作为堆栈来以更实用的方式执行此操作。您可以将push
和pop
替换为函数,而不是方法。
// Returns the new stack
let push item stack = item :: stack
// Returns (item, newStack) tuple or throws if stack is empty
let pop stack =
match stack with
| [] -> failwith "Stack is empty"
| item :: newStack -> item, newStack
// Example usage
let stack = []
let populatedStack = push "hello" stack
// populatedStack = ["hello"]
let item, emptiedStack = pop populatedStack
// item = "hello"
// emptiedStack = []
答案 1 :(得分:0)
内置的不可变列表已经是一个有效的Stack结构。推送操作为::
,您可以使用let (first,rest) = list
如果要从Scratch创建堆栈。以下是一些实现。
1)
a)堆栈是空的
b)或前一个堆栈的值和引用。
type Stack<'a> =
| Empty
| Value of 'a * Stack<'a>
module Stack =
let isEmpty = function
| Empty -> true
| Value _ -> false
let empty = Empty
let push x stack = Value (x, stack)
let pop (Value (x, stack)) = x, stack
let stk =
Stack.empty
|> Stack.push 1
|> Stack.push 2
|> Stack.push 3
let rec loop stack =
if Stack.isEmpty stack
then ()
else
let first, rest = Stack.pop stack
printfn "First: %d" first
loop rest
loop stk
您还可以选择记录作为基础数据结构。
type Stack<'a> = {
Value: 'a option
Next: Stack<'a> option
}
这样,具有三个元素的Stack看起来就像。
{Value=Some 3; Next=
{Value=Some 2; Next=
{Value=Some 1; Next=
{Value=None; Next=None}}}}
您还可以选择包含value
和next
字段的班级,并使用null
作为最后一个元素。
重要的是如何使用这些结构。使用不可变数据结构的方法是使用递归函数而不是循环。
创建一个尾递归函数,它访问每个元素并对每个元素执行一个函数是fold
函数,与forEach
相当。