I 've been procrastinating, playing with Lisp, but I can't solve this problem after all. In this case, I started to shake my mind.
The author first complained and compared the differences between lisp macros and other languages. Formally speaking, the so-called standard macros are the same as class libraries such as c and java, it is the stuff that has been completed and verified in advance and encapsulated in the compiling/virtual environment for coder to use (Save the repetitive wheel ). Of course, in lisp, macros are not just so simple, ~ Take care of him (please, this is just a beginner's tutorial !) This chapter mainly introduces some commonly used macros.
Conditional macros
The most common estimation is "if:
Java code
(If condition then-form [else-form])
(If (> 2 3) "Yup" "Nope") => "Nope"
(If (> 2 3) "Yup") => NIL
(If (> 3 2) "Yup" "Nope") => "Yup"
It's really easy to understand, but if only supports single-action statements, that is, "no matter whether the conditions are true or not, you can only do one thing, not one group of things ". In a real environment, the use area is naturally greatly affected. To this end, a special operator PROGN is introduced.
Java code
(If (spam-p current-message)
(Progn
(File-in-spam-folder current-message)
(Update-spam-database current-message )))
Well, it looks good, but what if there is a more concise solution:
Java code
(When (spam-p current-message)
(File-in-spam-folder current-message)
(Update-spam-database current-message ))
When is also a standard macro. Even if it is not, definition is not troublesome:
Java code
(Defmacro when (condition & rest body)
'(If, condition (progn, @ body )))
Its symbiotic brother unless is to make a inverse judgment on the condition:
Java code
(Defmacro unless (condition & rest body)
'(If (not, condition) (progn, @ body )))
It seems that there is no magic, that is, there is no mysterious magic and no logic of the dizzy man. Right! Perhaps "the greatest truths to the simplest" can be used as an explanation.
If when solves the problem of execution items, macro cond solves the problem of condition items:
Java code
(Cond
(Test-1 form *)
.
.
(Test-N form *))
(Cond (a (do-x ))
(B (do-y ))
(T (do-z) 'theoretically, the last one is default.
When performing conditional operations, it is usually used with some basic operators: and, or, not (not is actually a function ).
Java code
(Not nil) => T
(Not (= 1 1) => NIL
(And (= 1 2) (= 3 3) => NIL 'returns the first nil. If no NIL is returned
(Or (= 1 2) (= 3 3) => t' returns if the first T is met, and if not, nil
Loop macro
The simplest two cyclic macros are dolist and dotimes:
Java code
(Dolist (var list-form)
Body-form *)
(Dolist (x' (1 2 3) (print x ))
1
2
3
NIL
CL-USER> (dolist (x' (1 2 3) (print x) (if (evenp x) (return )))
1
2
NIL 'truncates the loop through return
-----------------------------
(Dotimes (var count-form)
Body-form *)
(Dotimes (I 4) (print I ))
0
1
2
3
NIL
A loop list and a progressive loop based on values are all customized cycle macros. And they all have a "parent macro" (which can be called this) -- do, which also reduces the ease of use due to its super flexibility (Wanjin oil:
Java code
(Do (variable-definition *)
(End-test-form result-form *)
Statement *)
Variable-definition --> (var init-form step-form)
To put it simply, you need to define the initial values of the loop in the retriable-definition, and the step interval (the default step if no value exists). When the end-test-form is reached, execute result-form, the return value is used as the return value of the entire loop. While statement is a loop body. Here is an example to prove do's flexibility:
Java code
(Do (n 0 (1 + n ))
(Cur 0 next)
(Next 1 (+ cur next )))
(= 10 n) cur ))
It is actually the legendary "Fibonacci series ". In this example, the cyclic body does not seem to be important, but the constructor itself is important. As mentioned above, flexibility and ease of use are being lost. The Arc in this example is impressive. Let's take a look at the comparison:
Java code
(Do (I 0 (1 + I )))
(> = I 4 ))
(Print I ))
(Dotimes (I 4) (print I ))
It is really "safflower needs green leaves "~
In some cases, loop conditions are not required:
Java code
(Do () 'although there are no conditions, the location must be kept
(> (Get-universal-time) * some-future-date *))
(Format t "Waiting ~ % ")
(Sleep 60 ))
What we will introduce next is the controversial "strong" loop macro In The lisp field: loop.
Although dolist and dotimes are simple, the coverage is not wide, and do is too flexible (= complicated ?), Then the seemingly more intermediate loop has a reason, even if many people disagree with him (and many support ).
It looks simple:
Java code
(Loop
Body-form *)
It is not good to use:
Java code
(Loop
(When (> (get-universal-time) * some-future-date *)
(Return ))
(Format t "Waiting ~ % ")
(Sleep 60 ))
Compared with do:
Java code
(Do (nums nil) (I 1 (1 + I )))
(> I 10) (nreverse nums ))
(Push I nums) ==> (1 2 3 4 5 6 7 8 9 10
(Loop for I from 1 to 10 collecting I) ==> (1 2 3 4 5 6 7 8 9 10)
Also to implement a "Fibonacci series ":
Java code
(Loop for I below 10
And a = 0 then B
And B = 1 then (+ B)
Finally (return ))
Although it uses loop-specific symbols like for and below, it does seem much more refreshing and more natural language-oriented. This is the reason why loop is surrounded by humans. It is also the point where opponents are arrogant. "It is not lisp at all." My personal opinion is, if you think of it, you can use it. It is exactly "a great road to Rome "~ Of course, the loop here is only the tip of the iceberg, and the subsequent sections will be detailed.
The standard macros provided by the lisp compiler are not just a few, but just a few common ones. In the following sections, we will introduce the custom macros in depth. Are you ready?
(To be continued)