Common Lisp:有没有更简单的方法输入数学表达式?

7

我喜欢使用Common Lisp,但有时输入简单的数学表达式真的很痛苦,比如:

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

(当然可以转换这个,但是有点慢,我先写 (+ () ()),然后在每个括号内放 (* () ())...)
我想知道这里是否有人知道更好的输入方法。 我正在考虑编写一个数学宏,其中
(math “a(8b^2+1)+4bc(4b^2+1)”) 

扩展为

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

但是对于变量名很长的变量来说,解析是一个问题。

有没有更好的建议?


(a) 如果你不喜欢将Lisp作为计算器使用,可以尝试其他语言。我推荐Python。(b) 进行这种类型的转换是基本的计算机科学练习。我强烈建议你自己解决问题,至少在字符串操作方面,当你遇到将其转化为便捷宏时出现问题时再回来寻求帮助。 - Marcin
好的,没问题。我猜是我自己的错,用Lisp来做Project Euler... - h__
6
Lisp 绝对适用于欧拉计划。当然,输入冗长的数学表达式可能会很麻烦;另一方面,Common Lisp非常适合许多PE问题所需的“自下而上构建解决方案”的方法。 - Haile
@Haile,你说的“自下而上构建解决方案”是什么意思?你能举个例子吗? - h__
2
@hyh 你需要将问题分解成小块。你专注于每个小块。你在REPL上尝试短小的代码片段。你开始组合这些片段,提供小而基本的功能。你立即使用REPL测试这些功能。你逐步编写解决方案,并与解释器交互。你将已经测试过的功能组合成更大的功能。以此类推... - Haile
好的,我明白了。那正是我正在做的事情... - h__
3个回答

28

这个目的有一些读取宏来实现。

参见:http://www.cliki.net/infix

例如:

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)))

'通常用于引号。 #I(某些中缀表达式)是阅读宏。


谢谢,我可以问一下这里的'#I是什么意思吗? - h__
8
这是一个阅读宏,定义在“infix”库中。 - Svante

4
我正在关注一个名为“Sweet-expressions”的项目,它是可读的Lisp S表达式项目的一部分。该项目的目标是添加“甜化”,这与s表达式向后兼容,并且足够简单,以便表达式不会妨碍宏。需要注意的是,该项目尤其是对于Common Lisp而言还处于起步阶段。但是,该项目已经实现了中缀表示法,其中使用花括号和简单算法。
{1 + {2 * 3} + {4 exp 5}}

翻译得很好

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

我只是把链接推荐给你,以便更深入地讨论花括号的语义。

2

我最近为了这个目的编写了一个称为ugly-tiny-infix-macro的cl宏,你可能会发现它很有用。

你可以将所需表达式表示为:

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

它被扩展到

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

解释: $是宏的名称。参数被视为表达式列表,因此自由使用空格来将数字/形式与表示运算符的符号分开。

考虑以下示例以更好地理解此宏的功能:

($ 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))

操作数位置上的括号内文本会被视为Lisp形式。

($ 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))

我认为这种方法比读取宏更易于理解并且更有优势,因为它可以与Lisp表达式混合使用,所以可以在表达式中嵌套Lisp表单。例如,(exp b 2) 可以是任何 Lisp 表单,例如 (max a b c) 或您自己定义的用户定义的 (foobar a b c)

您可以在Github上的README上找到更多信息。 它也在Quicklisp上提供。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接