this post was submitted on 24 Nov 2023
1 points (100.0% liked)

Lisp

53 readers
3 users here now

founded 1 year ago
MODERATORS
 

Hi. Need help with creating the 'for' macro. It must take a parameter, an initial value, an end value, and a loop step. Macro must return amount of iteration. Various operations should take place inside it. It should be designed without leaks.The preliminary version looks like this:

(defmacro for ((param-name start-value end-value &optional (step1 1)) &body body)
  (let* ((func-name (gensym))
(start (gensym))
(param-name (gensym))
(comparison (if (< step1 0) '< '>))
(end (gensym))
(step (gensym))
(k (gensym)))
'(labels ((,func-name (,param-name ,start ,end ,step ,k)
(let ((new-exprs (progn ,@body))
(newK (+ 1 ,k)))
(if (,comparison ,end ,param-name)
(,func-name (+ ,param-name ,step) ,start ,end ,step newK)
newK))))
(,func-name ,param-name ,start-value ,end-value ,step1 0))))

I understand that it looks terrible. I don't understand how you can access the parameter without violating the rule about leaks.

top 19 comments
sorted by: hot top controversial new old
[–] corbasai@alien.top 1 points 1 year ago (1 children)

param is value or procedure?

[–] Revolutionary_Utena@alien.top 1 points 1 year ago (1 children)
[–] corbasai@alien.top 1 points 1 year ago

pack it call with every step -> shell caller apply result

[–] dzecniv@alien.top 1 points 1 year ago (1 children)
[–] Revolutionary_Utena@alien.top 1 points 1 year ago

I need to implement for with these parameters.

[–] jacobb11@alien.top 1 points 1 year ago (1 children)

Any particular reason for the macro to use recursion instead of "do"?

I don't understand why the function ",func-name" has both a ",param-name" and ",start" parameter. I think (maybe misuderstanding intent) that you should drop ",param-name" from the last line and drop ",start" from function ",func-name".

Hm. ",end" never changes, so it would probably be simpler to declare it outside ",func-name" rather than pass it around. Same for ",step". And maybe ",k", but I'm not clear why ",k" is needed as well as ",param-name" and "start".

Also you need to change + to either + or - depending on "(< step1 0)".

[–] Revolutionary_Utena@alien.top 1 points 1 year ago (1 children)

