Next: Several defuns, Previous: Syntax, Up: Words in a defun [Contents][Index]
count-words-in-defun
FunctionWe 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 versions.
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 function.
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.
The 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 beginning-of-defun
to place point where we wish to start.
The 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.
The end-of-defun
function works like beginning-of-defun
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 looping.
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 by a
save-excursion
expression that returns the value of point after
end-of-defun
temporarily moves it to the end of the definition.
The second part of the count-words-in-defun
, after the set up, is the
while
loop.
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 count-words-example
:
;;; 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-in-defun
and count-words-defun
, and set the
keybinding. Then copy the following to an Emacs Lisp buffer (like, for
instance, *scratch*), place the cursor within the definition, and use
the C-c = command.
(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.
Next: Several defuns, Previous: Syntax, Up: Words in a defun [Contents][Index]