Elisp 04: iteration

Time:2021-6-10

Last chapter:variable

Iteration, also known as loop, represents a program that runs repeatedly, and its state can change in the process of each iteration.

Based on recursive function, the iterative process can be simulated. For example, the following program

(defun princ\' (x)
  (princ x)
  (princ "\n"))

(defun current-line ()
  (buffer-substring (line-beginning-position) (line-end-position)))

(defun every-line ()
  (if (= (point) (point-max))
      (princ "")
    (progn 
      (princ\' (current-line))
      (forward-line 1)
      (every-line))))

(progn
  (find-file "foo.md")
  (every-line))

Elisp interpreter in the last expression of the above program(every-line)When evaluated, it turns toevery-lineEach expression in the function definition is evaluated, but when the elisp interpreterevery-lineExpressions are encountered in the definition of(every-line)And it had to do it againevery-lineEvaluates each expression in the definition of. The process starts over and over againevery-lineWhen evaluating the definition of,princ\'Will continue to output the current line of text, andforward-lineIt keeps moving the insertion point to the beginning of the next line. Therefore, the above program solves the problem of reading each line of text in the current buffer and outputting it to the terminal.

Let’s live recursively. Just as we have longevity, elisp’s recursion depth to functions is limited.every-lineThis function can only be evaluated repeatedly by the elisp interpreter at mostmax-lisp-eval-depthTimes`

max-lisp-eval-depthIs the global variable of the elisp interpreter, which defines the recursive depth of the function. use

(princ\' max-lisp-eval-depth)

You can see its value, on my machine, the result is 800, which means the aboveevery-lineFunction can only be evaluated 800 times by the elisp interpreter. This also means that if there are more than 800 lines of text in the current buffer,every-lineThe definition of the function causes the elisp interpreter to crash and stop working. Its last words are

Lisp nesting exceeds ‘max-lisp-eval-depth’

In theory, if the elisp interpreter canevery-lineThis tail recursive form of the function to be optimized, you can let yourself endlessly trapped in the rightevery-lineIn the process of evaluating the definition of. This optimization is called tail recursive optimization.

However, the elisp interpreter does not have the ability to optimize tail recursion, so it must provide iterative syntax. The most common iteration syntax used in elisp programming iswhileExpression, the usage is as follows

(while logical expression)
  A program)

If the logical expression evaluates tot, elisp will be executed repeatedlywhileOtherwise, the elisp interpreter willnilAs a result of the evaluation, thewhileThe evaluation of the expression. That meanswhileThe expression evaluates to eithernil, or the elisp interpreter’s endless process of evaluating it. in fact,whileIt doesn’t matter what the evaluation result of the expression is, but how it expresses the change of the running state of the program and the response of the program.

be based onwhileGrammar, you can put the aboveevery-lineFunction redefined as

(defun every-line ()
  (while (< (point) (point-max))
    (princ\' (current-line))
    (forward-line 1)))

Now, you don’t have to worry about too many rows in the current buffer, unless you don’t have enough memoryevery-lineIt’s also more concise. Kill two birds with one stone, but if I don’t use recursive function to simulate the iterative process first, it’s hard for me to feel happy at the moment.

Similarly, the previous chapter redefined the list inversion function

(defun reverse-list (x)
  (let ((y '()))
    (defun reverse-list\' (x)
      (if (null x)
          y
        (progn
          (setq y (cons (car x) y))
          (reverse-list\' (cdr x)))))
    (reverse-list\' x)))

It can also be rewritten aswhileedition:

(defun reverse-list (x)
  (let ((y '()))
    (while (not (null x))
      (setq y (cons (car x) y))
      (setq x (cdr x)))
    y))

Among them,notIs elisp’s logical negation function. It should be noted that in the above code, local variablesyAt the end of the function definition, it is the result of the function evaluation. Because,yIt’s also an s expression that elisp can evaluate. If the last line of the above function definition does notySowhileThe evaluation result of the expressionnilIs the result of the function.

With iteration, is it necessary to reuse recursive functions?

be necessary.

Some tree structure creation and traversal, such as binary tree or multi tree, using recursive function, not only natural, but also very concise code, and usually do not need to worry about the limit of recursive depth. Take the height balanced binary tree as an example, the default value is 800max-lisp-eval-depthIt is enough, because the height balanced binary tree with the number of leaf nodes as high as $2 ^ {800} $is almost impossible to have practical significance.

Finally, remember that iteration is linear recursion.

Next chapter:Text matching

Recommended Today

The road of high salary, a more complete summary of MySQL high performance optimization in history

preface MySQL for many linux practitioners, is a very difficult problem, most of the cases are because of the database problems and processing ideas are not clear. Before the optimization of MySQL, we must understand the query process of MySQL. In fact, a lot of query optimization work is to follow some principles so that […]