The teacher said to do it this way. As I understand it, when using a macro, you need to refer to a parameter, for example (let ((s 0))

(for (i 2 N)

((inch s (sin i)))

s) Here it is necessary to refer to the modified sh. Therefore, it is passed to the recursive param-name call.

[–] jacobb11@alien.top 1 points 1 year ago (1 children)

Here it is necessary to refer to the modified sh. Therefore, it is passed to the recursive param-name call.

You can bind a variable outside the function and still refer to it inside the function. Here is an example that does so with ",end":

(defmacro for ((param-name start-value end-value &amp;optional (step1 1)) &amp;body body)
  (let* ((func-name (gensym))
         (start (gensym))
         (param-name (gensym))
         (comparison (if (&lt; step1 0) '&lt; '>))
         (end (gensym))
         (step (gensym))
         (k (gensym)))
    `(let ((,end ,end-value))
      (labels ((,func-name (,param-name ,start ,step ,k)
                   (let ((new-exprs (progn ,@body))
                         (newK (+ 1 ,k)))
                     (if (,comparison ,end ,param-name)
                         (,func-name (+ ,param-name ,step) ,start ,step newK)
                         newK))))
         (,func-name ,param-name ,start-value ,step1 0)))))
[–] Revolutionary_Utena@alien.top 1 points 1 year ago
(defmacro for ((param start-value end-value &amp;optional (step1 1)) &amp;body body)
  (let* ((func-name (gensym))
         (start (gensym))
         (param-name (gensym))
         (comparison (if (&lt; step1 0) '&lt; '>))
         (end (gensym))
         (step (gensym))
         (k (gensym)))
    `(let ((,end ,end-value)
           (,step ,step1)
           (,start ,start-value))
      (labels ((,func-name (,param-name ,k)
                   (let ((new-exprs (progn ,@body))
                         (newK (+ 1 ,k)))
                     (if (,comparison ,end ,param-name)
                         (,func-name (+ ,param-name ,step) newK)
                         newK))))
         (,func-name ,param 0)))))

(let ((s 0))
(for (i 2 5)
(incf s (sin i)))
s)

I made some changes but when I try to call macros with parameter (i 2 5) compiler give me error about unbound variable i.

[–] lispm@alien.top 1 points 1 year ago (2 children)
(defmacro for ((param-name start-value end-value &amp;optional (step1 1)) &amp;body body)
  (let* ((func-name (gensym))
         (comparison (if (&lt; step1 0) '&lt; '>))
         (start (gensym))
         (end (gensym))
         (step (gensym))
         (k (gensym)))

    `(let ((,start ,start-value)
           (,end   ,end-value)
           (,step  ,step1))

       (labels ((,func-name (,param-name ,k)
                  (let ((new-exprs (progn ,@body)))
                    (if (,comparison ,end ,param-name)
                        (,func-name (+ ,param-name ,step) (1+ ,k))
                      ,k))))
         (,func-name ,start 1)))))

Make sure that the new form evaluates the macro arguments in the correct order. The LET enforces it.

Now you would need to think what to do about the step thing.

Can it only be a number? Can it be computed? What if the step value is negative?

How to check that the PARAM-NAME is actually a symbol?

Write some test cases.

[–] Revolutionary_Utena@alien.top 1 points 1 year ago

Thanks man!

[–] Revolutionary_Utena@alien.top 1 points 1 year ago (1 children)

Do you know if it is possible to evaluate the value of body without entering a new variable?

[–] lispm@alien.top 1 points 1 year ago (1 children)
[–] Revolutionary_Utena@alien.top 1 points 1 year ago (1 children)

(defmacro for
  ((param-name start-value end-value &optional (step1 1)) &body body)
    (let* ((func-name (gensym))
           (comp (gensym))
           (comparison (if (< step1 0) '<= '>=))
           (start (gensym))
           (end (gensym))
           (step (gensym))
           (k (gensym)))
      `(let ((,start ,start-value)
             (,comp ,comparison)
             (,end ,end-value)
             (,step ,step1))
        (labels ((,func-name (,param-name ,k)
                   (if (,comparison ,end ,param-name)
                       (progn (progn ,@body)
                         (,func-name (+ ,param-name ,step)
                                     (1+ ,k)) ,k))))
        (,func-name ,start 1)))))

Do you know how can I close leak with comparison? When I am trying to bound comp to evaluated operator of comparison I receive error.

[–] lispm@alien.top 1 points 1 year ago (1 children)

(defmacro for   ((param-name start-value end-value &optional (step1 1)) &body body)     (let* ((func-name (gensym))            (comp (gensym))            (comparison (if (< step1 0) '<= '>=))            (start (gensym))            (end (gensym))            (step (gensym))            (k (gensym)))       `(let ((,start ,start-value)              (,comp ,comparison)              (,end ,end-value)              (,step ,step1))         (labels ((,func-name (,param-name ,k)                    (if (,comparison ,end ,param-name)                        (progn (progn ,@body)                          (,func-name (+ ,param-name ,step)                                      (1+ ,k)) ,k))))         (,func-name ,start 1)))))

Your indentation is wrong. The last ,k is at a wrong place. Shouldn't it be returned by the other if clause.

If you have an error and you need help, then you need to provide the necessary information:

  • a reproducible example
  • the error message
[–] Revolutionary_Utena@alien.top 1 points 1 year ago (1 children)

(defmacro for
  ((param-name start-value end-value &optional (step1 1)) &body body)
    (let* ((func-name (gensym))
           (comp (gensym))
           (comparison (if (< step1 0) '<= '>=))
           (start (gensym))
           (end (gensym))
           (step (gensym))
           (k (gensym)))
      `(let ((,start ,start-value)
             (,comp ,comparison)
             (,end ,end-value)
             (,step ,step1))
        (labels ((,func-name (,param-name ,k)
                   (if (,comp ,end ,param-name)
                       (progn (progn ,@body)
                         (,func-name (+ ,param-name ,step)
                                     (1+ ,k)) ) ,k)))
        (,func-name ,start 0)))))

Sorry man. I kinda fixed the issue. When I am trying to call macros, it return me error
; compilation unit finished

; Undefined functions:

; #:G1 #:G7

; Undefined variable:

; >=
I don't know how to deal with bounding <= or >= to generated variable.

[–] lispm@alien.top 1 points 1 year ago

why would evaluate the comparison? The comparison is a symbol, which a name for the function. Just use that symbol and insert it into the code.

(defmacro for ((param-name start-value end-value &amp;optional (step1 1)) &amp;body body)
  (let* ((func-name (gensym))
         (comparison (if (&lt; step1 0) '&lt;= '>=))
         (start (gensym))
         (end (gensym))
         (step (gensym))
         (k (gensym)))
    `(let ((,start ,start-value)
           (,end ,end-value)
           (,step ,step1))
       (labels ((,func-name (,param-name ,k)
                  (if (,comparison ,end ,param-name)
                      (progn (progn ,@body)
                        (,func-name (+ ,param-name ,step)
                                    (1+ ,k)))
                    ,k)))
         (,func-name ,start 0)))))

If you actually want to make sure that function does not get redefined, you need to understand that functions and variables are in different namespaces. You would need to get a function object and funcall it.

(defmacro for ((param-name start-value end-value &amp;optional (step1 1))
               &amp;body body)
  (let* ((func-name (gensym))
         (comp (gensym))
         (comparison (if (&lt; step1 0) '#'&lt;= '#'>=))
         (start (gensym))
         (end (gensym))
         (step (gensym))
         (k (gensym)))
    `(let ((,start ,start-value)
           (,comp ,comparison)
           (,end ,end-value)
           (,step ,step1))
       (labels ((,func-name (,param-name ,k)
                  (if (funcall ,comp ,end ,param-name)
                      (progn (progn ,@body)
                        (,func-name (+ ,param-name ,step)
                                    (1+ ,k)) ) ,k)))
         (,func-name ,start 0)))))

But you don't need that.

If you want to debug macros, you need to see the expansion. Do it like this:

CL-USER 14 > (pprint
              (macroexpand
               '(for (i start end)
                  (print i))))

(LET ((#:G1788 START) (#:G1789 END) (#:G1790 1))
  (LABELS ((#:G1787 (I #:G1791)
             (IF (>= #:G1789 I)
                 (PROGN
                   (PROGN (PRINT I))
                   (#:G1787 (+ I #:G1790) (1+ #:G1791)))
               #:G1791)))
    (#:G1787 #:G1788 0)))
[–] raevnos@alien.top 1 points 1 year ago

Is

(defmacro for ((param-name start-value end-value &amp;optional (step 1)) &amp;body body)
  `(loop for ,param-name from ,start-value by ,step to ,end-value do (progn ,@body)))

cheating?

[–] Wolfi_Witch@alien.top 1 points 1 year ago

Looks like you're trying to implement a 'for' macro with those parameters. Have you considered using a different approach for accessing the parameter without causing leaks? It's crucial to maintain a clean design while ensuring the macro handles the iteration properly. Keep at it, and you'll get it right!