| Deutsch English Français Italiano |
|
<87o782h5mh.fsf@axel-reichert.de> View for Bookmarking (what is this?) Look up another Usenet article |
Path: ...!eternal-september.org!feeder3.eternal-september.org!news.eternal-september.org!.POSTED!not-for-mail
From: Axel Reichert <mail@axel-reichert.de>
Newsgroups: comp.lang.lisp
Subject: Tail call recursion macro with named-let in Emacs Lisp (was: A simple lisp problem.)
Date: Sat, 15 Jun 2024 12:17:42 +0200
Organization: A noiseless patient Spider
Lines: 107
Message-ID: <87o782h5mh.fsf@axel-reichert.de>
References: <v4j2p1$3a0lu$1@dont-email.me>
MIME-Version: 1.0
Content-Type: text/plain
Injection-Date: Sat, 15 Jun 2024 12:17:45 +0200 (CEST)
Injection-Info: dont-email.me; posting-host="ecc537ebfc5213417124a7d49f80461d";
logging-data="3599724"; mail-complaints-to="abuse@eternal-september.org"; posting-account="U2FsdGVkX1+7OHUeTZeUZhFwtl3sheiI2hxOgdBb2rM="
User-Agent: Gnus/5.13 (Gnus v5.13) Emacs/28.2 (gnu/linux)
Cancel-Lock: sha1:EUzXnOCANkfSpnPRn7tT+1oIZQ0=
sha1:3HsDE+X2aP56zMsPdpTBfA5uypM=
Bytes: 3880
"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