Reputation: 408
I am trying to implement a very trivial stack in F# using its built-in generic type. Coming from the imperative paradigm it can sometimes be hard to imagine how to avoid mutability.
What I have so far is a simple data structure with a push and pop operator:
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
My idea was to make the Stack hold an S of 'a list
where we modify the list with the cons ::
operator to not mutate the list S
, but to replace it with S'
. As of now, the stack can at most have one element, and it doesn't grow when pushing elements to it – likewise doesn't shrink when popped.
Can anyone give me a hint on how rewrite the structure/think about it differently?
Thank you!
Upvotes: 1
Views: 472
Reputation: 4488
The built-in immutable list is already a valid Stack structure. The Push Operation is ::
and you get the last item with Pattern Matching like let (first,rest) = list
If you want to create a Stack from Scratch. Here are some implementations.
1)
a) a stack is either empty
b) or a value and a reference to the previous Stack.
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
You also could choose a record as the underlying data-structure.
type Stack<'a> = {
Value: 'a option
Next: Stack<'a> option
}
This way, a Stack with three elements looks like.
{Value=Some 3; Next=
{Value=Some 2; Next=
{Value=Some 1; Next=
{Value=None; Next=None}}}}
You also could choose a class with a value
and next
field and use null
for the last element.
The important thing is how to work with those structures. The way to work with immutable data-structures is by using recursive functions instead of looping.
Creating a tail-recursive function that visits each element and executes a function on every element is the fold
function, comparable to a forEach
.
Upvotes: 0
Reputation: 9258
You could do this in a more functional way by simply having a list act as the stack. Instead of methods you can make push
and pop
be functions instead.
// 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 = []
Upvotes: 5