# trying to make a self-modifying function [duplicate]

I'm trying to make a function that returns a list of perfect numbers:

A perfect number is a positive integer that is equal to the sum of its proper positive divisors

For instance, 28 is perfect because its factors are 1, 2, 4, 7, 14 and 1 + 2 + 4 + 7 + 14 = 28.

My initial code was:

(defun factors (n)
"Return a list of the factors of N, except N itself."
(let ((factors (list 1))
(i 2))
(while (<= i (/ n 2))
(if (zerop (% n i))
(setq factors (cons i factors)))
(setq i (1+ i)))
factors))

(defun is-perfect (n)
"Return t is N is a perfect number, nil otherwise."
(and (/= 1 n)
(let* ((factors (factors n))
(factors-sum (seq-reduce '+ factors 0)))
(= n factors-sum))))

(defun perfect-numbers (n)
"Return the perfect numbers smaller than or equal to N."
(seq-filter 'is-perfect (number-sequence 2 n)))


It works, but gets noticeably slow for 10000. Remembering that I recently had trouble with a self modifying function I thought I'd try to exploit this to optimize perfect-numbers. The idea is to keep the list of the perfect numbers we already computed. I tried this:

(defun perfect-numbers (n)
"Return the perfect numbers smaller than or equal to N."
(let* ((perfect-numbers '(6)) ; my hope was that this list would get updated each time I call the function
(highest (car (last perfect-numbers)))
(new-perfect-numbers (seq-filter 'is-perfect (number-sequence (1+ highest) n))))
(setq perfect-numbers (append perfect-numbers new-perfect-numbers))
(seq-filter (apply-partially '>= n) perfect-numbers)))


However in this case, perfect-numbers does not seems to be updated, and I don't understand why:

ELISP> (perfect-numbers 1000)
(6 28 496)
ELISP> (symbol-function 'perfect-numbers)
(lambda
(n)
"Return the perfect numbers smaller than or equal to N."
(let*
((perfect-numbers
'(6)) ; the list did not get updated :(
(highest
(car
(last perfect-numbers)))
(new-perfect-numbers
(seq-filter 'is-perfect
(number-sequence
(1+ highest)
n))))
(setq perfect-numbers
(append perfect-numbers new-perfect-numbers))
(seq-filter
(apply-partially '>= n)
perfect-numbers)))

• Little-known fact: "self-modifying code" is a synonym for "maddening to debug". Better use explicit memoization. May 23, 2019 at 17:26

This is because you modify the variable binding, not the data structure. append creates a fresh list, while nconc modifies its argument. Replace (setq perfect-numbers (append perfect-numbers new-perfect-numbers)) with (nconc perfect-numbers new-perfect-numbers) and you will get the behavior you think you want.

I must advise against this though because

1. modifying quoted lists is not a good idea.
2. You have to seed your variable with (6) which is poor style.

I suggest a more standard way to encapsulate the data (see Lexical-Binding):

;; -*- lexical-binding:t -*-
(let ((perfect-numbers ()))
(defun perfect-numbers (n)
"Return the perfect numbers smaller than or equal to N."
(let ((new-perfect-numbers
(seq-filter 'is-perfect (number-sequence
(if perfect-numbers
(1+ (car (last perfect-numbers)))
2)
n))))
(setq perfect-numbers (append perfect-numbers new-perfect-numbers))
(seq-filter (apply-partially '>= n) perfect-numbers))))


Now (perfect-numbers 100) returns (6 28) as expected.

• Thank you, nconc does solve the problem. However the better solution you provide does not work for me, I get *** Eval error *** Symbol’s value as variable is void: perfect-numbers. I did set lexical-binding to t in ielm. May 23, 2019 at 15:28
• Oh, it works if I copy paste the code in the interpreter actually, but not if I put it in my source file and then do C-x C-e or M-x eval-buffer. May 23, 2019 at 15:30
• Nevermind: things work well if I do M-: (setq lexical-binding t), and then re-evaluate the form. Setting it only in ielm does not seem to work. May 23, 2019 at 15:38
• My understanding is that having ;; -*- lexical-binding:t -*- as a file-local variable in the library which defines your function is the only dependable way to enable lexical binding. (Although ielm and M-: and *scratch* will default to lexical binding starting from Emacs 27.) May 23, 2019 at 21:42