#14 LAMBDA () transformation bug

open
nobody
None
5
2002-02-18
2002-02-18
No

I believe Series doesn't code-walk or transform
properly in the case below where I use LAMBDA both as
macro (equivalent to #'(lambda ...)) and in a ((lambda
(x) ...) y) form. Maybe it's a bug in CLISP?

I was trying to write a UNIQ filter as a macro (I
cannot use a function because &rest or &key wouldn't
work).

BTW, some unique/remove-duplicates functionality might
be worth including in the series package, since it's
already hidden in parts of it: SCAN-PLIST and
SCAN-ALIST remove duplicates. That's why (scan-alist l)
is not equivalent to (chunk 2 2 (scan l)).

The code is appended as a notice to this bug report
because Sourceforge doesn't preserve formatting in the
main text.

Series 2.2.5, clisp-2.27-win32 on MS-Windos-2k

Thanks for your help,
Jörg Höhle.

Discussion

  • Jörg Höhle

    Jörg Höhle - 2002-02-18

    Logged In: YES
    user_id=377168

    (defmacro uniq4 (items &key (key '#'identity) (test '#'eql)
    &allow-other-keys)
    ;;TODO evaluation order & eval-once
    ;;TODO &rest and apply #'make-hash-table to other keys
    (let ((seen (gensym "SEEN-")) (item (gensym "ITEM-")) )
    `(let ((,seen (make-hash-table :test ,test)))
    (split-if ,items
    (lambda (,item) ;doesn't work
    ;; the ((lambda ..) .) trick doesn't capture the
    variable elt
    ((lambda (elt)
    (unless (gethash elt ,seen) (setf (gethash elt
    ,seen) t)))
    (funcall ,key ,item)))))))

    [427]> (setq series:*series-expression-cache* t) ; don't get
    in my way
    T
    [428]> (uniq4 #z(1 2 3 4 2 3 6 4 2 7))
    ; works without surprises
    #Z(1 2 3 4 6 7) ;
    #Z(2 3 4 2)
    [429]> (collect (uniq4 #z(1 2 3 4 2 3 6 4 2 7)) )
    *** - EVAL: variable #:ITEM-41976 has no value
    > backtrace-1
    [...]
    EVAL frame for form
    (COMMON-LISP:LET*
    ((#:SEEN-41975 (MAKE-HASH-TABLE :TEST #'EQL))
    (ELT (COMMON-LISP:FUNCALL #'IDENTITY #:ITEM-41976)))
    ; ^^ this was incorrectly moved up to the level of the
    hash table.
    ; There of course, ITEM(-41976) is not yet defined.
    (COMMON-LISP:LET (#:OUT-41989)
    (SETQ #:OUT-41989
    (IF (NOT (GETHASH ELT #:SEEN-41975))
    (PROGN (SETF (GETHASH ELT #:SEEN-41975) T))))
    (COMMON-LISP:LET (#:OUT-41991)
    (SETQ #:OUT-41991 (LAMBDA (#:ITEM-41976) #:OUT-41989))
    ; ^^ item-41976 is defined here
    (COMMON-LISP:LET
    (#:LISTPTR-41984 #:ELEMENTS-41985 (#:LASTCONS-41973
    (LIST NIL))
    #:LST-41974)
    (DECLARE (TYPE LIST #:LISTPTR-41984) (TYPE CONS
    #:LASTCONS-41973)
    (TYPE LIST #:LST-41974))
    (SETQ #:LISTPTR-41984 '(1 2 3 4 2 3 6 4 2 7))
    (SETQ #:LST-41974 #:LASTCONS-41973)
    (TAGBODY #:LL-41992 (IF (ENDP #:LISTPTR-41984) (GO
    SERIES::END))
    (SETQ #:ELEMENTS-41985 (CAR #:LISTPTR-41984))
    (SETQ #:LISTPTR-41984 (CDR #:LISTPTR-41984))
    (IF (NOT (COMMON-LISP:FUNCALL #:OUT-41991
    #:ELEMENTS-41985))
    (GO #:SS-41982))
    (SETQ #:LASTCONS-41973
    (SETF (CDR #:LASTCONS-41973) (CONS #:ELEMENTS-41985
    NIL)))
    (GO #:SS-41982) #:SS-41982 (GO #:LL-41992) SERIES::END)
    (CDR #:LST-41974)))))
    EVAL frame for form (COLLECT (UNIQ4 (SERIES::LITERAL-SERIES
    '(1 2 3 4 2 3 6 4 2 7))))
    [...]
    [407]> (macroexpand-1 '(uniq4 #z(1 2 3) :test #'eql))
    (LET ((#:SEEN-41970 (MAKE-HASH-TABLE :TEST #'EQL)))
    (SPLIT-IF (SERIES::LITERAL-SERIES '(1 2 3))
    (LAMBDA (#:ITEM-41971)
    ((LAMBDA (ELT)
    (UNLESS (GETHASH ELT #:SEEN-41970) (SETF (GETHASH ELT
    #:SEEN-41970) T)))
    (FUNCALL #'IDENTITY #:ITEM-41971))))) ;
    T ; expansion looks ok

    Small variants of the macro do work:

    Here I'm using #'(lambda ...) instead of the (lambda ...)
    macro that
    ANSI-CL introduced.
    (defmacro uniq4 (items &key (key '#'identity) (test '#'eql)
    &allow-other-keys)
    ;;TODO evaluation order & eval-once
    ;;TODO &rest and apply make-hash-table to other keys
    (let ((seen (gensym "SEEN-")) (item (gensym "ITEM-")) )
    `(let ((,seen (make-hash-table :test ,test)))
    (split-if ,items
    #'(lambda (,item) ;seems to work
    ;; the ((lambda ..) .) trick doesn't capture the
    variable elt
    ((lambda (elt)
    (unless (gethash elt ,seen) (setf (gethash elt
    ,seen) t)))
    (funcall ,key ,item)))))))

    Here I'm using (LAMBDA ...) but not ((LAMBDA ...) ...):
    (defmacro uniq4 (items &key (key '#'identity) (test '#'eql)
    &allow-other-keys)
    ;;TODO evaluation order & eval-once
    ;;TODO &rest and apply make-hash-table to other keys
    (let ((table (gensym "TABLE-")) (item (gensym "ITEM-"))
    (elt (gensym "ELT-")))
    `(let ((,table (make-hash-table :test ,test)))
    (split-if ,items
    (lambda (,item &aux (,elt (funcall ,key ,item)))
    (print ,item)
    (unless (gethash ,elt ,table) (setf (gethash ,elt
    ,table) t)))))))

     
  • Rahul Jain

    Rahul Jain - 2002-02-24

    Logged In: YES
    user_id=246924

    it seems that the macroexpansion in SERIES doesn't recognize
    (lambda...) as a function form, but only recognizes
    (function...) as a function form.

     

Log in to post a comment.