• Tail call recursion macro with named-let in Emacs Lisp (was: A simple l

    From Axel Reichert@21:1/5 to B. Pym on Sat Jun 15 12:17:42 2024
    "B. Pym" <No_spamming@noWhere_7073.org> writes:

    Frank Schwieterman wrote:

    (defun difference (param)
    (if (> (length param) 1)
    (if (<= (abs (- (first param) (first (rest param)))) 1 )
    (difference (rest param))
    nil
    )
    T
    )
    )

    Gauche Scheme

    (define (diff1? xs)
    (every
    (lambda (a b) (= 1 (abs (- a b))))
    xs
    (cdr xs)))

    (diff1? '(2 3 4 3 4 5 6 7 8 9 8))
    ===>
    #t
    (diff1? '(2 3 4 3 4 5 6 7 8 9 8 0))
    ===>
    #f

    A nice, recursive solution in Emacs Lisp would be

    (defun single-step-p (xs)
    (cond ((null (cdr xs)) t)
    ((= 1 (abs (- (car xs) (cadr xs)))) (single-step-p (cdr xs)))
    (t nil)))

    but it fails for longer lists such as

    (single-step-p (number-sequence 1 1000))

    because Emacs Lisp does not have proper tail call elimination. There is
    an exception, though, "named-let", see here:

    https://www.gnu.org/software/emacs/manual/html_node/elisp/Local-Variables.html

    So I rewrote the function to

    (defun single-step-p (xs)
    (named-let single-step-p ((xs xs))
    (cond ((null (cdr xs)) t)
    ((= 1 (abs (- (car xs) (cadr xs)))) (single-step-p (cdr xs)))
    (t nil))))

    which work fine also for

    (single-step-p (number-sequence 1 1000000))

    but of course looks a bit redundant compared to the "normal" use of
    "named-let" for helper functions such as accumulating results etc.

    So I wrote a small macro doing the wrapping in a "named-let" for me:

    (defmacro defun-tco-1 (name args body)
    `(defun ,name ,args
    (named-let ,name (,args ,args)
    ,body)))

    Now

    (defun-tco-1 single-step-p (xs)
    (cond ((null (cdr xs)) t)
    ((= 1 (abs (- (car xs) (cadr xs)))) (single-step-p (cdr xs)))
    (t nil)))

    works, but the macro of course fails for a different number of
    arguments, as can be seen with

    (macroexpand-1 '(defun-tco-1 foo (a b) (+ a b)))

    which gives

    (defun foo (a b) (named-let foo ((a b) (a b)) (+ a b)))

    and of course also wrong results (14 instead of 10) for

    (foo 3 7)

    So I needed to loop through the args of the macro and construct the
    proper bindings for the "named-let":

    (defmacro defun-tco (name args body)
    (let ((bindings (mapcar #'(lambda (arg) (list arg arg)) args)))
    `(defun ,name ,args
    (named-let ,name ,bindings
    ,body))))

    Is this fine from the point of the experts here (in fact, it is my first
    macro that is unrelated to any macro tutorials/book, but rather came
    from my own needs)? I am quite sure that I could suffer from variable
    capture and perhaps also from multiple evaluation, but this would be the
    second step for me after a first, general criticism here.

    Many thank for any comments!

    Best regards

    Axel

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From HenHanna@21:1/5 to HenHanna on Sat Jun 15 17:20:31 2024
    On 6/15/2024 1:08 PM, HenHanna wrote:
    On 6/14/2024 8:45 PM, B. Pym wrote:
    Frank Schwieterman wrote:

    I am Lisp beginner and I am doing a simple execise on lisp. I was asked >>>> to write a lisp program , by using either recursion or do, to return
    true if the difference between each successive pair of the them is 1. (or LESS)

    ie:

    (difference '(2 1))
    T
    (difference'(3 4 5 6 5 4))
    T
    (differnce '(3 4 5 3))
    NIL
      ....

    (defun difference (param)
          (if (> (length param) 1)
             (if (<= (abs (- (first param) (first (rest param)))) 1 ) >>>             (difference (rest param))
                nil
                )
             T
             )
          )


    looks good. except for CADR (second) and ) ) )




    (define (diff1p?  x)
      (or (null? x)
          (null? (cdr x))
          (and (= 1 (abs (- (car x) (cadr x)) ))
               (diff1p? (cdr x)) )))



    Gauche Scheme

    (define (diff1? xs)
       (every
         (lambda (a b) (= 1 (abs (- a b))))
         xs
         (cdr xs)))

    (diff1? '(2 3 4 3 4 5 6 7 8 9 8))     ===>  #t

    (diff1? '(2 3 4 3 4 5 6 7 8 9 8 0))   ===>  #f



    in Scheme (Gauche), is there a way to write it more like this ?

    def diff1(x):
    return all( abs(x[i] - x[i+1]) == 1 for i in range(len(x)-1) )

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)