Author Topic: Lambda as variable, with or without quote?  (Read 2380 times)

0 Members and 1 Guest are viewing this topic.

dexus

  • Bull Frog
  • Posts: 208
Lambda as variable, with or without quote?
« on: March 24, 2022, 09:31:33 AM »
Hi everyone,

I have a couple of functions that take a (lambda) function as a variable.

For example here is a function that gets repeated an n amount of times and puts the return values of that function into a list.
Code - Auto/Visual Lisp: [Select]
  1. (defun repeatToList (i f / r)
  2.   (setq f (eval f))
  3.   (repeat i
  4.     (setq r (cons (f) r))
  5.   )
  6.   (reverse r)
  7. )

It can be called like this:
Code - Auto/Visual Lisp: [Select]
  1. (setq x 0)
  2. (repeatToList 5
  3.   (lambda nil (setq x (1+ x)))
  4. )

But because I have put an eval function in the first line, it can also be called with a quote before the function:
Code - Auto/Visual Lisp: [Select]
  1. (setq x 0)
  2. (repeatToList 5
  3.   '(lambda nil (setq x (1+ x)))
  4. )
The second way seems more common in autolisp because of functions like apply and mapcar needing a quoted function.
But it does make my function a bit slower because of this extra step.

My question is:
What are the advantages of using a quoted function instead of a plain one?
Should I keep the eval in there or is it better to take that line out? Or is it just personal preference?

JohnK

  • Administrator
  • Seagull
  • Posts: 10648
Re: Lambda as variable, with or without quote?
« Reply #1 on: March 24, 2022, 10:12:35 AM »
As far as I know, the quote will slow your procedure down because the preprocessor has to create the definition over each iteration vs look up the function (but I could be off).
TheSwamp.org (serving the CAD community since 2003)
Member location map - Add yourself

Donate to TheSwamp.org

baitang36

  • Bull Frog
  • Posts: 213
Re: Lambda as variable, with or without quote?
« Reply #2 on: March 25, 2022, 09:19:49 AM »
Hi everyone,

I have a couple of functions that take a (lambda) function as a variable.

For example here is a function that gets repeated an n amount of times and puts the return values of that function into a list.
Code - Auto/Visual Lisp: [Select]
  1. (defun repeatToList (i f / r)
  2.   (setq f (eval f))
  3.   (repeat i
  4.     (setq r (cons (f) r))
  5.   )
  6.   (reverse r)
  7. )

It can be called like this:
Code - Auto/Visual Lisp: [Select]
  1. (setq x 0)
  2. (repeatToList 5
  3.   (lambda nil (setq x (1+ x)))
  4. )

But because I have put an eval function in the first line, it can also be called with a quote before the function:
Code - Auto/Visual Lisp: [Select]
  1. (setq x 0)
  2. (repeatToList 5
  3.   '(lambda nil (setq x (1+ x)))
  4. )
The second way seems more common in autolisp because of functions like apply and mapcar needing a quoted function.
But it does make my function a bit slower because of this extra step.

My question is:
What are the advantages of using a quoted function instead of a plain one?
Should I keep the eval in there or is it better to take that line out? Or is it just personal preference?
My advice to you is not to use lambda and define a normal function

dexus

  • Bull Frog
  • Posts: 208
Re: Lambda as variable, with or without quote?
« Reply #3 on: April 01, 2022, 07:03:03 AM »

As far as I know, the quote will slow your procedure down because the preprocessor has to create the definition over each iteration vs look up the function (but I could be off).
Thanks for your answer.
I'm thinking without a quote it still has to create the function every time I call lambda right?
So maybe it is just the eval that slows it down, but I have no idea how preprocessors work :embarrassed2:

My advice to you is not to use lambda and define a normal function

Thanks for your reply. Can you explain why I should use a normal function instead?
What are the advantages other than not having to recreate the lambda function every time it is used?

I did some benchmarks and the lambda without a quote still is the fastest:
Code - Auto/Visual Lisp: [Select]
  1. (setq x 0)
  2. (benchmark
  3.   '(
  4.     (progn ; <fastest>
  5.       (repeatToList 5
  6.         (lambda nil (setq x (1+ x)))
  7.       )
  8.     )
  9.     (progn
  10.       (defun plusone nil (setq x (1+ x)))
  11.       (repeatToList 5 plusone)
  12.     )
  13.     (progn
  14.       (defun plusone nil (setq x (1+ x)))
  15.       (repeatToList 5 'plusone)
  16.     )
  17.     (progn ; <slowest>
  18.       (repeatToList 5
  19.         '(lambda nil (setq x (1+ x)))
  20.       )
  21.     )
  22.   )
  23. )

