past eventful years thick in the Python language standard of Comparisions chapter mentioned in
Also unlike C, expressions like a < b < c have the interpretation that is conventional in mathematics
In other words, to be written in the C language a < b && b < c
expressions can be written in Python a < b < c
. And, the standard also mentions
Comparisons can be chained arbitrarily, e.g., x < y <= z is equivalent to x < y and y <= z, except that y is evaluated only once (but in both cases z is not evaluated at all when x < y is found to be false).
This property is generally referred to as a short circuit. Therefore, 2 < 1 < (1 / 0)
will not raise an exception in Python, but instead return False
.
Python's less than sign can have short-circuit characteristics because it is not an ordinary function, but an operator supported by the language. In Common Lisp (hereinafter referred to as CL), the less than sign is just an ordinary function, just like the less than sign in Haskell is also a function. The difference is that CL's less than sign can accept more than two parameters
(< 1 2 3 -1) ; 结果为NIL
But it does not have short circuit characteristics
(< 1 2 3 -1 (/ 1 0)) ; 引发名为DIVISION-BY-ZERO的错误
If you want to simulate the less than sign with short-circuit characteristics, you must rely on the power of macros.
What kind of code do you want to generate
To write a macro, you must first imagine its syntax and what kind of code it will expand into. less-than
name this macro 060d6b463a03ab, and its syntax should be
(defmacro less-than (form &rest more-forms)
; TBC
)
There are many options for its expansion result. For example, (less-than 2 1 (/ 1 0))
can be expanded into the form and
with short circuit characteristics.
(and (< 2 1) (< 1 (/ 1 0)))
MAX
macro that calculates the maximum value of the two in the C language, the above expansion method will lead to repeated evaluation in some cases
(less-than 1 (progn (print 'hello) 2) 3)
Therefore, at least it should be expanded into a combination and
and let
(let ((g917 1)
(g918 (progn (print 'hello) 2)))
(and (< g917 g918)
(let ((g919 3))
(< g918 g919))))
If you want to expand into this structure, you can implement less-than
(defmacro less-than (form &rest more-forms)
(labels ((aux (lhs forms)
"LHS表示紧接着下一次要比较的、小于号的左操作数。"
(unless forms
(return-from aux))
(let* ((rhs (gensym))
(rv (aux rhs (rest forms))))
(if rv
`(let ((,rhs ,(first forms)))
(and (< ,lhs ,rhs)
,rv))
`(< ,lhs ,(first forms))))))
(cond ((null more-forms)
`(< ,form))
(t
(let ((lhs (gensym)))
`(let ((,lhs ,form))
,(aux lhs more-forms)))))))
Use the above input to verify whether it will cause repeated evaluation
CL-USER> (macroexpand-1 '(less-than 1 (progn (print 'hello) 2) 3))
(LET ((#:G942 1))
(LET ((#:G943 (PROGN (PRINT 'HELLO) 2)))
(AND (< #:G942 #:G943) (< #:G943 3))))
T
Optimize
Obviously less-than
can be optimized, just simply use recursive techniques.
(defmacro less-than (form &rest more-forms)
(cond ((<= (length more-forms) 1)
`(< ,form ,@more-forms))
(t
(let ((lhs (gensym))
(rhs (gensym)))
`(let ((,lhs ,form)
(,rhs ,(first more-forms)))
(and (< ,lhs ,rhs)
(less-than ,rhs ,@(rest more-forms))))))))
The expanded code is much shorter
CL-USER> (macroexpand-1 '(less-than 1 (progn (print 'hello) 2) 3))
(LET ((#:G955 1) (#:G956 (PROGN (PRINT 'HELLO) 2)))
(AND (< #:G955 #:G956) (LESS-THAN #:G956 3)))
T
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。