Common lisp: is there a less painful way to input math expressions?
Asked Answered
T

3

6

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?

Tushy answered 12/8, 2012 at 18:27 Comment(7)
(a) If you don't like lisp as a calculator, use something else. I recommend python (b) performing this sort of transformation is a basic computer science exercise. I strongly suggest that you figure it out yourself, at least on the string manipulation side, and come back when you hit problems making it into a convenient macro.Pelias
OK, fine. I guess it is my fault to do Project Euler with lisp...Tushy
Lisp is absolutely ok for Project Euler. Sure a lengthy math expression can be a pain to type in; on the other hand Common Lisp is very suitable for the "build solutions bottom-up" approach lots of problems on PE require.Chauncey
@Chauncey What do you mean by that (build solutions bottom-up)? Can you give an example?Tushy
@hyh You break down the problem in small pieces. You focus on each piece alone. You try short snippets of code on the REPL. You start composing those snippets in functions, to provide small and basic functionalities. You test those functionalities right away using the REPL. You write your solution incrementally, interacting with the interpreter. You compose already tested functionalities into bigger ones. And so on..Chauncey
OK, i see. That's exactly what I'm doing...Tushy
"parsing is a problem for variables whose names are long" -- it's not a parsing problem, it's a failure to define a syntax that distinguishes between ab as a*b and ab as an identifier.Jermainejerman
F
28

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.

Fractionize answered 12/8, 2012 at 18:54 Comment(2)
Thanks, may I ask what does '#I mean here?Tushy
It is a reader macro, which is defined in the infix library.Overcharge
M
4

One project I'm keeping an eye on are so-called "Sweet-expressions", part of the Readable Lisp S-expressions Project. 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.

Martinemartineau answered 25/4, 2013 at 4:11 Comment(0)
E
2

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.

Exacting answered 10/7, 2016 at 6:1 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.