We have seen that there are several ways to write a
count-words-region function. To write a
count-words-in-defun, we need merely adapt one of these
The version that uses a
while loop is easy to understand, so I
am going to adapt that. Because
count-words-in-defun will be
part of a more complex program, it need not be interactive and it need
not display a message but just return the count. These considerations
simplify the definition a little.
On the other hand,
count-words-in-defun will be used within a
buffer that contains function definitions. Consequently, it is
reasonable to ask that the function determine whether it is called
when point is within a function definition, and if it is, to return
the count for that definition. This adds complexity to the
definition, but saves us from needing to pass arguments to the
These considerations lead us to prepare the following template:
(defun count-words-in-defun () "documentation..." (set up... (while loop...) return count)
As usual, our job is to fill in the slots.
First, the set up.
We are presuming that this function will be called within a buffer
containing function definitions. Point will either be within a
function definition or not. For
count-words-in-defun to work,
point must move to the beginning of the definition, a counter must
start at zero, and the counting loop must stop when point reaches the
end of the definition.
beginning-of-defun function searches backwards for an
opening delimiter such as a ‘(’ at the beginning of a line, and
moves point to that position, or else to the limit of the search. In
practice, this means that
beginning-of-defun moves point to the
beginning of an enclosing or preceding function definition, or else to
the beginning of the buffer. We can use
place point where we wish to start.
while loop requires a counter to keep track of the words or
symbols being counted. A
let expression can be used to create
a local variable for this purpose, and bind it to an initial value of zero.
end-of-defun function works like
except that it moves point to the end of the definition.
end-of-defun can be used as part of an expression that
determines the position of the end of the definition.
The set up for
count-words-in-defun takes shape rapidly: first
we move point to the beginning of the definition, then we create a
local variable to hold the count, and finally, we record the position
of the end of the definition so the
while loop will know when to stop
The code looks like this:
(beginning-of-defun) (let ((count 0) (end (save-excursion (end-of-defun) (point))))
The code is simple. The only slight complication is likely to concern
end: it is bound to the position of the end of the definition
save-excursion expression that returns the value of point
end-of-defun temporarily moves it to the end of the
The second part of the
count-words-in-defun, after the set up,
The loop must contain an expression that jumps point forward word by
word and symbol by symbol, and another expression that counts the
jumps. The true-or-false-test for the
while loop should test
true so long as point should jump forward, and false when point is at
the end of the definition. We have already redefined the regular
expression for this, so the loop is straightforward:
(while (and (< (point) end) (re-search-forward "\\(\\w\\|\\s_\\)+[^ \t\n]*[ \t\n]*" end t)) (setq count (1+ count)))
The third part of the function definition returns the count of words
and symbols. This part is the last expression within the body of the
let expression, and can be, very simply, the local variable
count, which when evaluated returns the count.
Put together, the
count-words-in-defun definition looks like this:
(defun count-words-in-defun () "Return the number of words and symbols in a defun." (beginning-of-defun) (let ((count 0) (end (save-excursion (end-of-defun) (point)))) (while (and (< (point) end) (re-search-forward "\\(\\w\\|\\s_\\)+[^ \t\n]*[ \t\n]*" end t)) (setq count (1+ count))) count))
How to test this? The function is not interactive, but it is easy to
put a wrapper around the function to make it interactive; we can use
almost the same code as for the recursive version of
;;; Interactive version. (defun count-words-defun () "Number of words and symbols in a function definition." (interactive) (message "Counting words and symbols in function definition ... ") (let ((count (count-words-in-defun))) (cond ((zerop count) (message "The definition does NOT have any words or symbols.")) ((= 1 count) (message "The definition has 1 word or symbol.")) (t (message "The definition has %d words or symbols." count)))))
Let's re-use C-c = as a convenient keybinding:
(global-set-key "\C-c=" 'count-words-defun)
Now we can try out
count-words-defun: install both
count-words-defun, and set the
keybinding, and then place the cursor within the following definition:
(defun multiply-by-seven (number) "Multiply NUMBER by seven." (* 7 number)) ⇒ 10
Success! The definition has 10 words and symbols.
The next problem is to count the numbers of words and symbols in several definitions within a single file.