gile

  • Gator
  • Posts: 2507
  • Marseille, France
Re: Lambda as variable, with or without quote?
« Reply #4 on: April 01, 2022, 07:31:29 AM »
Hi,

How far is it faster?
IMHO, if speed is really the issue, forget LISP and switch to .NET or ObjectARX.

In this case, I'd rather try to be consistent with other AutoLISP higher order functions and use the quoted function with the (setq f (eval f)) expression (even it is a little slower).
See this reply.
Speaking English as a French Frog

dexus

  • Bull Frog
  • Posts: 208
Re: Lambda as variable, with or without quote?
« Reply #5 on: April 01, 2022, 07:45:33 AM »
Hi,

How far is it faster?
IMHO, if speed is really the issue, forget LISP and switch to .NET or ObjectARX.

In this case, I'd rather try to be consistent with other AutoLISP higher order functions and use the quoted function with the (setq f (eval f)) expression (even it is a little slower).
See this reply.
Hi gile,

Your post was the reason I started thinking about this in the first place. :lol:
Just for consistency is a pretty good reason, I was wondering if there were more reasons to use quoted functions. But none so far.

For this simple function, remove the eval is up to 1.85 times faster.
I'm guessing for more elaborate functions the difference will be much much smaller.
Code: [Select]
(eval removed, no quote, lambda function).....1047 / 1.85 <fastest>
(eval removed, no quote, normal function).....1093 / 1.77
(with eval,    no quote, lambda function).....1843 / 1.05
(with eval,    no quote, normal function).....1844 / 1.05
(with eval,    quoted,   normal function).....1859 / 1.04
(with eval,    quoted,   lambda function).....1937 / 1 <slowest>

gile

  • Gator
  • Posts: 2507
  • Marseille, France
Re: Lambda as variable, with or without quote?
« Reply #6 on: April 01, 2022, 08:04:45 AM »
Do not focus on the 'x times faster' and look at the ellapsed millseconds divided by number of loops differences.
Also, do benchmarks with (repeatToList 5 ...), (repeatToList 100 ...), (repeatToList 10000 ...)... because the (eval ...) expression is only run once whatever the number of loops.
Speaking English as a French Frog

baitang36

  • Bull Frog
  • Posts: 213
