我想创建一个可以在Common Lisp中创建函数调用的宏

时间:2014-03-13 07:22:46

标签: function variables macros common-lisp

我有一堆名为

的函数
(test-1)
(test-2)
(test-3)
(test-n)...

我想创建一个名为'(magically-add)'的宏或函数,它将从宏或函数内的预设变量值和提供给宏的参数中创建一个函数调用。例如,预设变量值,让我们调用宏内的变量'foo'

(setf foo test-)

并在帖子顶部创建一个函数,提供的参数将等于1,2,3或n。

所以当我运行它时,我会按如下所示运行它,在这种情况下选择“1”并将输出保存到带有'defparameter'的变量。

 (defparameter bar (magically-add 1))

所以现在我在repl之前只输入“bar”而没有引号我知道它的内容是一个看起来像这样的函数调用,

(test-1) so when I run 'bar' at the repl

如此

 REPL> bar

输出是函数(test-1)在repl运行时的输出。所以在运行(魔法添加)以连接后,不知何故,从宏内部'test-'和'“1”'没有引号我提供给宏的变量然后'bar'将等于'(test-1)'。因此,假设'(test-1)'的正常输出为'“Hello World”'然后在repl处运行'bar'将具有与下面相同的输出 输出>“Hello World”

有谁愿意帮我创造这个'神奇的功能'?提前感谢任何参与者。

编辑:

我想知道如果,如果这是test-1

我会怎么做
(defun test-1 (matrix type row cols)
   (find-element matrix type rows cols))

如果我打电话

(defmacro magically-add (matrix type rows cols &optional (base 'test-))
  (list (intern (concatenate 'string
                         (symbol-name base)
                         (write-to-string type)))))

然后

  (magically-add hypothetical-matrix double 0 0)

我得到:'参数数量无效0'

似乎魔法添加会调用函数test-1但需要更新才能将参数传递给它...我的最终目标是实际传递参数以神奇地添加并让参数最终作为参数进行测试-1。对此有何建议?再次感谢天才回答btw。

编辑2:

我道歉我使用'test-1 .. n'作为一个捕获所有以前我添加了类型作为参数....但它实际上命名为'test-double',名称告诉哪一个,我/ e'test-double''test-float''test-int',运行。对此感到抱歉,只是一个简化的小陷阱。

所以使用我的'(test-double)'函数进行最新编辑,例如:

(defun test-double (matrix rows cols)
    (find-element matrix rows cols))

我可以打电话'(神奇地添加'测试 - '双重矩阵0 0)',但现在唯一的问题是它的美丽。我希望能够打电话而不是

 '(magically-add 'test- 'double matrix 0 0)'

只是

'(magically-add 'test 'double matrix 0 0)'

因此,如果我将我的函数重命名为(testdouble),它可能但有点难看,但如果我打电话

(defun magically-add (base number &rest args)
    (apply (intern (concatenate 'string
                            (symbol-name base)
                             "-"
                            (write-to-string number)))
     args))

有可能......你能给我最后一条建议,关于我如何使用下面的关键字打电话。 Lisp关键字是如此美丽(不同的颜色和所有)。然后我可以访问我的所有“'测试 - ”,如下所示....我认为这可能是最终的...特别是如果它更快,再次高性能库...但你提供的目前为止只是很棒我我们必须将两者作为真正的赢家进行比较......非常感谢您抽出宝贵的时间来帮助我。

(magically-add :test :double matrix 0 0)

将发布这些功能的基准测试,而不是今天晚些时候在这一行下运行原件。

在dotimes的十万次运行中,在dotimes中没有其他功能,但功能,原始功能和Frank在新鲜的emacs上大约相同的时间5秒。

在十亿次运行中使用dotimes并且在dotimes中没有别的东西,但原始功能和Frank的功能完全不同

原来花了大约41秒完成十亿次迭代......不错...... 弗兰克的功能可以理解地慢了 - 完成2000万次迭代需要41秒,而十亿次......我很道歉但是我在8分钟后停止了计数......

因此,如果有人知道如何加快这种编码风格以使其具有可比性,我很乐意听到它。既然这些是高性能库的功能,我现在只需要像C ++库那样使用名称,并调用它们(test-int)(test-float)(test-double)....等等。对于许多功能来说,很容易记住,就像在c ++中做Mat.at或Mat.cols或Mat.rows一样。

1 个答案:

答案 0 :(得分:1)

我希望我理解你的想法。这是什么意思?

(defun magically-add (number &optional (base 'test-))
  (funcall (intern (concatenate 'string
                                (symbol-name base)
                                (write-to-string number)))))

它将数字和基数转换为字符串,将其连接起来,然后将其转换回符号以便调用它。

测试:

CL-USER 1 > (defun test-1 () "hello World")
TEST-1

CL-USER 2 > (magically-add 1)
"hello World"

请注意,这仅适用于动态范围的函数 - 这意味着它不适用于使用label或flet定义的函数。 (由于类似的原因,你无法获得词法范围符号的值 - 更多细节看看here)如果你想拥有它,你可以使用宏:

(defmacro magically-add (number &optional (base 'test-))
  (list (intern (concatenate 'string
                             (symbol-name base)
                             (write-to-string number)))))

现在它也适用于词法范围的函数:

CL-USER 3 > (flet ((test-1 () "Hello again")) (magically-add 1))
"Hello again"

修改

编辑之后回答您的问题 - 有两个重大问题。

首先,您省略了number变量并将新变量type转换为字符串。因此,您将调用一个名为test-double的函数,该函数不存在。其次,你不需要任何参数就可以调用该函数。您必须将它们添加到函数调用中。

这是一个魔法添加的版本,它接收任意数量的参数,但你必须给它base变量。它不再默认为test-。它也使用DEFUN版本,因为Rainer Joswigs在下面发表评论。被限制为仅使用number参数的普通数字(而不是变量而不是列表的元素)似乎比不使用词法范围的函数给我带来更大的不便。

(defun magically-add (base number &rest args)
  (apply (intern (concatenate 'string
                              (symbol-name base)
                              (write-to-string number)))
         args))

所以前两个参数用于构造函数名称以及用于调用此函数的所有其余参数:

CL-USER 1 > (defun test-1 (foo bar baz) (list foo bar baz))
TEST-1

CL-USER 2 > (magically-add 'test- 1 "hi" 42 'symbol)
("hi" 42 SYMBOL)

如果有人知道如何做类似的事情而不仅限于动态范围的功能,我也会感兴趣。

编辑2:

要回答你的第二个编辑:很少有人可以改变以实现这一目标。 write-to-string为您提供了number的打印表示字符串。当然,如果是关键字:double,您将获得":DOUBLE"。您可以再次使用symbol-name来检索关键字的名称 - 这只是"DOUBLE"

(defun magically-add (praefix postfix &rest args)
    (apply (intern (concatenate 'string
                                (symbol-name praefix)
                                "-"
                                (symbol-name postfix)))
     args))