all messages for Emacs-related lists mirrored at yhetil.org
 help / color / mirror / code / Atom feed
* How to mapcar or across a list?
@ 2015-07-15 20:14 Marcin Borkowski
  2015-07-15 20:42 ` Rasmus
                   ` (3 more replies)
  0 siblings, 4 replies; 24+ messages in thread
From: Marcin Borkowski @ 2015-07-15 20:14 UTC (permalink / raw
  To: Help Gnu Emacs mailing list

Hi there,

so here's my problem: I have a list of Boolean values, and I want to
`mapcar' an `or' across it (IOW, I want to test whether at least one of
them is true).  Of course, (apply #'or my-list) does not work.  Of
course, I can (cl-reduce (lambda (x y) (or x y)) my-list) -- but is
there a better method?

BTW, my-list doesn't really exist: it is a result of `mapcar'ing
a function taking some value and yielding a Boolean value, so bonus
points if the method does not process the whole list.

(Note: I just noticed that my-list is in fact sorted so that all true
values (if any) will occur at the beginning anyway, so I can just test
the first one.  This means that my problem is purely academic, though
still interesting, I guess.)

Best,

-- 
Marcin Borkowski
http://octd.wmi.amu.edu.pl/en/Marcin_Borkowski
Faculty of Mathematics and Computer Science
Adam Mickiewicz University



^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: How to mapcar or across a list?
  2015-07-15 20:14 How to mapcar or across a list? Marcin Borkowski
@ 2015-07-15 20:42 ` Rasmus
  2015-07-15 20:55   ` Marcin Borkowski
  2015-07-15 22:21 ` Emanuel Berg
                   ` (2 subsequent siblings)
  3 siblings, 1 reply; 24+ messages in thread
From: Rasmus @ 2015-07-15 20:42 UTC (permalink / raw
  To: help-gnu-emacs

Marcin Borkowski <mbork@mbork.pl> writes:

> Hi there,
>
> so here's my problem: I have a list of Boolean values, and I want to
> `mapcar' an `or' across it (IOW, I want to test whether at least one of
> them is true).  Of course, (apply #'or my-list) does not work.  Of
> course, I can (cl-reduce (lambda (x y) (or x y)) my-list) -- but is
> there a better method?
>
> BTW, my-list doesn't really exist: it is a result of `mapcar'ing
> a function taking some value and yielding a Boolean value, so bonus
> points if the method does not process the whole list.

Does cl-some and cl-every fit the bill?

Rasmus

-- 
Vote for Dick Taid in an election near you!




^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: How to mapcar or across a list?
       [not found] <mailman.6968.1436991281.904.help-gnu-emacs@gnu.org>
@ 2015-07-15 20:45 ` Pascal J. Bourguignon
  2015-07-15 21:02   ` Marcin Borkowski
  2015-07-15 23:56 ` Barry Margolin
  1 sibling, 1 reply; 24+ messages in thread
From: Pascal J. Bourguignon @ 2015-07-15 20:45 UTC (permalink / raw
  To: help-gnu-emacs

Marcin Borkowski <mbork@mbork.pl> writes:

> Hi there,
>
> so here's my problem: I have a list of Boolean values, and I want to
> `mapcar' an `or' across it (IOW, I want to test whether at least one of
> them is true).  Of course, (apply #'or my-list) does not work.  Of
> course, I can (cl-reduce (lambda (x y) (or x y)) my-list) -- but is
> there a better method?
>
> BTW, my-list doesn't really exist: it is a result of `mapcar'ing
> a function taking some value and yielding a Boolean value, so bonus
> points if the method does not process the whole list.

It's too late, lisp is not a lazy language.

> (Note: I just noticed that my-list is in fact sorted so that all true
> values (if any) will occur at the beginning anyway, so I can just test
> the first one.  This means that my problem is purely academic, though
> still interesting, I guess.)

First you want to reduce a list of booleans to a single value, so what
makes you thing that mapcar is the right too for that?  There's the word
"map" in "mapcar", didn't you notice?

       map           reduce

    x1 --> r1       x1 -\
    x2 --> r2       x2 --+-> r
    x3 --> r3       x3 -/

Also, mapcar allocates a new list of same length as the original list
for the result, so it is costly.  At least, you could consider mapc.



With mapc (or mapcar) you could perform your task with some cl magic:

    (require 'cl)
    (setf lexical-binding t)

    (defun* or-all (list)
       (mapc (lambda (element)
                (when element
                  (return-from or-all t)))
             list)
       nil)

    (defun* and-all (list)
       (mapc (lambda (element)
                (unless element
                  (return-from and-all nil)))
             list)
       t)

    (or-all '(nil nil nil)) --> nil
    (or-all '(nil t   nil)) --> t 

    (and-all '(t   t   t)) --> t
    (and-all '(t   nil t)) --> nil 


But I fail to see how it's better than:

    (defun* or-all (list)
       (reduce (lambda (a b) (or a b)) list))

    (defun* and-all (list)
       (reduce (lambda (a b) (and a b)) list))


or than just:

   (some     'identity '(nil nil nil)) --> nil
   (some     'identity '(nil t   nil)) --> t
   (some     'identity '(t   t   t  )) --> t

   (every    'identity '(nil nil nil)) --> nil
   (every    'identity '(t   nil t  )) --> nil 
   (every    'identity '(t   t   t  )) --> t

There are also the negations:

   (notevery 'identity '(nil nil nil)) --> t
   (notevery 'identity '(nil t   nil)) --> t
   (notevery 'identity '(t   t   t  )) --> nil

   (notany   'identity '(nil nil nil)) --> t
   (notany   'identity '(t   nil t  )) --> nil 
   (notany   'identity '(t   t   t  )) --> nil


And since your booleans are computed by a function with mapcar, you
could avoid computing this function for all the elements of the original
list, and you could avoid allocating the temporary list by having some
call this function:


So instead of:

    (reduce (lambda (a b) (or a b))
            (mapcar 'complex-and-lengthy-predicate list))

use:

    (some 'complex-and-lengthy-predicate list)



For example:
    (some (lambda (x) (print x) (evenp x)) '(1 2 3 5 7 9 11))
prints:
    1

    2
--> t


Learn more about Common Lisp:
http://www.lispworks.com/documentation/HyperSpec/Front/
http://cliki.net/

-- 
__Pascal Bourguignon__                 http://www.informatimago.com/
“The factory of the future will have only two employees, a man and a
dog. The man will be there to feed the dog. The dog will be there to
keep the man from touching the equipment.” -- Carl Bass CEO Autodesk


^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: How to mapcar or across a list?
  2015-07-15 20:42 ` Rasmus
@ 2015-07-15 20:55   ` Marcin Borkowski
  0 siblings, 0 replies; 24+ messages in thread
From: Marcin Borkowski @ 2015-07-15 20:55 UTC (permalink / raw
  To: help-gnu-emacs


On 2015-07-15, at 22:42, Rasmus <rasmus@gmx.us> wrote:

> Marcin Borkowski <mbork@mbork.pl> writes:
>
>> Hi there,
>>
>> so here's my problem: I have a list of Boolean values, and I want to
>> `mapcar' an `or' across it (IOW, I want to test whether at least one of
>> them is true).  Of course, (apply #'or my-list) does not work.  Of
>> course, I can (cl-reduce (lambda (x y) (or x y)) my-list) -- but is
>> there a better method?
>>
>> BTW, my-list doesn't really exist: it is a result of `mapcar'ing
>> a function taking some value and yielding a Boolean value, so bonus
>> points if the method does not process the whole list.
>
> Does cl-some and cl-every fit the bill?

Ha.  Their only drawback is that I didn't know about them...

> Rasmus

Thanks!

-- 
Marcin Borkowski
http://octd.wmi.amu.edu.pl/en/Marcin_Borkowski
Faculty of Mathematics and Computer Science
Adam Mickiewicz University



^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: How to mapcar or across a list?
  2015-07-15 20:45 ` How to mapcar or across a list? Pascal J. Bourguignon
@ 2015-07-15 21:02   ` Marcin Borkowski
  0 siblings, 0 replies; 24+ messages in thread
From: Marcin Borkowski @ 2015-07-15 21:02 UTC (permalink / raw
  To: help-gnu-emacs


On 2015-07-15, at 22:45, Pascal J. Bourguignon <pjb@informatimago.com> wrote:

> Marcin Borkowski <mbork@mbork.pl> writes:
>
>> Hi there,
>>
>> so here's my problem: I have a list of Boolean values, and I want to
>> `mapcar' an `or' across it (IOW, I want to test whether at least one of
>> them is true).  Of course, (apply #'or my-list) does not work.  Of
>> course, I can (cl-reduce (lambda (x y) (or x y)) my-list) -- but is
>> there a better method?
>>
>> BTW, my-list doesn't really exist: it is a result of `mapcar'ing
>> a function taking some value and yielding a Boolean value, so bonus
>> points if the method does not process the whole list.
>
> It's too late, lisp is not a lazy language.

Well, *after* I mapcar then it's too late, but your own answer (below)
shows that mapcar itself is not necessary (as I suspected).

>> (Note: I just noticed that my-list is in fact sorted so that all true
>> values (if any) will occur at the beginning anyway, so I can just test
>> the first one.  This means that my problem is purely academic, though
>> still interesting, I guess.)
>
> First you want to reduce a list of booleans to a single value, so what
> makes you thing that mapcar is the right too for that?  There's the word
> "map" in "mapcar", didn't you notice?

My bad, you're right, of course.

>        map           reduce
>
>     x1 --> r1       x1 -\
>     x2 --> r2       x2 --+-> r
>     x3 --> r3       x3 -/

Please, do not shame a mathematician.  I hope notany (pun intended;-))
of my students will read this...

> Also, mapcar allocates a new list of same length as the original list
> for the result, so it is costly.  At least, you could consider mapc.

True.

> With mapc (or mapcar) you could perform your task with some cl magic:
>
>     (require 'cl)
>     (setf lexical-binding t)
>
>     (defun* or-all (list)
>        (mapc (lambda (element)
>                 (when element
>                   (return-from or-all t)))
>              list)
>        nil)
>
>     (defun* and-all (list)
>        (mapc (lambda (element)
>                 (unless element
>                   (return-from and-all nil)))
>              list)
>        t)
>
>     (or-all '(nil nil nil)) --> nil
>     (or-all '(nil t   nil)) --> t 
>
>     (and-all '(t   t   t)) --> t
>     (and-all '(t   nil t)) --> nil 
>
>
> But I fail to see how it's better than:
>
>     (defun* or-all (list)
>        (reduce (lambda (a b) (or a b)) list))
>
>     (defun* and-all (list)
>        (reduce (lambda (a b) (and a b)) list))

Of course, but - as I expected - someone already felt that there should
be a more elegant way, and hence the abstraction called `some', `every' etc.

> or than just:
>
>    (some     'identity '(nil nil nil)) --> nil
>    (some     'identity '(nil t   nil)) --> t
>    (some     'identity '(t   t   t  )) --> t
>
>    (every    'identity '(nil nil nil)) --> nil
>    (every    'identity '(t   nil t  )) --> nil 
>    (every    'identity '(t   t   t  )) --> t
>
> There are also the negations:
>
>    (notevery 'identity '(nil nil nil)) --> t
>    (notevery 'identity '(nil t   nil)) --> t
>    (notevery 'identity '(t   t   t  )) --> nil
>
>    (notany   'identity '(nil nil nil)) --> t
>    (notany   'identity '(t   nil t  )) --> nil 
>    (notany   'identity '(t   t   t  )) --> nil
>
> And since your booleans are computed by a function with mapcar, you
> could avoid computing this function for all the elements of the original
> list, and you could avoid allocating the temporary list by having some
> call this function:
>
>
> So instead of:
>
>     (reduce (lambda (a b) (or a b))
>             (mapcar 'complex-and-lengthy-predicate list))
>
> use:
>
>     (some 'complex-and-lengthy-predicate list)

How cool.  Again: I felt that something like that should exist, so I'm
not surprised that it actually does.

> For example:
>     (some (lambda (x) (print x) (evenp x)) '(1 2 3 5 7 9 11))
> prints:
>     1
>
>     2
> --> t
>
>
> Learn more about Common Lisp:
> http://www.lispworks.com/documentation/HyperSpec/Front/
> http://cliki.net/

Well, maybe I should.  I have a bunch of students who want to study CL,
I should be able to help them;-).  (I read parts of the Cl spec, most of
"Practical Common Lisp" and small parts of "On Lisp", but that was a few
years ago...)

Thanks again,

-- 
Marcin Borkowski
http://octd.wmi.amu.edu.pl/en/Marcin_Borkowski
Faculty of Mathematics and Computer Science
Adam Mickiewicz University



^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: How to mapcar or across a list?
  2015-07-15 20:14 How to mapcar or across a list? Marcin Borkowski
  2015-07-15 20:42 ` Rasmus
@ 2015-07-15 22:21 ` Emanuel Berg
  2015-07-15 22:21 ` John Mastro
       [not found] ` <mailman.6977.1436998965.904.help-gnu-emacs@gnu.org>
  3 siblings, 0 replies; 24+ messages in thread
From: Emanuel Berg @ 2015-07-15 22:21 UTC (permalink / raw
  To: help-gnu-emacs

Marcin Borkowski <mbork@mbork.pl> writes:

> so here's my problem: I have a list of Boolean
> values, and I want to `mapcar' an `or' across it
> (IOW, I want to test whether at least one of them is
> true). Of course, (apply #'or my-list) does not
> work. Of course, I can (cl-reduce (lambda (x y) (or
> x y)) my-list) -- but is there a better method?

"Better" I don't know, but how about using your
new-found skills with the backquote? (`or' itself
should be used, of course.)

    (setq boolean-list-1 '(t   nil nil nil t   nil nil))
    (setq boolean-list-2 '(nil nil nil nil nil nil nil))

    (eval `(or ,@boolean-list-1)) ; t
    (eval `(or ,@boolean-list-2)) ; nil

> BTW, my-list doesn't really exist: it is a result of
> `mapcar'ing a function taking some value and
> yielding a Boolean value, so bonus points if the
> method does not process the whole list.

Check. It is a consequence of using `or', which is
"short circuited" as it is called in other languages,
perhaps in Lisp too - I've read
"conditional evaluation" as well in AI books, but
perhaps that is something else.

Anyway:

    (or t undefined) ; t
    (or undefined t) ; error: (void-variable undefined) in eval

To verify, append "undefined" to "boolean-list-1"
(no error) - but, there will be an error (the same as
above) if appended to "boolean-list-2"!

... I suppose now would be a good time for you to hand
over them points!

-- 
underground experts united
http://user.it.uu.se/~embe8573




^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: How to mapcar or across a list?
  2015-07-15 20:14 How to mapcar or across a list? Marcin Borkowski
  2015-07-15 20:42 ` Rasmus
  2015-07-15 22:21 ` Emanuel Berg
@ 2015-07-15 22:21 ` John Mastro
       [not found] ` <mailman.6977.1436998965.904.help-gnu-emacs@gnu.org>
  3 siblings, 0 replies; 24+ messages in thread
From: John Mastro @ 2015-07-15 22:21 UTC (permalink / raw
  To: Help Gnu Emacs mailing list

> so here's my problem: I have a list of Boolean values, and I want to
> `mapcar' an `or' across it (IOW, I want to test whether at least one of
> them is true).  Of course, (apply #'or my-list) does not work.  Of
> course, I can (cl-reduce (lambda (x y) (or x y)) my-list) -- but is
> there a better method?

You've already gotten good answers from others but, for the sake of one
more option, you could also accomplish this with `cl-loop' (which, of
course, some love and some hate).

It would end up looking something like:

(cl-loop for item in list
         thereis (your-predicate item))

If the predicate would be `(not (null item))', you can drop it entirely
(as you would expect):

(cl-loop for item in list thereis item)

-- 
john



^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: How to mapcar or across a list?
       [not found] ` <mailman.6977.1436998965.904.help-gnu-emacs@gnu.org>
@ 2015-07-15 22:28   ` Pascal J. Bourguignon
  2015-07-15 22:36     ` Emanuel Berg
                       ` (2 more replies)
  0 siblings, 3 replies; 24+ messages in thread
From: Pascal J. Bourguignon @ 2015-07-15 22:28 UTC (permalink / raw
  To: help-gnu-emacs

Emanuel Berg <embe8573@student.uu.se> writes:

> Marcin Borkowski <mbork@mbork.pl> writes:
>
>> so here's my problem: I have a list of Boolean
>> values, and I want to `mapcar' an `or' across it
>> (IOW, I want to test whether at least one of them is
>> true). Of course, (apply #'or my-list) does not
>> work. Of course, I can (cl-reduce (lambda (x y) (or
>> x y)) my-list) -- but is there a better method?
>
> "Better" I don't know, but how about using your
> new-found skills with the backquote? (`or' itself
> should be used, of course.)
>
>     (setq boolean-list-1 '(t   nil nil nil t   nil nil))
>     (setq boolean-list-2 '(nil nil nil nil nil nil nil))
>
>     (eval `(or ,@boolean-list-1)) ; t
>     (eval `(or ,@boolean-list-2)) ; nil

Nope.  

It's always a bad idea to use eval, because eval evalutes in the nil
environment, not in the local lexical envrionment.

Your example works because those lists are literal, but then you don't
need or to know the answer.

In pratice, translating:

   (flet ((some-complex-predicate (x) …))
     (let ((some-data (generate-some-data)))
        (some (function some-complex-predicate) some-data)))

to:

   (flet ((some-complex-predicate (x) …))
     (let ((some-data (generate-some-data)))
       (eval `(or ,@(mapcar (lambda (data)
                               `(some-complex-predicate ',data))
                            some-data)))))

fails lamentably.  

It is also very slow, since it makes you build a new list,
and it makes you interpret, or worse, compile, some new code!

-- 
__Pascal Bourguignon__                 http://www.informatimago.com/
“The factory of the future will have only two employees, a man and a
dog. The man will be there to feed the dog. The dog will be there to
keep the man from touching the equipment.” -- Carl Bass CEO Autodesk


^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: How to mapcar or across a list?
  2015-07-15 22:28   ` Pascal J. Bourguignon
@ 2015-07-15 22:36     ` Emanuel Berg
  2015-07-15 22:57     ` never use `eval' (was: Re: How to mapcar or across a list?) Emanuel Berg
       [not found]     ` <mailman.6982.1437001117.904.help-gnu-emacs@gnu.org>
  2 siblings, 0 replies; 24+ messages in thread
From: Emanuel Berg @ 2015-07-15 22:36 UTC (permalink / raw
  To: help-gnu-emacs

"Pascal J. Bourguignon" <pjb@informatimago.com>
writes:

> It's always a bad idea to use eval, because eval
> evalutes in the nil environment, not in the local
> lexical envrionment.
>
> Your example works because those lists are literal,
> but then you don't need or to know the answer.

OK, what about doing `cl-dolist' which we also had in
a recent discussion?

    (cl-dolist (b boolean-list-1) (when b (cl-return t))) ; t
    (cl-dolist (b boolean-list-2) (when b (cl-return t))) ; nil

-- 
underground experts united
http://user.it.uu.se/~embe8573




^ permalink raw reply	[flat|nested] 24+ messages in thread

* never use `eval' (was: Re: How to mapcar or across a list?)
  2015-07-15 22:28   ` Pascal J. Bourguignon
  2015-07-15 22:36     ` Emanuel Berg
@ 2015-07-15 22:57     ` Emanuel Berg
  2015-07-15 23:27       ` John Mastro
       [not found]     ` <mailman.6982.1437001117.904.help-gnu-emacs@gnu.org>
  2 siblings, 1 reply; 24+ messages in thread
From: Emanuel Berg @ 2015-07-15 22:57 UTC (permalink / raw
  To: help-gnu-emacs

"Pascal J. Bourguignon" <pjb@informatimago.com>
writes:

> It's always a bad idea to use eval, because eval
> evalutes in the nil environment, not in the local
> lexical envrionment.

I have used `eval' eight times in my current setup of
75 files.

Tho some of this code I wrote several years ago, I'd
be happy to correct it. Any hints - general or
specific - are appreciated.

;; From: http://user.it.uu.se/~embe8573/conf/emacs-init/faces.el

(defun set-color-face (name front &optional bold back)
  (eval `(defvar ,(make-symbol name)))
  (eval `(setq ,(intern name)
               `((t (:foreground ,front
                     :background ,back
                     :bold       ,bold) )))))

(defvar cf-black)   (set-color-face "cf-black"   "black")
(defvar cf-red)     (set-color-face "cf-red"     "red")
;; ...

;; From: http://user.it.uu.se/~embe8573/conf/emacs-init/ide/lisp.el

(defun do-repeat-complex-command ()
  (interactive)
  (eval (car command-history) ))

;; From: http://user.it.uu.se/~embe8573/conf/emacs-init/isbn.el

(defun book-page-to-bibtex ()
  (interactive)
  (save-excursion
    (goto-char (point-min))
    (let ((title (get-title))
          (data  (mapcar 'key-value
                         '("authors?" "publisher" "published" "isbn-10")) ))
      (eval `(create-book nil ,title ,@data) )))) ; don't INSERT

;; From: http://user.it.uu.se/~embe8573/conf/emacs-init/match-data-format.el

(defun match-data-format (data match format-str)
  (save-match-data
    (string-match match data)
    (eval `(message format-str ,@(make-match-list 1 data) ))))

;; From: http://user.it.uu.se/~embe8573/conf/emacs-init/wrap-search.el

(defun wrap-search-again (prefix)
  "Search again for the most recent search string of `wrap-search'.
Use \\[universal-argument] \(to set the PREFIX\) to toggle case sensitiveness."
  (interactive "p")
  (let ((cmd (cl-dolist (cmd command-history)
               (if (and (eq (car cmd) 'wrap-search)
                        (not (string= (cl-caddr cmd) "")) )
                   (cl-return cmd) ))))
    (if cmd
        (if (eq prefix 4)
            (let*((old-prefix (cadr  cmd))
                  (search-str (cl-caddr cmd))
                  (new-prefix (if (eq old-prefix 4) 1 4))
                  (final-cmd  `(wrap-search ,new-prefix ,search-str)) )
              (setq command-history (cons final-cmd command-history))
              (eval final-cmd) )
          (eval cmd) )
      (message " No previous search.") )))

-- 
underground experts united
http://user.it.uu.se/~embe8573




^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: never use `eval' (was: Re: How to mapcar or across a list?)
  2015-07-15 22:57     ` never use `eval' (was: Re: How to mapcar or across a list?) Emanuel Berg
@ 2015-07-15 23:27       ` John Mastro
  2015-07-15 23:57         ` Emanuel Berg
       [not found]         ` <mailman.6984.1437004760.904.help-gnu-emacs@gnu.org>
  0 siblings, 2 replies; 24+ messages in thread
From: John Mastro @ 2015-07-15 23:27 UTC (permalink / raw
  To: help-gnu-emacs@gnu.org

> Tho some of this code I wrote several years ago, I'd
> be happy to correct it. Any hints - general or
> specific - are appreciated.

> (defun set-color-face (name front &optional bold back)
>   (eval `(defvar ,(make-symbol name)))
>   (eval `(setq ,(intern name)
>                `((t (:foreground ,front
>                      :background ,back
>                      :bold       ,bold) )))))
>
> (defvar cf-black)   (set-color-face "cf-black"   "black")
> (defvar cf-red)     (set-color-face "cf-red"     "red")

I would use something like this:

(defmacro define-color-face (name front &optional bold back)
  `(defvar ,name
     (quote ((t (:foreground ,front
                             :background ,back
                             :bold ,bold))))))

(define-color-face cf-black "black")

> (defun book-page-to-bibtex ()
>   (interactive)
>   (save-excursion
>     (goto-char (point-min))
>     (let ((title (get-title))
>           (data  (mapcar 'key-value
>                          '("authors?" "publisher" "published" "isbn-10")) ))
>       (eval `(create-book nil ,title ,@data) )))) ; don't INSERT

For this (and others like it) it looks like you could instead simply
use e.g. (apply #'create-book nil title data)

-- 
john



^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: How to mapcar or across a list?
       [not found] <mailman.6968.1436991281.904.help-gnu-emacs@gnu.org>
  2015-07-15 20:45 ` How to mapcar or across a list? Pascal J. Bourguignon
@ 2015-07-15 23:56 ` Barry Margolin
  1 sibling, 0 replies; 24+ messages in thread
From: Barry Margolin @ 2015-07-15 23:56 UTC (permalink / raw
  To: help-gnu-emacs

In article <mailman.6968.1436991281.904.help-gnu-emacs@gnu.org>,
 Marcin Borkowski <mbork@mbork.pl> wrote:

> Hi there,
> 
> so here's my problem: I have a list of Boolean values, and I want to
> `mapcar' an `or' across it (IOW, I want to test whether at least one of
> them is true).  Of course, (apply #'or my-list) does not work.  Of
> course, I can (cl-reduce (lambda (x y) (or x y)) my-list) -- but is
> there a better method?

"mapcar" is not the right metaphor. It's for running the same function 
separately on each element of a list, not for combining elements.

-- 
Barry Margolin, barmar@alum.mit.edu
Arlington, MA
*** PLEASE post questions in newsgroups, not directly to me ***


^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: never use `eval' (was: Re: How to mapcar or across a list?)
  2015-07-15 23:27       ` John Mastro
@ 2015-07-15 23:57         ` Emanuel Berg
  2015-07-16  0:18           ` John Mastro
       [not found]         ` <mailman.6984.1437004760.904.help-gnu-emacs@gnu.org>
  1 sibling, 1 reply; 24+ messages in thread
From: Emanuel Berg @ 2015-07-15 23:57 UTC (permalink / raw
  To: help-gnu-emacs

John Mastro <john.b.mastro@gmail.com> writes:

> I would use something like this:
>
> (defmacro ...

Macros never liked me, and that is mutual. Are they
really preferrable to `eval'?

> For this (and others like it)

... which are? Do you mean the eval + backquote combo?

> it looks like you could instead simply use e.g.
> (apply #'create-book nil title data)

This is what it looks like:

    (eval `(create-book nil ,title ,@data))

With `apply', won't the last "data" be a list, i.e.

    (a b ... n)

instead of

    a b ... n

?

-- 
underground experts united
http://user.it.uu.se/~embe8573




^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: never use `eval' (was: Re: How to mapcar or across a list?)
       [not found]     ` <mailman.6982.1437001117.904.help-gnu-emacs@gnu.org>
@ 2015-07-16  0:01       ` Barry Margolin
  2015-07-16  1:13         ` Emanuel Berg
  2015-07-16  0:22       ` Pascal J. Bourguignon
  1 sibling, 1 reply; 24+ messages in thread
From: Barry Margolin @ 2015-07-16  0:01 UTC (permalink / raw
  To: help-gnu-emacs

In article <mailman.6982.1437001117.904.help-gnu-emacs@gnu.org>,
 Emanuel Berg <embe8573@student.uu.se> wrote:

> "Pascal J. Bourguignon" <pjb@informatimago.com>
> writes:
> 
> > It's always a bad idea to use eval, because eval
> > evalutes in the nil environment, not in the local
> > lexical envrionment.
> 
> I have used `eval' eight times in my current setup of
> 75 files.
> 
> Tho some of this code I wrote several years ago, I'd
> be happy to correct it. Any hints - general or
> specific - are appreciated.
> 
> ;; From: http://user.it.uu.se/~embe8573/conf/emacs-init/faces.el
> 
> (defun set-color-face (name front &optional bold back)
>   (eval `(defvar ,(make-symbol name)))
>   (eval `(setq ,(intern name)
>                `((t (:foreground ,front
>                      :background ,back
>                      :bold       ,bold) )))))

(eval `(setq ,<expr> <val>))

can practically always be replaced with:

(setf (symbol-value <expr>) <val>)

-- 
Barry Margolin, barmar@alum.mit.edu
Arlington, MA
*** PLEASE post questions in newsgroups, not directly to me ***


^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: never use `eval' (was: Re: How to mapcar or across a list?)
       [not found]         ` <mailman.6984.1437004760.904.help-gnu-emacs@gnu.org>
@ 2015-07-16  0:04           ` Barry Margolin
  0 siblings, 0 replies; 24+ messages in thread
From: Barry Margolin @ 2015-07-16  0:04 UTC (permalink / raw
  To: help-gnu-emacs

In article <mailman.6984.1437004760.904.help-gnu-emacs@gnu.org>,
 Emanuel Berg <embe8573@student.uu.se> wrote:

> John Mastro <john.b.mastro@gmail.com> writes:
> 
> > I would use something like this:
> >
> > (defmacro ...
> 
> Macros never liked me, and that is mutual. Are they
> really preferrable to `eval'?
> 
> > For this (and others like it)
> 
> ... which are? Do you mean the eval + backquote combo?
> 
> > it looks like you could instead simply use e.g.
> > (apply #'create-book nil title data)
> 
> This is what it looks like:
> 
>     (eval `(create-book nil ,title ,@data))
> 
> With `apply', won't the last "data" be a list, i.e.
> 
>     (a b ... n)
> 
> instead of
> 
>     a b ... n
> 
> ?

No. apply spreads its last argument. You're thinking of funcall.

-- 
Barry Margolin, barmar@alum.mit.edu
Arlington, MA
*** PLEASE post questions in newsgroups, not directly to me ***


^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: never use `eval' (was: Re: How to mapcar or across a list?)
  2015-07-15 23:57         ` Emanuel Berg
@ 2015-07-16  0:18           ` John Mastro
  0 siblings, 0 replies; 24+ messages in thread
From: John Mastro @ 2015-07-16  0:18 UTC (permalink / raw
  To: help-gnu-emacs@gnu.org

>> I would use something like this:
>>
>> (defmacro ...
>
> Macros never liked me, and that is mutual. Are they
> really preferrable to `eval'?

Yes, I think so. I certainly don't see any advantages to `eval' where a
function or macro would work.

> This is what it looks like:
>
>     (eval `(create-book nil ,title ,@data))
>
> With `apply', won't the last "data" be a list, i.e.
>
>     (a b ... n)
>
> instead of
>
>     a b ... n

No, that's `funcall'. For example, these both evaluate to 6:

(apply #'+ '(1 2 3))
(apply #'+ 1 2 '(3))

-- 
john



^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: never use `eval'
       [not found]     ` <mailman.6982.1437001117.904.help-gnu-emacs@gnu.org>
  2015-07-16  0:01       ` Barry Margolin
@ 2015-07-16  0:22       ` Pascal J. Bourguignon
  2015-07-16  1:11         ` Emanuel Berg
  1 sibling, 1 reply; 24+ messages in thread
From: Pascal J. Bourguignon @ 2015-07-16  0:22 UTC (permalink / raw
  To: help-gnu-emacs

Emanuel Berg <embe8573@student.uu.se> writes:

> "Pascal J. Bourguignon" <pjb@informatimago.com>
> writes:
>
>> It's always a bad idea to use eval, because eval
>> evalutes in the nil environment, not in the local
>> lexical envrionment.
>
> I have used `eval' eight times in my current setup of
> 75 files.
>
> Tho some of this code I wrote several years ago, I'd
> be happy to correct it. Any hints - general or
> specific - are appreciated.
>
> ;; From: http://user.it.uu.se/~embe8573/conf/emacs-init/faces.el
>
> (defun set-color-face (name front &optional bold back)
>   (eval `(defvar ,(make-symbol name)))
>   (eval `(setq ,(intern name)
>                `((t (:foreground ,front
>                      :background ,back
>                      :bold       ,bold) )))))

As mentionned, eval setq can be replaced by (setf symbol-value) or even
set, if you like archaic forms.

For defvar, and in general for operations like this set-color-face that
_defines_ a symbol to bind it to some kind of object, what you should do
is to write a define macro instead.  

Also, your code is higly suspicious: why do you use make-symbol?  This
will create a _different_ symbol!

     (eq (make-symbol "x") (intern "x")) --> nil

therefore your defvar will be totally useless!

Instead, you can just give a symbol to the macro.

    (defmacro define-color-face (name front &optional bold back)
       `(progn
          (defvar ,name)
          (setq ,name '((t (:foreground ,front
                            :background ,back
                            :bold ,bold))))))

    (define-color-face cf-black "black")
    cf-black --> ((t (:foreground "black" :background nil :bold nil)))



> (defun do-repeat-complex-command ()
>   (interactive)
>   (eval (car command-history) ))

This use is acceptable.


> ;; From: http://user.it.uu.se/~embe8573/conf/emacs-init/isbn.el
>
> (defun book-page-to-bibtex ()
>   (interactive)
>   (save-excursion
>     (goto-char (point-min))
>     (let ((title (get-title))
>           (data  (mapcar 'key-value
>                          '("authors?" "publisher" "published" "isbn-10")) ))
>       (eval `(create-book nil ,title ,@data) )))) ; don't INSERT

Just use apply!  

        (apply (function create-book) nil title data)


> ;; From: http://user.it.uu.se/~embe8573/conf/emacs-init/match-data-format.el
>
> (defun match-data-format (data match format-str)
>   (save-match-data
>     (string-match match data)
>     (eval `(message format-str ,@(make-match-list 1 data) ))))

Use apply.


> ;; From: http://user.it.uu.se/~embe8573/conf/emacs-init/wrap-search.el
>
> (defun wrap-search-again (prefix)
>   "Search again for the most recent search string of `wrap-search'.
> Use \\[universal-argument] \(to set the PREFIX\) to toggle case sensitiveness."
>   (interactive "p")
>   (let ((cmd (cl-dolist (cmd command-history)
>                (if (and (eq (car cmd) 'wrap-search)
>                         (not (string= (cl-caddr cmd) "")) )
>                    (cl-return cmd) ))))
>     (if cmd
>         (if (eq prefix 4)
>             (let*((old-prefix (cadr  cmd))
>                   (search-str (cl-caddr cmd))
>                   (new-prefix (if (eq old-prefix 4) 1 4))
>                   (final-cmd  `(wrap-search ,new-prefix ,search-str)) )
>               (setq command-history (cons final-cmd command-history))
>               (eval final-cmd) )

Use apply.

>           (eval cmd) )

Ok for eval.

>       (message " No previous search.") )))


Notice:

    command-history is a variable defined in `C source code'.
    Its value is shown below.

    Documentation:
    List of recent commands that read arguments from terminal.
    Each command is represented as a form to evaluate.

So clearly, (eval (first command-history)) is vetted.


-- 
__Pascal Bourguignon__                 http://www.informatimago.com/
“The factory of the future will have only two employees, a man and a
dog. The man will be there to feed the dog. The dog will be there to
keep the man from touching the equipment.” -- Carl Bass CEO Autodesk


^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: never use `eval'
  2015-07-16  0:22       ` Pascal J. Bourguignon
@ 2015-07-16  1:11         ` Emanuel Berg
  0 siblings, 0 replies; 24+ messages in thread
From: Emanuel Berg @ 2015-07-16  1:11 UTC (permalink / raw
  To: help-gnu-emacs

"Pascal J. Bourguignon" <pjb@informatimago.com>
writes:

> As mentioned, eval setq can be replaced by (setf
> symbol-value) or even set, if you like
> archaic forms.

Do I? Or do you? What do you mean?

But: which case is "eval setq"? From reading your
post, there are two OK uses, one that should be
a macro, and the rest `apply'?

> For defvar, and in general for operations like this
> set-color-face that _defines_ a symbol to bind it to
> some kind of object, what you should do is to write
> a define macro instead.

OK, thanks to all of you!

*Define* is exactly what I wanted to do, but I didn't
succeed and that is the reason for all those `defvars'
in the original code. The line you mentioned is
probably a leftover from an attempt to not have to
have those. Ironically, instead the macro is exactly
what I needed to not have to have them.

Now it looks ten times better:

    http://user.it.uu.se/~embe8573/conf/emacs-init/faces.el

> Notice:
>
>     command-history is a variable defined in `C source
>     code'. Its value is shown below.
>
>     Documentation: List of recent commands that read
>     arguments from terminal. Each command is represented
>     as a form to evaluate.
>
> So clearly, (eval (first command-history))
> is vetted.

I see: the other `eval' that is also "vetted" also
stems from `command-history'.

-- 
underground experts united
http://user.it.uu.se/~embe8573




^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: never use `eval' (was: Re: How to mapcar or across a list?)
  2015-07-16  0:01       ` Barry Margolin
@ 2015-07-16  1:13         ` Emanuel Berg
  2015-07-17  0:55           ` Emanuel Berg
       [not found]           ` <mailman.7027.1437094623.904.help-gnu-emacs@gnu.org>
  0 siblings, 2 replies; 24+ messages in thread
From: Emanuel Berg @ 2015-07-16  1:13 UTC (permalink / raw
  To: help-gnu-emacs

Barry Margolin <barmar@alum.mit.edu> writes:

>> (defun set-color-face (name front &optional bold
>> back) (eval `(defvar ,(make-symbol name))) (eval
>> `(setq ,(intern name) `((t (:foreground ,front
>> :background ,back :bold ,bold) )))))
>
> (eval `(setq ,<expr> <val>))
>
> can practically always be replaced with:
>
> (setf (symbol-value <expr>) <val>)

Aha, that one! Got it. Please ignore previous question
which one is "eval setq".

-- 
underground experts united
http://user.it.uu.se/~embe8573




^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: never use `eval' (was: Re: How to mapcar or across a list?)
  2015-07-16  1:13         ` Emanuel Berg
@ 2015-07-17  0:55           ` Emanuel Berg
       [not found]           ` <mailman.7027.1437094623.904.help-gnu-emacs@gnu.org>
  1 sibling, 0 replies; 24+ messages in thread
From: Emanuel Berg @ 2015-07-17  0:55 UTC (permalink / raw
  To: help-gnu-emacs

Remember when I said the byte compiler should be more
offensive and offer more elaborate style warnings?

And this is a good example of that!

Even in the Emacs help

    (describe-function 'eval)

it doesn't say anything about the dangers of the
"nil environment":

    Evaluate FORM and return its value.
    If LEXICAL is t, evaluate using lexical scoping.
    LEXICAL can also be an actual lexical environment,
    in the form of an alist mapping symbols to
    their value.

This book -

    @book{artificial-intelligence-and-the-design,
      title      = {Artificial Intelligence and the Design of Expert Systems},
      author     = {George Luger and William Stubblefield},
      publisher  = {Benjamin-Cummings},
      year       = 1989,
      ISBN       = 0805301399
    }

- has a chapter on LISP. One of the first things
they mention is `eval', and they mention it in
connection to the `quote', eval "undoing" quote so you
get the feeling it is as indispensible. There are
several examples using it.

Their other LISP isn't that good either by the way.
There are several examples where they do (car l)
several times in one execution branch instead of
`let'ing it be a local identifier; and, they speak
appreciatively of all the different types of recursion
(tail etc.) but at least so far hasn't mentioned (and
will they?) anything of the drawbacks of recursion in
terms of efficiency. (When I started out with Lisp,
coming "closest" from SML of the languages I did
then, I did recursion all the time by the way.)

-- 
underground experts united
http://user.it.uu.se/~embe8573




^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: never use `eval'
       [not found]           ` <mailman.7027.1437094623.904.help-gnu-emacs@gnu.org>
@ 2015-07-17  1:39             ` Pascal J. Bourguignon
  2015-07-17  2:16               ` Emanuel Berg
  2015-07-17  8:36               ` Barry Margolin
  0 siblings, 2 replies; 24+ messages in thread
From: Pascal J. Bourguignon @ 2015-07-17  1:39 UTC (permalink / raw
  To: help-gnu-emacs

Emanuel Berg <embe8573@student.uu.se> writes:

> Remember when I said the byte compiler should be more
> offensive and offer more elaborate style warnings?
>
> And this is a good example of that!
>
> Even in the Emacs help
>
>     (describe-function 'eval)
>
> it doesn't say anything about the dangers of the
> "nil environment":
>

   (eval FORM &optional LEXICAL)

>     Evaluate FORM and return its value.
>     If LEXICAL is t, evaluate using lexical scoping.
>     LEXICAL can also be an actual lexical environment,
>     in the form of an alist mapping symbols to
>     their value.

Doesn't seem to work:

    (setf lexical-binding t)

    (let ((x 42))
      (eval 'x t))
    Debugger entered--Lisp error: (void-variable x)

    (let ((x 42))
      (eval 'x nil))
    Debugger entered--Lisp error: (void-variable x)


> This book -
>
>     @book{artificial-intelligence-and-the-design,
>       title      = {Artificial Intelligence and the Design of Expert Systems},
>       author     = {George Luger and William Stubblefield},
>       publisher  = {Benjamin-Cummings},
>       year       = 1989,
>       ISBN       = 0805301399
>     }
>
> - has a chapter on LISP. One of the first things
> they mention is `eval', and they mention it in
> connection to the `quote', eval "undoing" quote so you
> get the feeling it is as indispensible. There are
> several examples using it.

Without lexical binding, there's no other environment than the global
environment, so it works better:

    (setf lexical-binding nil)

    (let ((x 42))
      (eval 'x)) 
    --> 42

Old lisp didn't have lexical binding (the famous funarg problem), and
this is why eval could have been more useful then.

But once lexical bindings were introduced in scheme and in Common Lisp,
and now in emacs lisp, you cannot be so lenient with eval.


What this means for the modern (emacs or Common) lisper?  That you
should take old lisp code that uses eval with some precautions, such as,
using (setf lexical-binding nil) or making sure that all the variables
are declared special.

See for example, the definition of the DEFINE macro in:
http://www.informatimago.com/develop/lisp/com/informatimago/small-cl-pgms/wang.html
which declares all the variables with DEFPARAMETER, hence declaiming
them to be special and of the dynamic binding persuasion.


> Their other LISP isn't that good either by the way.
> There are several examples where they do (car l)
> several times in one execution branch instead of
> `let'ing it be a local identifier;

It should not be a problem, at that time, or nowadays.  In the old days,
car and cdr were quite the optimized instructions: they corresponded to
7090 machine instructions, or to lisp machine instructions, so calling
(car x) several times was probably faster than storing the result in a
variable or even a register (because of the possible register spilling)
and calling it up again.

And nowadays, with honest compilers performing some common subexpression
elimination optimization, it should compile to exactly the same.



In the case of ccl, the code is quite similar, but indeed
(car x) (car x) will hit the (cache) memory each time, recomputing
the offset (assumedly in the pipeline so at zero cost) each time, while
using a temporary variable saves it in a register.

cl-user> (disassemble (compile nil (lambda (x) (list (car x) (car x) (car x)))))
L0
    (leaq (@ (:^ L0) (% rip)) (% fn))       ;     [0]
    (pushq (% rbp))                         ;     [7]
    (movq (% rsp) (% rbp))                  ;     [8]
    (pushq (% save0))                       ;    [11]
    (movq (% arg_z) (% save0))              ;    [13]
    (pushq (@ 5 (% save0)))                 ;    [16]
    (pushq (@ 5 (% save0)))                 ;    [20]
    (pushq (@ 5 (% save0)))                 ;    [24]
    (movl ($ 24) (% nargs))                 ;    [28]
    (leaq (@ (:^ L53) (% fn)) (% temp2))    ;    [33]
    (nop)                                   ;    [40]
    (nop)                                   ;    [43]
    (jmpq (@ .SPCONSLIST))                  ;    [46]
L53
    (leaq (@ (:^ L0) (% rip)) (% fn))       ;    [53]
    (popq (% save0))                        ;    [60]
    (leaveq)                                ;    [62]
    (retq)                                  ;    [63]
nil
cl-user> (disassemble (compile nil (lambda (x) (let ((y (car x))) (list y y y)))))
L0
    (leaq (@ (:^ L0) (% rip)) (% fn))       ;     [0]
    (pushq (% rbp))                         ;     [7]
    (movq (% rsp) (% rbp))                  ;     [8]
    (pushq (% arg_z))                       ;    [11]
    (pushq (% save0))                       ;    [12]
    (movq (@ 5 (% arg_z)) (% save0))        ;    [14]
    (pushq (% save0))                       ;    [18]
    (pushq (% save0))                       ;    [20]
    (pushq (% save0))                       ;    [22]
    (movl ($ 24) (% nargs))                 ;    [24]
    (leaq (@ (:^ L45) (% fn)) (% temp2))    ;    [29]
    (nop)                                   ;    [36]
    (jmpq (@ .SPCONSLIST))                  ;    [38]
L45
    (leaq (@ (:^ L0) (% rip)) (% fn))       ;    [45]
    (popq (% save0))                        ;    [52]
    (leaveq)                                ;    [54]
    (retq)                                  ;    [55]
nil
cl-user> (optimization)
(optimize (safety 0) (debug 0) (speed 3) (space 3) (compilation-speed 1))


But in the case of sbcl, it compiles to the exact same instructions
(the only difference being in the procedure entry, probably allocating
some space on the stack that is left unused in the let case).


diff -c /tmp/b /tmp/a
*** /tmp/b	Fri Jul 17 03:29:50 2015
--- /tmp/a	Fri Jul 17 03:29:56 2015
***************
*** 1,10 ****
! * (disassemble (compile nil (lambda (x) (list (car x) (car x) (car x)))))
  
  ; disassembly for (LAMBDA (X))
         488B4AF9         MOV RCX, [RDX-7]           ; no-arg-parsing entry point
-        488B7AF9         MOV RDI, [RDX-7]
-        488B72F9         MOV RSI, [RDX-7]
         488BD1           MOV RDX, RCX
         49896C2440       MOV [R12+64], RBP
         4D8B5C2418       MOV R11, [R12+24]
         498D5B30         LEA RBX, [R11+48]
--- 1,10 ----
! * (disassemble (compile nil (lambda (x) (let ((y (car x))) (list y y y)))))
  
  ; disassembly for (LAMBDA (X))
         488B4AF9         MOV RCX, [RDX-7]           ; no-arg-parsing entry point
         488BD1           MOV RDX, RCX
+        488BF9           MOV RDI, RCX
+        488BF1           MOV RSI, RCX
         49896C2440       MOV [R12+64], RBP
         4D8B5C2418       MOV R11, [R12+24]
         498D5B30         LEA RBX, [R11+48]
***************
*** 36,38 ****
--- 36,39 ----
         488D5B07         LEA RBX, [RBX+7]
         EBB3             JMP L0
  NIL
+ * 

Diff finished.  Fri Jul 17 03:30:00 2015


* (disassemble (compile nil (lambda (x) (list (car x) (car x) (car x)))))

; disassembly for (LAMBDA (X))
; 02AB631F:       488B4AF9         MOV RCX, [RDX-7]           ; no-arg-parsing entry point
;       23:       488B7AF9         MOV RDI, [RDX-7]
;       27:       488B72F9         MOV RSI, [RDX-7]
;       2B:       488BD1           MOV RDX, RCX
;       2E:       49896C2440       MOV [R12+64], RBP
;       33:       4D8B5C2418       MOV R11, [R12+24]
;       38:       498D5B30         LEA RBX, [R11+48]
;       3C:       49395C2420       CMP [R12+32], RBX
;       41:       7642             JBE L2
;       43:       49895C2418       MOV [R12+24], RBX
;       48:       498D5B07         LEA RBX, [R11+7]
;       4C: L0:   488BC3           MOV RAX, RBX
;       4F:       488950F9         MOV [RAX-7], RDX
;       53:       4883C010         ADD RAX, 16
;       57:       488940F1         MOV [RAX-15], RAX
;       5B:       488978F9         MOV [RAX-7], RDI
;       5F:       4883C010         ADD RAX, 16
;       63:       488940F1         MOV [RAX-15], RAX
;       67:       488970F9         MOV [RAX-7], RSI
;       6B:       48C7400117001020 MOV QWORD PTR [RAX+1], 537919511
;       73:       49316C2440       XOR [R12+64], RBP
;       78:       7402             JEQ L1
;       7A:       CC09             BREAK 9                    ; pending interrupt trap
;       7C: L1:   488BD3           MOV RDX, RBX
;       7F:       488BE5           MOV RSP, RBP
;       82:       F8               CLC
;       83:       5D               POP RBP
;       84:       C3               RET
;       85: L2:   6A30             PUSH 48
;       87:       4C8D1C2570724200 LEA R11, [#x427270]        ; alloc_tramp
;       8F:       41FFD3           CALL R11
;       92:       5B               POP RBX
;       93:       488D5B07         LEA RBX, [RBX+7]
;       97:       EBB3             JMP L0
NIL
* (disassemble (compile nil (lambda (x) (let ((y (car x))) (list y y y)))))

; disassembly for (LAMBDA (X))
; 02B12EAF:       488B4AF9         MOV RCX, [RDX-7]           ; no-arg-parsing entry point
;      EB3:       488BD1           MOV RDX, RCX
;      EB6:       488BF9           MOV RDI, RCX
;      EB9:       488BF1           MOV RSI, RCX
;      EBC:       49896C2440       MOV [R12+64], RBP
;      EC1:       4D8B5C2418       MOV R11, [R12+24]
;      EC6:       498D5B30         LEA RBX, [R11+48]
;      ECA:       49395C2420       CMP [R12+32], RBX
;      ECF:       7642             JBE L2
;      ED1:       49895C2418       MOV [R12+24], RBX
;      ED6:       498D5B07         LEA RBX, [R11+7]
;      EDA: L0:   488BC3           MOV RAX, RBX
;      EDD:       488950F9         MOV [RAX-7], RDX
;      EE1:       4883C010         ADD RAX, 16
;      EE5:       488940F1         MOV [RAX-15], RAX
;      EE9:       488978F9         MOV [RAX-7], RDI
;      EED:       4883C010         ADD RAX, 16
;      EF1:       488940F1         MOV [RAX-15], RAX
;      EF5:       488970F9         MOV [RAX-7], RSI
;      EF9:       48C7400117001020 MOV QWORD PTR [RAX+1], 537919511
;      F01:       49316C2440       XOR [R12+64], RBP
;      F06:       7402             JEQ L1
;      F08:       CC09             BREAK 9                    ; pending interrupt trap
;      F0A: L1:   488BD3           MOV RDX, RBX
;      F0D:       488BE5           MOV RSP, RBP
;      F10:       F8               CLC
;      F11:       5D               POP RBP
;      F12:       C3               RET
;      F13: L2:   6A30             PUSH 48
;      F15:       4C8D1C2570724200 LEA R11, [#x427270]        ; alloc_tramp
;      F1D:       41FFD3           CALL R11
;      F20:       5B               POP RBX
;      F21:       488D5B07         LEA RBX, [RBX+7]
;      F25:       EBB3             JMP L0
NIL
* 



In the case of emacs, being an implementation targetting a lisp machine
VM, the multiple (cdr x) version looks definitely more optimized (less
VM instructions), as expected.

(disassemble (byte-compile (lambda (x) (let ((y (car x))) (list y y y)))))
byte code:
  args: (x)
0       varref    x
1       car       
2       dup       
3       varbind   y
4       dup       
5       varref    y
6       list3     
7       unbind    1
8       return    

(disassemble (byte-compile (lambda (x) (list (car x) (car x) (car x)))))
byte code:
  args: (x)
0       varref    x
1       car       
2       varref    x
3       car       
4       varref    x
5       car       
6       list3     
7       return    



> and, they speak
> appreciatively of all the different types of recursion
> (tail etc.) but at least so far hasn't mentioned (and
> will they?) anything of the drawbacks of recursion in
> terms of efficiency. (When I started out with Lisp,
> coming "closest" from SML of the languages I did
> then, I did recursion all the time by the way.)

Again, if you have tail call optimization, then there's no recursion
occuring, and efficiency is equivalent to iteration (because in effect,
the compiler generates an iteration).

Of course, CL implementations and emacs lisp don't necessarily implement
TCO, so recursion could be less efficient.  But as long as response
times are below the 300 ms threshold, it's ok. ;-)

-- 
__Pascal Bourguignon__                 http://www.informatimago.com/
“The factory of the future will have only two employees, a man and a
dog. The man will be there to feed the dog. The dog will be there to
keep the man from touching the equipment.” -- Carl Bass CEO Autodesk


^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: never use `eval'
  2015-07-17  1:39             ` never use `eval' Pascal J. Bourguignon
@ 2015-07-17  2:16               ` Emanuel Berg
  2015-07-17  8:36               ` Barry Margolin
  1 sibling, 0 replies; 24+ messages in thread
From: Emanuel Berg @ 2015-07-17  2:16 UTC (permalink / raw
  To: help-gnu-emacs

"Pascal J. Bourguignon" <pjb@informatimago.com>
writes:

> It should not be a problem, at that time, or
> nowadays. In the old days, car and cdr were quite
> the optimized instructions: they corresponded to
> 7090 machine instructions, or to lisp machine
> instructions, so calling (car x) several times was
> probably faster than storing the result in
> a variable or even a register (because of the
> possible register spilling) and calling it up again.
>
> And nowadays, with honest compilers performing some
> common subexpression elimination optimization, it
> should compile to exactly the same.

Yeah, it is more a question of the code not looking
good and being difficult to work with, say for example
if the data item that once was (car l) changes - you
have to change all them places. You can do
search-and-replace but then you better catch them all
and since it is such a common thing you might catch
some unwanted fish in the bargain/net as well.

I don't really care about speed as such, but of course
I want to write efficient code. Still computers have
always been always fast enough for me, and I have used
old computers (5-10 years) all my "career". What I do
don't require speed it would seem and besides I don't
think I'm that slow just because I don't disassemble
code to compare which is preferable. I'm on the human
side of the equation. I can't even say how many cache
levels my architecture has. OK, 'lscpu' tells me
three, but you know what I'm saying.

> In the case of ccl, the code is quite similar, but
> indeed (car x) (car x) will hit the (cache) memory
> each time, recomputing the offset (assumedly in the
> pipeline so at zero cost) each time, while using
> a temporary variable saves it in a register.

"ccl" I don't know.

> But in the case of sbcl, it compiles to the exact
> same instructions (the only difference being in the
> procedure entry, probably allocating some space on
> the stack that is left unused in the let case).

Steel Bank Common Lisp!

    (defvar inferior-lisp-program)
    (setq inferior-lisp-program "/usr/bin/sbcl --noinform")

Did the US steel industry have a bank, which had its
own a dialect of LISP? Crazy times... Probably a lot
of job opportunities for real programmers in
those days.

> Again, if you have tail call optimization, then
> there's no recursion occuring, and efficiency is
> equivalent to iteration (because in effect, the
> compiler generates an iteration).

I do like some recursion which uses one function
(itself) and do recursion there. This recursion
pattern is cool:

    * empty list - nil
    * not empty list - do something with car
                     - do it again with cdr

The recursion I don't like is recursion which relies
on helper functions and "accumulator" variables and
the like - them arguments should be input data, not
"persistent variables" to carry state
between invocations.

Some recursion can be with an infix operator and then
do recursion divide-and-conquer both ways down
searching a tree, then leading up to the operands of
the function. It looks cool to be sure.

But the coolest search algorithm I've seen is
"candidate elimination" which searches search space
two ways, from the general to the specific, and
simultaneously, form the specific to the general, and
where they meet is the answer (no nodes left to go to
but to stay put).

This reminds me of what happened once in the FOSS
world when there was a non-free component of KDE,
namely Qt (pronounced "cute"?) - to counterpunch this,
two projects were launched: GNOME, to replace KDE -
i.e., the general approach; and, Harmony to replace Qt
(the specific). The end of the story is GNOME was
succesful (still not to my liking, but I don't like
the desktop at all); Harmony was dropped (?); and, Qt
turned free. It is just the way of the street. In
100 years the same thing will happen and people will
be angry and creative about it. Then and now and in
the future, the only thing bigger than the grievance
is the mirth.

> Of course, CL implementations and emacs lisp don't
> necessarily implement TCO, so recursion could be
> less efficient. But as long as response times are
> below the 300 ms threshold, it's ok. ;-)

Right. When the carpenter applies sandpaper to a piece
of wood, he doesn't think a person will get stuck by
every single chip he removes. But if he didn't do it
at all, and used the plank to lay a floor, the
shoe-less hippies and their dogs would cry out their
spits and angers...

"If everything can start anew, then everything
must continue."

-- 
underground experts united
http://user.it.uu.se/~embe8573




^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: never use `eval'
  2015-07-17  1:39             ` never use `eval' Pascal J. Bourguignon
  2015-07-17  2:16               ` Emanuel Berg
@ 2015-07-17  8:36               ` Barry Margolin
  2015-07-17  8:55                 ` Pascal J. Bourguignon
  1 sibling, 1 reply; 24+ messages in thread
From: Barry Margolin @ 2015-07-17  8:36 UTC (permalink / raw
  To: help-gnu-emacs

In article <87d1zrlfz6.fsf@kuiper.lan.informatimago.com>,
 "Pascal J. Bourguignon" <pjb@informatimago.com> wrote:

> Emanuel Berg <embe8573@student.uu.se> writes:
> 
> > Remember when I said the byte compiler should be more
> > offensive and offer more elaborate style warnings?
> >
> > And this is a good example of that!
> >
> > Even in the Emacs help
> >
> >     (describe-function 'eval)
> >
> > it doesn't say anything about the dangers of the
> > "nil environment":
> >
> 
>    (eval FORM &optional LEXICAL)
> 
> >     Evaluate FORM and return its value.
> >     If LEXICAL is t, evaluate using lexical scoping.
> >     LEXICAL can also be an actual lexical environment,
> >     in the form of an alist mapping symbols to
> >     their value.
> 
> Doesn't seem to work:
> 
>     (setf lexical-binding t)
> 
>     (let ((x 42))
>       (eval 'x t))
>     Debugger entered--Lisp error: (void-variable x)
> 
>     (let ((x 42))
>       (eval 'x nil))
>     Debugger entered--Lisp error: (void-variable x)

I don't think that could be expected to work. Since eval is a function, 
not a special form, there's no way it can access the lexical environment 
outside it. I assume the LEXICAL parameter means that it implements 
lexical binding in the code being evaluated, e.g. if you do:

(eval '(let ((x 42)) x) t)

it binds x lexically, not dynamically.

But it also says that LEXICAL can be an alist, so you could do:

(eval 'x '((x . 42)))

and it should return 42.

-- 
Barry Margolin, barmar@alum.mit.edu
Arlington, MA
*** PLEASE post questions in newsgroups, not directly to me ***


^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: never use `eval'
  2015-07-17  8:36               ` Barry Margolin
@ 2015-07-17  8:55                 ` Pascal J. Bourguignon
  0 siblings, 0 replies; 24+ messages in thread
From: Pascal J. Bourguignon @ 2015-07-17  8:55 UTC (permalink / raw
  To: help-gnu-emacs

Barry Margolin <barmar@alum.mit.edu> writes:

> In article <87d1zrlfz6.fsf@kuiper.lan.informatimago.com>,
>  "Pascal J. Bourguignon" <pjb@informatimago.com> wrote:
>> Doesn't seem to work:
>> 
>>     (setf lexical-binding t)
>> 
>>     (let ((x 42))
>>       (eval 'x t))
>>     Debugger entered--Lisp error: (void-variable x)
>> 
>>     (let ((x 42))
>>       (eval 'x nil))
>>     Debugger entered--Lisp error: (void-variable x)
>
> I don't think that could be expected to work. Since eval is a function, 
> not a special form, there's no way it can access the lexical environment 
> outside it. I assume the LEXICAL parameter means that it implements 
> lexical binding in the code being evaluated, e.g. if you do:
>
> (eval '(let ((x 42)) x) t)
>
> it binds x lexically, not dynamically.
>
> But it also says that LEXICAL can be an alist, so you could do:
>
> (eval 'x '((x . 42)))
>
> and it should return 42.

Oh!  I see…  

This is nice, to be able to pass an environment like this, because we
could write macros that would build a "lexical environment" to be passed
to eval…


-- 
__Pascal Bourguignon__                 http://www.informatimago.com/
“The factory of the future will have only two employees, a man and a
dog. The man will be there to feed the dog. The dog will be there to
keep the man from touching the equipment.” -- Carl Bass CEO Autodesk


^ permalink raw reply	[flat|nested] 24+ messages in thread

end of thread, other threads:[~2015-07-17  8:55 UTC | newest]

Thread overview: 24+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2015-07-15 20:14 How to mapcar or across a list? Marcin Borkowski
2015-07-15 20:42 ` Rasmus
2015-07-15 20:55   ` Marcin Borkowski
2015-07-15 22:21 ` Emanuel Berg
2015-07-15 22:21 ` John Mastro
     [not found] ` <mailman.6977.1436998965.904.help-gnu-emacs@gnu.org>
2015-07-15 22:28   ` Pascal J. Bourguignon
2015-07-15 22:36     ` Emanuel Berg
2015-07-15 22:57     ` never use `eval' (was: Re: How to mapcar or across a list?) Emanuel Berg
2015-07-15 23:27       ` John Mastro
2015-07-15 23:57         ` Emanuel Berg
2015-07-16  0:18           ` John Mastro
     [not found]         ` <mailman.6984.1437004760.904.help-gnu-emacs@gnu.org>
2015-07-16  0:04           ` Barry Margolin
     [not found]     ` <mailman.6982.1437001117.904.help-gnu-emacs@gnu.org>
2015-07-16  0:01       ` Barry Margolin
2015-07-16  1:13         ` Emanuel Berg
2015-07-17  0:55           ` Emanuel Berg
     [not found]           ` <mailman.7027.1437094623.904.help-gnu-emacs@gnu.org>
2015-07-17  1:39             ` never use `eval' Pascal J. Bourguignon
2015-07-17  2:16               ` Emanuel Berg
2015-07-17  8:36               ` Barry Margolin
2015-07-17  8:55                 ` Pascal J. Bourguignon
2015-07-16  0:22       ` Pascal J. Bourguignon
2015-07-16  1:11         ` Emanuel Berg
     [not found] <mailman.6968.1436991281.904.help-gnu-emacs@gnu.org>
2015-07-15 20:45 ` How to mapcar or across a list? Pascal J. Bourguignon
2015-07-15 21:02   ` Marcin Borkowski
2015-07-15 23:56 ` Barry Margolin

Code repositories for project(s) associated with this external index

	https://git.savannah.gnu.org/cgit/emacs.git
	https://git.savannah.gnu.org/cgit/emacs/org-mode.git

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.