Re: Lambda as variable, with or without quote?
« Reply #7 on: April 02, 2022, 09:09:21 AM »
This problem should be analyzed from the Underlying technology, and the two writing methods should be compiled into Fas respectively. The decoded FAS is like this
Offset      0  1  2  3  4  5  6  7   8  9  A  B  C  D  E  F
00000000   0D 0A 20 46 41 53 34 2D  46 49 4C 45 20 3B 20 44      FAS4-FILE ; D
00000010   6F 20 6E 6F 74 20 63 68  61 6E 67 65 20 69 74 21   o not change it!
00000020   0D 0A 31 31 36 0D 0A 31  31 20 24 14 03 02 02 00     116  11 $     
00000030   09 0A 00 5C 00 00 09 09  00 5C 01 00 18 05 00 09      \     \     
00000040   08 00 51 01 07 00 01 00  0A 03 09 00 35 01 06 00     Q         5   
00000050   03 06 09 00 03 0A 00 5D  02 00 5C 02 00 32 00 4B          ]  \  2 K
00000060   67 1C 00 00 00 5C 02 00  50 5D 02 00 35 00 09 00   g    \  P]  5   
00000070   03 03 05 00 35 02 04 00  03 06 05 00 57 D9 FF FF       5       W?
00000080   FF 03 05 00 35 01 03 00  03 19 03 00 16
14 00 00      5           
00000090   00 00 03 02 00 35 01 01  00 03 0B 06 02 00 16
24        5         $
000000A0   0D 0A 32 30 39 20 36 20  24 14 01 01 01 00 32 00     209 6 $     2
000000B0   32 2F 2A 39 01 00 5B 58  00 52 45 50 45 41 54 54   2/*9  [X REPEATT
000000C0   4F 4C 49 53 54 00 00 01  01 43 00 00 04 00 0A 32   OLIST    C     2
000000D0   62 32 74 2A 32 00 32 62  2A 39 02 00 5B 31 2B 00   b2t*2 2b*9  [1+
000000E0   58 00 52 45 56 45 52 53  45 00 43 4F 4E 53 00 52   X REVERSE CONS R
000000F0   00 45 56 41 4C 00 00 56  5F 61 6C 2D 62 69 6E 64    EVAL  V_al-bind
00000100   2D 61 6C 69 73 74 00 00  5B 52 00 00 39 01 00 5B   -alist  [R  9  [
00000110   46 00 49 00 00 5C 00 00  43 00 00 0B 00 0A 5C 00   F I  \  C     \
00000120   00 32 62 5B 2D 6C 61 6D  62 64 61 2D 00 00 3A 5C    2b[-lambda-  :\
00000130   00 00 32 00 5B 52 45 50  45 41 54 54 4F 4C 49 53     2 [REPEATTOLIS
00000140   54 00 00 3A 01 43 04 00  02 00 1C 14 01 00 00 00   T  : C         
00000150   09 03 00 0A 57 00 00 00  00 09 05 00 06 02 00 09       W           
00000160   02 00 0A 32 00 0B 06 01  00 0A 32 05 57 00 00 00      2      2 W   
00000170   00 09 04 00 35 02 02 00  03 16 17 00 87 FD 12 D2       5       圐 ?
00000180   9A 4D 9C 0A 3B 66 61 73  34 20 63 72 75 6E 63 68   歁?;fas4 crunch
00000190   0A 3B 24 3B 41 34 2F 32  2F 32 32                   ;$;A4/2/22
This is the first way to write


Offset      0  1  2  3  4  5  6  7   8  9  A  B  C  D  E  F
00000000   0D 0A 20 46 41 53 34 2D  46 49 4C 45 20 3B 20 44      FAS4-FILE ; D
00000010   6F 20 6E 6F 74 20 63 68  61 6E 67 65 20 69 74 21   o not change it!
00000020   0D 0A 39 38 0D 0A 39 20  24 14 03 02 02 00 09 08     98  9 $       
00000030   00 5C 00 00 09 07 00 5C  01 00 18 05 00 09 06 00    \     \       
00000040   51 01 05 00 01 00 0A 03  07 00 35 01 04 00 03 06   Q         5     
00000050   07 00 03 08 00 5D 02 00  5C 02 00 32 00 4B 67 1C        ]  \  2 Kg
00000060   00 00 00 5C 02 00 50 5D  02 00 35 00 07 00 03 03      \  P]  5     
00000070   03 00 35 02 02 00 03 06  03 00 57 D9 FF FF FF 03     5       W?
00000080   03 00 35 01 01 00 03 19  03 00 16
24 0D 0A 32 31     5        $  21
00000090   36 20 36 20 24 14 01 01  01 00 32 00 32 2A 2A 39   6 6 $     2 2**9
000000A0   01 00 5B 4C 41 4D 42 44  41 00 00 01 5B 53 45 54     [LAMBDA   [SET
000000B0   51 00 00 5B 58 00 00 5B  31 2B 00 00 5B 58 00 00   Q  [X  [1+  [X 
000000C0   39 02 00 39 03 00 39 03  00 5B 58 00 52 45 50 45   9  9  9  [X REPE
000000D0   41 54 54 4F 4C 49 53 54  00 00 01 01 43 00 00 05   ATTOLIST    C   
000000E0   00 0A 32 00 32 62 2A 39  01 00 5B 52 45 56 45 52     2 2b*9  [REVER
000000F0   53 45 00 43 4F 4E 53 00  52 00 45 56 41 4C 00 00   SE CONS R EVAL 
00000100   56 5F 61 6C 2D 62 69 6E  64 2D 61 6C 69 73 74 00   V_al-bind-alist
00000110   00 5B 52 00 00 39 01 00  5B 46 00 49 00 00 5C 00    [R  9  [F I  \
00000120   00 43 00 00 09 00 0A 5C  00 00 32 00 5B 52 45 50    C     \  2 [REP
00000130   45 41 54 54 4F 4C 49 53  54 00 00 3A 01 43 05 00   EATTOLIST  : C 
00000140   01 00 1C 14 01 00 00 00  09 04 00 0A 57 00 00 00               W   
00000150   00 09 05 00 06 03 00 09  03 00 0A 32 00 0B 06 02              2   
00000160   00 0A 32 05 09 01 00 35  02 03 00 03 16 15 00 F9     2    5       ?
00000170   CC B8 D7 42 0A 3B 66 61  73 34 20 63 72 75 6E 63   谈譈 ;fas4 crunc
00000180   68 0A 3B 24 3B 41 34 2F  32 2F 32 32               h ;$;A4/2/22
This is the second way to write

It can be seen that the first method defines two functions, red and green, and the second method has only one function.
In other words, the first method is to compile lambda into ordinary functions. In the second method, lambda is not a function.(LAMBDA NIL (SETQ X (1+ X ) ) ) is a list,It is interpreted and executed, so the speed is slow.

Acad's help file says that the function of lambda is to define a function without name. It is equivalent to the function defined and called now, because it is nameless and cannot be called elsewhere.

After analyzing the fas file, it is found that it is no different from general functions, except that all Lambdas use the same function name -lambda-
« Last Edit: April 02, 2022, 10:02:18 AM by baitang36 »

baitang36

  • Bull Frog
  • Posts: 213
Re: Lambda as variable, with or without quote?
« Reply #8 on: April 03, 2022, 09:14:51 AM »
Hi,

How far is it faster?
IMHO, if speed is really the issue, forget LISP and switch to .NET or ObjectARX.

In this case, I'd rather try to be consistent with other AutoLISP higher order functions and use the quoted function with the (setq f (eval f)) expression (even it is a little slower).
See this reply.
Hi gile,

Your post was the reason I started thinking about this in the first place. :lol:
Just for consistency is a pretty good reason, I was wondering if there were more reasons to use quoted functions. But none so far.

For this simple function, remove the eval is up to 1.85 times faster.
I'm guessing for more elaborate functions the difference will be much much smaller.
Code: [Select]
(eval removed, no quote, lambda function).....1047 / 1.85 <fastest>
(eval removed, no quote, normal function).....1093 / 1.77
(with eval,    no quote, lambda function).....1843 / 1.05
(with eval,    no quote, normal function).....1844 / 1.05
(with eval,    quoted,   normal function).....1859 / 1.04
(with eval,    quoted,   lambda function).....1937 / 1 <slowest>
eval is  run source code,it is  very slow

gile

  • Gator
  • Posts: 2507
  • Marseille, France
Re: Lambda as variable, with or without quote?
« Reply #9 on: April 03, 2022, 10:43:52 AM »
eval is  run source code,it is  very slow
What does mean with "very slow"?
Do not focus on the "1.85 times faster".
From the test I did, the difference between using or not the (eval ...) expression is less than 0.02 milliseconds.
Speaking English as a French Frog

baitang36

  • Bull Frog
  • Posts: 213
Re: Lambda as variable, with or without quote?
« Reply #10 on: April 03, 2022, 11:05:50 AM »
eval is  run source code,it is  very slow
What does mean with "very slow"?
Do not focus on the "1.85 times faster".
From the test I did, the difference between using or not the (eval ...) expression is less than 0.02 milliseconds.
0.02 ms? That means your expression is not complex enough. There is a big difference between compiled execution and interpreted execution.

gile

  • Gator
  • Posts: 2507
  • Marseille, France
Re: Lambda as variable, with or without quote?
« Reply #11 on: April 03, 2022, 12:35:09 PM »
We were not discussing about compiled vs interpreted.
We were just talking about the cost of a (eval ...) expression while passing a quoted function vs passing an unquoted function.
The tests I did were with something similar to the OP:
Code - Auto/Visual Lisp: [Select]
  1. (defun repeatToListQuoted (i f / r)
  2.   (setq f (eval f))
  3.   (repeat i
  4.     (setq r (cons (f) r))
  5.   )
  6.   (reverse r)
  7. )
  8.  
  9. (defun repeatToListUnquoted (i f / r)
  10.   (repeat i
  11.     (setq r (cons (f) r))
  12.   )
  13.   (reverse r)
  14. )

Here're some benchmark results with the ellpsed milliseconds calculation for 1 iteration (results are less than 0.02).
Code: [Select]
_$ (benchmark '(
((lambda (x) (repeatToListQuoted 5 '(lambda () (setq x (1+ x))))) 0)
((lambda (x) (repeatToListUnquoted 5 (lambda () (setq x (1+ x))))) 0)))
Benchmarking ..................Elapsed milliseconds / relative speed for 32768 iteration(s):

    ((LAMBDA (X) (REPEATTOLISTUNQUOTED 5...).....1641 / 1.35 <fastest>
    ((LAMBDA (X) (REPEATTOLISTQUOTED 5 (...).....2219 / 1.00 <slowest>
_$ (/ (- 2219. 1641.) 32768)
0.0176392
_$ (benchmark '(
((lambda (x) (repeatToListQuoted 100 '(lambda () (setq x (1+ x))))) 0)
((lambda (x) (repeatToListUnquoted 100 (lambda () (setq x (1+ x))))) 0)))
Benchmarking ................Elapsed milliseconds / relative speed for 8192 iteration(s):

    ((LAMBDA (X) (REPEATTOLISTUNQUOTED 1...).....1156 / 1.11 <fastest>
    ((LAMBDA (X) (REPEATTOLISTQUOTED 100...).....1281 / 1.00 <slowest>
_$ (/ (- 1281. 1156.) 8192)
0.0152588
_$ (benchmark '(
((lambda (x) (repeatToListQuoted 1000 '(lambda () (setq x (1+ x))))) 0)
((lambda (x) (repeatToListUnquoted 1000 (lambda () (setq x (1+ x))))) 0)))
Benchmarking .............Elapsed milliseconds / relative speed for 1024 iteration(s):

    ((LAMBDA (X) (REPEATTOLISTQUOTED 100...).....1062 / 1.02 <fastest>
    ((LAMBDA (X) (REPEATTOLISTUNQUOTED 1...).....1079 / 1.00 <slowest>
_$ (/ (- 1079. 1062.) 1024)
0.0166016
Speaking English as a French Frog

baitang36

  • Bull Frog
  • Posts: 213
Re: Lambda as variable, with or without quote?
« Reply #12 on: April 04, 2022, 08:47:38 AM »
We were not discussing about compiled vs interpreted.
We were just talking about the cost of a (eval ...) expression while passing a quoted function vs passing an unquoted function.
The tests I did were with something similar to the OP:
Code - Auto/Visual Lisp: [Select]
  1. (defun repeatToListQuoted (i f / r)
  2.   (setq f (eval f))
  3.   (repeat i
  4.     (setq r (cons (f) r))
  5.   )
  6.   (reverse r)
  7. )
  8.  
  9. (defun repeatToListUnquoted (i f / r)
  10.   (repeat i
  11.     (setq r (cons (f) r))
  12.   )
  13.   (reverse r)
  14. )

Here're some benchmark results with the ellpsed milliseconds calculation for 1 iteration (results are less than 0.02).
Code: [Select]
_$ (benchmark '(
((lambda (x) (repeatToListQuoted 5 '(lambda () (setq x (1+ x))))) 0)
((lambda (x) (repeatToListUnquoted 5 (lambda () (setq x (1+ x))))) 0)))
Benchmarking ..................Elapsed milliseconds / relative speed for 32768 iteration(s):

    ((LAMBDA (X) (REPEATTOLISTUNQUOTED 5...).....1641 / 1.35 <fastest>
    ((LAMBDA (X) (REPEATTOLISTQUOTED 5 (...).....2219 / 1.00 <slowest>
_$ (/ (- 2219. 1641.) 32768)
0.0176392
_$ (benchmark '(
((lambda (x) (repeatToListQuoted 100 '(lambda () (setq x (1+ x))))) 0)
((lambda (x) (repeatToListUnquoted 100 (lambda () (setq x (1+ x))))) 0)))
Benchmarking ................Elapsed milliseconds / relative speed for 8192 iteration(s):

    ((LAMBDA (X) (REPEATTOLISTUNQUOTED 1...).....1156 / 1.11 <fastest>
    ((LAMBDA (X) (REPEATTOLISTQUOTED 100...).....1281 / 1.00 <slowest>
_$ (/ (- 1281. 1156.) 8192)
0.0152588
_$ (benchmark '(
((lambda (x) (repeatToListQuoted 1000 '(lambda () (setq x (1+ x))))) 0)
((lambda (x) (repeatToListUnquoted 1000 (lambda () (setq x (1+ x))))) 0)))
Benchmarking .............Elapsed milliseconds / relative speed for 1024 iteration(s):

    ((LAMBDA (X) (REPEATTOLISTQUOTED 100...).....1062 / 1.02 <fastest>
    ((LAMBDA (X) (REPEATTOLISTUNQUOTED 1...).....1079 / 1.00 <slowest>
_$ (/ (- 1079. 1062.) 1024)
0.0166016
If you run the source code directly, the speed should be little different, because it is the source code. It is recommended that you compile into Fas file and then compare the running speed. The compiled Eval still runs the source code.