Common lisp: is there a less painful way to input

2019-03-29 14:28发布

问题:

I enjoy common lisp, but sometimes it is really painful to input simple math expressions like

a(8b^2+1)+4bc(4b^2+1)

(Sure I can convert this, but it is kind of slow, I write (+ () ()) first, and then in each bracket I put (* () ())...)

I'm wondering if anyone here knows a better way to input this. I was thinking about writing a math macro, where

(math “a(8b^2+1)+4bc(4b^2+1)”) 

expands to

(+ (* a (1+ (* 8 b b))) (* 4 b c (1+ (* 4 b b))))

but parsing is a problem for variables whose names are long.

Anybody has better suggestions?

回答1:

There are reader macros for this purpose.

See: http://www.cliki.net/infix

For example:

CL-USER 17 > '#I(a*(8*b^^2+1)+ 4*b*c*(4*b^^2+1) )
(+ (* A (+ (* 8 (EXPT B 2)) 1)) (* 4 B C (+ (* 4 (EXPT B 2)) 1)))

' is the usual quote. #I( some-infix-expression ) is the reader macro.



回答2:

One project I'm keeping an eye on are so-called "Sweet Expressions". The goal of the project is to add "sweetening" that is backwards-compatible with s-expressions, and simple enough that the expressions won't get in the way of macros. (It has been observed, for example, that operator precedence really interferes with the macro system; hence, the proposed solution doesn't use operator precedence.)

It should be kept in mind that the project is in its infancy, particularly for Common Lisp; however, the project has a working implementation of infix notation, which relies on curly brackets and a simple algorithm:

{1 + {2 * 3} + {4 exp 5}}

translates nicely into

(+ 1 (* 2 3) (exp 4 5))

I'll just refer you to the link for a more in-depth discussion of the semantics of curly-braces.



回答3:

I recently wrote a cl macro exactly for this purpose, you might find it useful. It's called ugly-tiny-infix-macro.

You could write the expression in question as:

($ a * ($ 8 * (expt b 2) + 1) + 4 * b * c * ($ 4 * (expt b 2) + 1))

It is expanded to

(+ (* A (+ (* 8 (EXPT B 2)) 1)) (* (* (* 4 B) C) (+ (* 4 (EXPT B 2)) 1)))

Explanation: $ is the name of macro. The arguments are considered as a list of expressions and hence the liberal use of whitespace to separate numbers/forms from symbols that denote operators.

Consider the following examples to understand function of this macro better:

($ 1 + 2)       ; gets converted to (+ 1 2), where name of the macro is $
($ t and nil)   ; gets converted to (and t nil)
($ 3 > 5)       ; gets converted to (> 3 5)
($ 1 + 2 + 3)   ; gets converted to (+ (+ 1 2) 3)
($ 1 + 2 *  3)      ; gets converted to (+ 1 (* 2 3))
($ 1 < 2 and 2 < 3) ; gets converted to (AND (< 1 2) (< 2 3))

Anything within parentheses at position of an operand is treated like a lisp form.

($ 2 + (max 9 10 11)) ; gets converted to (+ 2 (max 9 10 11)). It could have been any function / lisp form.
($ 6 / ($ 1 + 2))     ; gets converted to (/ 6 ($ 1 + 2)), and then subsequently to (/6 (+ 1 2))

I find it easier to reason about and more advantageous than a reader macro, since it may be easily intermixed with lisp forms, so you can nest lisp forms within the expression. For example, the (exp b 2) could have been any lisp form, like (max a b c) or your own user defined (foobar a b c).

You can find more information on the README on github. It's also available on quicklisp.