Calculating Big-O time and space complexity for functional languages
I'm thinking of using Ocaml for technical interviews in the future. However, I'm not sure how to calculate time and space complexity for functional languages. What are the basic runtimes for the basic higher level functions like map, reduce, and filter, and how do I calculate runtime and space complexity in general?
The time complexity of persistent recursive implementations is easy to infer directly from the implementation. In this case, the recursive definition maps directly to the recurrence relation. Consider the List.map function as it is implemented in the Standard Library: let rec map f = function |  ->  | a::l -> f a :: map f l The complexity is map(N) = 1 + map (N-1) thus it is O(N). Speaking of the space complexity it is not always that obvious, as it requires an understanding of tail-calls and a skill to see the allocations. The general rule is that in OCaml native integers, characters, and constructors without arguments do no allocate the heap memory, everything else is allocated in the heap and is boxed. All non-tail calls create a stack frame and thus consume the stack space. In our case, the complexity of the map in the stack domain is O(N), as it makes N non-tail calls. The heap-complexity is also O(N) as the :: operator is invoked N times. Another place, where space is consumed are closures. If a function has at least one free variable (i.e., a variable that is not bound to function parameters and is not in the global scope), then a functional object called closure is created, that contains a pointer to the code and a pointer to each free variable (also called the captured variable). For example, consider the following function: let rec rsum = function |  -> 0 | x :: xs -> List.fold_left (fun y -> x + y) 0 xs + rsum xs For each element of a list, this function computes a sum this element, with all consecutive elements. The naive implementation above is O(N) in the stack (as each step has two non-tail calls), O(N) in the heap size, as each step constructs a new closure (unless the compiler is clever enough to optimize it). Finally, it is O(N^2) in the time domain (rsum(N) = (N-1) + rsum(N-1)). However, it brings a question - should we take into account a garbage, that is produced by a computation? I.e., those values, that were allocated, during the computation, but are not referenced by it. Or those values, that are referenced only during a step, as in this case. So it all depends on the model of computation that you chose. If you will choose a reference counting GC, then the example above is definitely O(1) in the heap size. Hope this will give some insights. Feel free to ask questions, if something is not clear.
Erlang determine if record has a field
List option to list in OCaml
Some questions when reading “Why functional programming matters”
How to find double quotation marks in ML
Splitting array in erlang
OCaml visitor pattern
Why is OCaml still reasonable fast while constantly creating new things?
How to write a simple while true loop in Erlang for a receive call
Would like to swap elements in programming [closed]
OCaml : Simple function with conditionals doesn't work
NetLogo: How to implement `sentence-map` (aka `flatMap`)?
Erlang function calls - Why does it not work to call self() as a direct argument?
How to extract the instantiated variable in Isabelle?
Prove LISP is not functional [closed]
Why doesn't the set! function modify the original list in Scheme (r5rs)?
Differences between Derivable rules and Admissible rules?