A typical way is for the non-variable state to be passed along the call stack, and the auxiliary functions will take the current state, returning a new state for the “fake” mutation.
Perhaps (albeit quite suboptimal) Sudoku Solver:
;;; Use a list of 81 integers to represent a sudoku board, ;;; each number 1-9 represents itself, 0 represents a blank (defun sudoku-solver (board) (cond ((notany
This will automatically back off until a solution is found. I skipped hiding the demo using the support code, which will turn it from the demo into the actual working code.
source share