We should also note that a more efficient way to write goto-percentis this:
(defun goto-percent (pct)
(interactive "nPercent: ")
(goto-char (/ (* pct (point-max)) 100)))
11.2.2 Control Structures
We already saw that the whilefunction acts as a control structure like similar statements in other languages. There are two other important control structures in Lisp: ifand cond.
The iffunction has the form:
(if condition true-case false-block )
Here, the condition is evaluated; if it is non- nil, true-case
is evaluated; if nil, false-block
is evaluated. Note that true-case
is a single statement whereas false-block
is a statement block; false-block
is optional.
As an example, let's suppose we're writing a function that performs some complicated series of edits to a buffer and then reports how many changes it made. We're perfectionists, so we want the status report to be properly pluralized, that is to say "made 53 changes" or "made 1 change." This is a common enough programming need that we decide to write a general-purpose function to do it so that we can use it in other projects too.
The function takes two arguments: the word to be pluralized (if necessary) and the count to be displayed (which determines whether it's necessary).
(defun pluralize (word count)
(if (= count 1)
word
(concat word "s")))
The condition in the ifclause tests to see if countis equal to 1. If so, the first statement gets executed. Remember that the "true" part of the iffunction is only one statement, so prognwould be necessary to make a statement block if we wanted to do more than one thing. In this case, we have the opposite extreme; our "true" part is a single variable, word. Although this looks strange, it is actually a very common Lisp idiom and worth getting used to. When the condition block is true, the value of wordis evaluated, and this value becomes the value of the entire ifstatement. Because that's the last statement in our function, it is the value returned by pluralize. Note that this is exactly the result we want when countis 1: the value of wordis returned unchanged.
The remaining portion of the ifstatement is evaluated when the condition is false, which is to say, when counthas a value other than 1. This results in a call to the built-in concatfunction, which concatenates all its arguments into a single string. In this case it adds an "s" at the end of the word we've passed in. Again, the result of this concatenation becomes the result of the ifstatement and the result of our pluralizefunction.
If you type it in and try it out, you'll see results like this:
(pluralize "goat" 5)
"goats"
(pluralize "change" 1)
"change"
Of course, this function can be tripped up easily enough. You may have tried something like this already:
(pluralize "mouse" 5)
"mouses"
To fix this, we'd need to be able to tell the function to use an alternate plural form for tricky words. But it would be nice if the simple cases could remain as simple as they are now. This is a good opportunity to use an optional parameter. If necessary, we supply the plural form to use; if we don't supply one, the function acts as it did in its first incarnation. Here's how we'd achieve that:
(defun pluralize (word count &optional plural)
(if (= count 1)
word
(if (null plural)
(concat word "s")
plural)))
The "else" part of our code has become another ifstatement. It uses the nullfunction to check whether we were given the pluralparameter or not. If pluralwas omitted, it has the value niland the nullfunction returns tif its argument is nil. So this logic reads "if bwas missing, just add an sto word; otherwise return the special pluralvalue we were given."
This gives us results like this:
(pluralize "mouse" 5)
"mouses"
(pluralize "mouse" 5 "mice")
"mice"
(pluralize "mouse" 1 "mice")
"mouse"
A more general conditional control structure is the condfunction, which has the following form:
(cond
( condition1 statement-block1 )
( condition2 statement-block2 )
...)
Java and Perl programmers can think of this as a sequence of if then else if then else if . . . , or as a kind of generalized switch statement. The conditions are evaluated in order, and when one of them evaluates to non- nil
, the corresponding statement block is executed; the condfunction terminates and returns the last value in that statement block. [77]
We can use condto give a more folksy feel to our hypothetical status reporter now that it's pluralizing nicely. Instead of reporting an actual numeric value for the number of changes, we could have it say no , one , two , or many as appropriate. Again we'll write a general function to do this:
( defun how-many (count)
(cond ((zerop count) "no")
((= count 1) "one")
((= count 2) "two")
(t "many")))
The first conditional expression introduces a new primitive Lisp function, zerop. It checks whether its argument is zero, and returns t
(true) when it is. So when countis zero, the condstatement takes this first branch, and our function returns the value no. This strange function name bears a little explanation. It is pronounced "zero-pee" and is short for "zero predicate." In the realm of mathematical logic from which Lisp evolved, a predicate is a function that returns true or false based on some attribute of its argument. Lisp has a wide variety of similar predicate functions, with structurally related names. When you run into the next one, you'll understand it. (Of course, you might now expect the nullfunction we introduced in the previous example to be called " nilp" instead. Nobody's perfectly consistent.)
The next two conditional expressions in the condstatement check if countis 1 or 2 and cause it to return "one" or "two" as appropriate. We could have written the first one using the same structure, but then we'd have missed out on an opportunity for a digression into Lisp trivia!
The last conditional expression is simply the atom t(true), which means its body is executed whenever all the preceding expressions failed. It returns the value many. Executing this function gives us results like these:
Читать дальше