F#中的不可变数据结构

时间:2018-03-05 14:52:55

标签: f# stack immutability

我正在尝试使用其内置泛型类型在 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} }}。截至目前,堆栈最多只能有一个元素,并且在向元素推送元素时它不会增长 - 弹出时同样不会收缩。

任何人都可以给我一个关于如何重写结构/以不同方式思考它的暗示吗?

谢谢!

2 个答案:

答案 0 :(得分:5)

您可以通过简单地将列表作为堆栈来以更实用的方式执行此操作。您可以将pushpop替换为函数,而不是方法。

// 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}}}}

您还可以选择包含valuenext字段的班级,并使用null作为最后一个元素。

重要的是如何使用这些结构。使用不可变数据结构的方法是使用递归函数而不是循环。

创建一个尾递归函数,它访问每个元素并对每个元素执行一个函数是fold函数,与forEach相当。