Вопрос

I have a bunch of functions named as follows

(test-1)
(test-2)
(test-3)
(test-n)...

I would like to make a macro or function called '(magically-add)' that would create a function call out of a preset variable value inside the macro or function and a parameter supplied to the macro. For example the preset variable value, lets call the variable 'foo' inside the macro would be

(setf foo test-)

and to create one of the functions at the top of my post the supplied parameter would equal 1, 2, 3 or n.

so when I run it, I would run it as below, choosing '1' in this case and saving the output to a variable with 'defparameter'.

 (defparameter bar (magically-add 1))

so now before I enter just "bar" without quotes at the repl I know the contents of it is a function call that looks like this,

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

as so

 REPL> bar

The output is whatever the output of the function (test-1) would have been had I ran that at the repl. so after running (magically-add) to concatenate, somehow, 'test-' from inside the macro and '"1"' without quotes the variable I supplied to the macro then 'bar' would equal '(test-1)'. So lets say the normal output of '(test-1)' would be '"Hello World"' then running 'bar' at the repl would have the same output as below OUTPUT>"Hello World"

Anyone like to help me create this 'magical function'? Thanks in advance for any takers.

Edit:

I was wondering what would I do if , lets say this was test-1

(defun test-1 (matrix type row cols)
   (find-element matrix type rows cols))

If I called

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

then

  (magically-add hypothetical-matrix double 0 0)

I get: 'Invalid number of arguments 0'

It seem that magically-add will call the function test-1 but would need updating to pass parameters to it...That my ultimate goal to actually pass parameters to magically-add and have the parameters end up as arguments to test-1. Any advice on this?? Thanks again for that genius answer btw.

Edit 2:

I do apologize I was using 'test-1 .. n' as a catch all Previously I added the type as a parameter....but its actually named 'test-double' and the name tells which one, i/e 'test-double' 'test-float' 'test-int', to run. So sorry about that, just a little pitfall of simplifying.

So with your latest edit using my '(test-double)' function as example:

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

I can call '(magically-add 'test- 'double matrix 0 0)' but the only question now is in the beauty of it. I'd like to be able to call instead of

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

just

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

So if I rename my function to (testdouble) it's possible but kinda ugly, but if I call

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

It's possible...Can you give me one last piece of advice, on how would I call with keywords as below. Lisp keywords are so beautiful(different colors and all). Then I can access all my "'test-"' as below....I think that might be the ultimate...especially if its quicker, again High Performance library...but what you provided so far is just awesome I'll have to compare the two for the real winner...Thank you very much for taking the time to help me this.

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

Will post benchmarking of these functions vs running just the originals later today under this line.

On a hundred thousand runs with a dotimes and nothing else in the dotimes but the function, the original function and Frank's did about the same time 5 seconds on a fresh emacs.

On a billion runs with a dotimes and nothing else in the dotimes but the function the original function and Frank's did drastically different

the original took about 41 seconds to complete a billion iterations...not bad... Frank's function was understandably slower - It took 41 seconds to complete 20 million iterations and as for a billion...Well I apologize but I stopped counting after 8 minutes...

So if anybody has an idea how to speed up this style of coding to make it comparable I'd love to hear it. Since these are functions for high performance library for now I'll just be craft with the names as C++ libraries do sometimes and call them (test-int) (test-float) (test-double)....etc. For a lot of functions that makes it easy to remember like in c++ doing Mat.at or Mat.cols or Mat.rows.

Это было полезно?

Решение

I hope I understand your idea right. What's about this?

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

It converts the number as well as the base into a string, concatenates it, then converts it back into a symbol for calling it.

Testing:

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

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

Note that this holds only for dynamically scoped functions – that means it does not work for functions defined with labels or flet. (For a similar reason why you cannot get the value of a lexical scoped symbol – for more details have a look here) If you want to have that, you can use a macro:

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

Now it works for lexically scoped functions, too:

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

EDIT:

To answer to your problem after your edit – There two major things wrong.

First, you omitted the number variable and converted your new variable type to a string. So you will call a function named test-double that doesn't exist. Second, you call that function without any arguments. You must add them to the function-call.

Here is a version of magically-add that receives arbitrary number of arguments, but you MUST give the base variable to it. It doesn't default to test- anymore. It also uses the DEFUN version because of Rainer Joswigs comment below. Being restricted to only using plain numbers for the number argument (and not variables and not elements of lists) seems to be a greater inconvenience to me than not using lexically scoped functions.

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

So the first two arguments are for contructing the function name and all the rest arguments used to call this function:

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)

If someone has an idea how to do something similar to that with not being restricted to dynamically scoped functions, I'd be interested, too.

EDIT 2:

To answer your second edit: There's quiet few to change to achieve that. write-to-string gives you a string of the printed representation of number. Of course, if it's the keyword :double, you'll get ":DOUBLE". You can just use symbol-name again to retrieve the name of the keyword – which is just "DOUBLE".

(defun magically-add (praefix postfix &rest args)
    (apply (intern (concatenate 'string
                                (symbol-name praefix)
                                "-"
                                (symbol-name postfix)))
     args))
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top