There are two existing loops in the code: one while loop and one for loop. Let’s replace each of them with their Functional Programming equivalent - recursion. A recursion is a function that calls itself.

The findShortestPath() implementation can be replaced with:

fun findShortestPath ( start : String , end : String ): Int { recurseFindShortestPath ( start , end ) return evaluatedPaths . getValue ( end ) }

The recurseFindShortestPath() function is recursive - as its name implies. A recursive function offers different branches, the most important one being the stop branch. In our case, the stop branch here is when the node has reached the end .

private fun recurseFindShortestPath ( node : String , end : String ): String { return if ( node == end ) end // else ... }

The other branch contains the same code as before, with one small change: we compute the next node by calling the same function with the updated node.

private fun recurseFindShortestPath ( node : String , end : String ): String { return if ( node == end ) end else { // ... same code here val nextNode = evaluatedPaths . minBy { it . value } ?. key ?: throw RuntimeException ( "Map was empty" ) recurseFindShortestPath ( nextNode , end ) } }

The biggest issue with recursion is when calls pile upon one another on the stack. Because it’s based on real-world hardware, the stack has a limit, however high it is. When it’s reached, then the infamous StackOverflowError occurs. To avoid that, the compiler can actually generate the equivalent non-recursive bytecode.

This requires:

The function to be marked with tailrec

The last instruction of the function should be the recursive call