Since Push requires stacks to manipulate its data, we need an implementation of this data structure. There is of course a .NET implementation, however, it is not a “functional” data structure, in a sense that it is mutable. It is easy enough to implement our own, purely functional, immutable stack.
F# list is a logical choice for an underlying implementation. It derives from Seq (i.e. implements all IEnumerable flavors), has a useful length property which we would like to have for our stack as well:
[<StructuredFormatDisplay("{StructuredFormatDisplay}")>] type Stack<'a> = | StackNode of 'a list with member private t.StructuredFormatDisplay = if t.length = 0 then "()" else let str = t |> Seq.fold (fun st e -> st + e.ToString() + "; ") "(" str.Substring(0, str.Length - 2) + ")" member t.length = t |> Seq.length member internal t.asList = match t with StackNode(x) -> x member t.isEmpty = t.length = 0 interface IEnumerable<'a> with member x.GetEnumerator() = (x.asList |> List.toSeq).GetEnumerator() interface IEnumerable with member x.GetEnumerator() = (x.asList |> List.toSeq).GetEnumerator() :> IEnumerator
What remains is to implement basic operations, which are all static members of the Stack module.
let peek = function | StackNode([]) -> Unchecked.defaultof<'a> | StackNode(hd::tl) -> hd let pushStack hd tl = match tl with |StackNode(x) -> StackNode(hd::x) let pop = function | StackNode([]) -> Unchecked.defaultof<'a>, StackNode([]) | StackNode(hd::tl) -> hd, StackNode(tl)
These are pretty straightforward. pop function has a slight quirk: we would like to return both the value of the head of the stack as well as “the rest” of the stack. So the return type in this case is a tuple.
Another slight irregularity: stack functions work on any kinds of instantiated stack object. pop would return something even if the stack is empty. This is done to conform to the Push language “absolute robustness” rule. A syntactically correct program is guaranteed to execute.
A noteworthy function is popMany. Its implementation shows off a cool feature of functional languages: tail call elimination. Any recursive function, where a call to itself produces a return value which is then immediately returned, is called tail recursive. F# optimizes tail recursive functions by handling recursive calls on the same stack frame as the tail call itself (rather than creating a new stack frame for each recursive call). This normally means significant performance advantages, not to mention eliminates any danger of running out of stack.
let popMany n (stack : Stack<'a>) = let noopReturn = [], stack if stack.length = 0 then noopReturn else match n with | x when x <= 0 || stack.length < n -> noopReturn | x -> let rec popManyTail n st acc = match n with | 0 -> acc // exit recursion | _ -> let hd, tl = List.head st, List.tail st popManyTail (n - 1) tl (hd::fst acc, StackNode(tl)) //keep track of intermediate results popManyTail n stack.asList ([],empty) // call the actual worker function
It is very easy to write tail-recursive functions by accumulating results(when tail recursion is possible). Here is the pattern:
- Identify recursion exit (in this case – nothing more needs to be popped off the stack).
- Carry the accumulated result as part of the method signature: in this case, the function keeps track of the results of its intermediate calls by adding these results to the accumulator argument of popManyTail, which it eventually returns.
The accumulator is of type: ‘a list * Stack<‘a>. The list of popped values (in reverse order of their position on the stack) is combined with the remainder of the stack in a tuple. - Call the actual tail recursive function with the seed value of the accumulator
In our implementation, we handle the edge cases of popMany first and then write the tail recursive function without worrying about anything going wrong.
Reblogged this on A Journey Through Code.