Re: [pro] why :key arguments?

2011-07-05 Thread Tamas Papp
On Mon, 04 Jul 2011 20:11:56 +0300, Nikodemus Siivola wrote:

 On 4 July 2011 19:42, Pascal J. Bourguignon
 p...@informatimago.com wrote:
 
 I don't think so.
 
 I disagree strongly, and I'm pretty sure CLHS agrees with me, since it
 goes to the trouble of specifying what happens with FUNCALL.
 
 CLHS, DEFINE-COMPILER-MACRO: The whole argument is bound to the form
 argument that is passed to the compiler macro function. The remaining
 lambda-list parameters are specified as if this form contained the
 function name in the car and the actual arguments in the cdr, but if the
 car of the actual form is the symbol funcall, then the destructuring of
 the arguments is actually performed using its cddr instead.
 
 (I'm not really interested in fencing re. compiler-macros here, just
 trying to keep the record only moderately crooked. This is a sidetrack
 of epic proportions already: the OP asked about :KEY, not
 compiler-macros.)

I am very happy to learn about these things.  Currently I am working
on the algorithms and my main concern is to ensure correctness; speed
is secondary at this point, but even though I am not optimizing, I
want to keep my code optimizable later on.

My problem with the key argument is that it complicates the interface.  I 
would like to use the same interface for sample statistics and random 
variables, eg currently in CL-NUM-UTILS and CL-RANDOM I have

(mean #(1d0 2d0 3d0)) ; = 2, a sample mean
(mean (r-normal 2 1)) ; = 2d0, mean of a univariate normal distribution

If I had a :KEY argument, I would have to check that it is EQ to
#'identity or not provided in methods for random variables.

APPLY is not a major concern for me at the moment, all of these
functions have a fixed number of arguments (usually one or two).  So
compiler macros still look attractive: I guess I could just write them
for the function I define (eg MAP1), with the understanding that if
the user wants speed, he should stick to mapping with this function.

I also thought of the following possibility using runtime dispatch: 

(defstruct (w/key (:constructor w/key (key object)))
  key object)

(defgeneric mean (object)
  (:method ((obj w/key))
(mean-w/key (w/key-object obj) (w/key-key obj)))
  (:method ((obj sequence))
(/ (reduce #'+ obj) (length obj

(defmethod mean-w/key ((obj sequence) key)
  (/ (reduce #'+ obj :key key) (length obj)))

(mean #(1 2 3)); = 2
(mean (w/key #'1+ #(1 2 3)))   ; = 3

Tamas


___
pro mailing list
pro@common-lisp.net
http://lists.common-lisp.net/cgi-bin/mailman/listinfo/pro


[pro] why :key arguments?

2011-07-04 Thread Tamas Papp
Why do some CL library functions have :key arguments?

I am asking because I am working on some statistics functions, and the 
design choice came up.  Specifically, I can write functions like

(defun quantiles (sequence quantiles key (key #'identity))
   ...)

but it is a bit cumbersome.  I can make my code simpler by relying on 
calls like

(quantiles (map 'vector key vector) quantiles)

but this conses a bit more.  Not a major concern at the moment, but if it 
ever becomes one, I could just define a compiler macro to take care of 
forms like this and transform to something like

(quantile-with-keys vector quantiles key)

I also thought of defining something like

(defgeneric map1 (function object)
  (:method (function (list list))
 (mapcar function list))
  ...)

for the sole purpose of mapping objects to similar objects (eg lists to 
lists, arrays to arrays, etc) and allow it to be optimized away (like 
above) by compiler macros.

I would appreciate advice on this.  I am especially interested in the 
reason why some CL functions have :key arguments: is it because of 
efficiency, backward-compatibility/history, or something else?

Thanks,

Tamas


___
pro mailing list
pro@common-lisp.net
http://lists.common-lisp.net/cgi-bin/mailman/listinfo/pro


Re: [pro] why :key arguments?

2011-07-04 Thread Tamas Papp
On Mon, 04 Jul 2011 11:39:39 +0200, Hans Hübner wrote:

 On Mon, Jul 4, 2011 at 11:31 AM, Tamas Papp
 tkp...@gmail.com wrote:
 Why do some CL library functions have :key arguments?
 [...]
 but it is a bit cumbersome.  I can make my code simpler by relying on
 calls like

 (quantiles (map 'vector key vector) quantiles)
 
 This not only conses a bit more, it also duplicates traversal efforts
 - The original list must be traversed, and the consed-up list of key
 values as well.  I think it is prudent that the CL library functions
 offer ways to reduce consing for cases where a bit is too much (and a
 bit can become a lot if a program operates on long lists).

I understand this.  My main question is: why not do this with compiler 
macros?  Is there any reason for this, other than historical?

Note that I am not complaining about the standard, I just want to learn 
the reason for this design choice so that I can take it into account when 
writing my own libraries.

Best,

Tamas


___
pro mailing list
pro@common-lisp.net
http://lists.common-lisp.net/cgi-bin/mailman/listinfo/pro


Re: [pro] why :key arguments?

2011-07-04 Thread Stas Boukarev
Tamas Papp tkp...@gmail.com writes:

 On Mon, 04 Jul 2011 11:39:39 +0200, Hans Hübner wrote:

 On Mon, Jul 4, 2011 at 11:31 AM, Tamas Papp
 tkp...@gmail.com wrote:
 Why do some CL library functions have :key arguments?
 [...]
 but it is a bit cumbersome.  I can make my code simpler by relying on
 calls like

 (quantiles (map 'vector key vector) quantiles)
 
 This not only conses a bit more, it also duplicates traversal efforts
 - The original list must be traversed, and the consed-up list of key
 values as well.  I think it is prudent that the CL library functions
 offer ways to reduce consing for cases where a bit is too much (and a
 bit can become a lot if a program operates on long lists).

 I understand this.  My main question is: why not do this with compiler 
 macros?  Is there any reason for this, other than historical?
Because it's not easy to do with compiler macros.

 Note that I am not complaining about the standard, I just want to learn 
 the reason for this design choice so that I can take it into account when 
 writing my own libraries.
I find (foo sequence :key #'key) much nicer than
(foo (map 'sequence #'key sequence))

-- 
With best regards, Stas.

___
pro mailing list
pro@common-lisp.net
http://lists.common-lisp.net/cgi-bin/mailman/listinfo/pro


Re: [pro] why :key arguments?

2011-07-04 Thread Tamas Papp
On Mon, 04 Jul 2011 12:12:33 +0200, Svante Carl v. Erichsen wrote:

 -BEGIN PGP SIGNED MESSAGE-
 Hash: SHA1
 
 Am 04.07.2011 11:31, schrieb Tamas Papp:
 Why do some CL library functions have :key arguments?
 
 I am asking because I am working on some statistics functions, and the
 design choice came up.  Specifically, I can write functions like
 
 (defun quantiles (sequence quantiles key (key #'identity))
...)
 
 but it is a bit cumbersome.  I can make my code simpler by relying on
 calls like
 
 (quantiles (map 'vector key vector) quantiles)
 
 but this conses a bit more.
 
 Doesn't quantiles just pass the key to sort?  Is there some smarter
 algorithm I am not aware of right now?

Eg

@inproceedings{greenwald2001space,
  title={Space-efficient online computation of quantile summaries},
  author={Greenwald, M. and Khanna, S.},
  booktitle={ACM SIGMOD Record},
  volume={30},
  number={2},
  pages={58--66},
  year={2001},
  organization={ACM}
}

I am making quantiles a generic function: it should work on objects 
returned by the method above, and also on vectors.  My problem was that

(quantiles quantile-summary #(0.25 0.5 0.75) :key something)

makes little sense, since the summary is already accumulated.  So I will 
drop key for the moment, and will probably use compiler macros.

I also thought of a lazy solution that would just save the function and 
the object, and traverse once, eg

(quantiles (with-key function object) ...)

but I need to think about that.

 Also, passing the key lets you return the complete objects (or
 whatever), not just the keys as in the map call.  For example, compare

That's a good point, I didn't think of that.

Best,

Tamas


___
pro mailing list
pro@common-lisp.net
http://lists.common-lisp.net/cgi-bin/mailman/listinfo/pro


Re: [pro] why :key arguments?

2011-07-04 Thread Tamas Papp
On Mon, 04 Jul 2011 14:20:32 +0400, Stas Boukarev wrote:

 Tamas Papp tkp...@gmail.com writes:
 
 On Mon, 04 Jul 2011 11:39:39 +0200, Hans Hübner wrote:

 On Mon, Jul 4, 2011 at 11:31 AM, Tamas Papp tkp...@gmail.com wrote:
 Why do some CL library functions have :key arguments?
 [...]
 but it is a bit cumbersome.  I can make my code simpler by relying on
 calls like

 (quantiles (map 'vector key vector) quantiles)
 
 This not only conses a bit more, it also duplicates traversal
 efforts - The original list must be traversed, and the consed-up list
 of key values as well.  I think it is prudent that the CL library
 functions offer ways to reduce consing for cases where a bit is too
 much (and a bit can become a lot if a program operates on long
 lists).

 I understand this.  My main question is: why not do this with compiler
 macros?  Is there any reason for this, other than historical?
 Because it's not easy to do with compiler macros.

Can you (or someone) please elaborate on that?  I have just started 
reading up on compiler macros, and I don't understand why.

Thanks,

Tamas


___
pro mailing list
pro@common-lisp.net
http://lists.common-lisp.net/cgi-bin/mailman/listinfo/pro


Re: [pro] why :key arguments?

2011-07-04 Thread Stas Boukarev
Tamas Papp tkp...@gmail.com writes:

 On Mon, 04 Jul 2011 14:20:32 +0400, Stas Boukarev wrote:

 Tamas Papp tkp...@gmail.com writes:
 
 On Mon, 04 Jul 2011 11:39:39 +0200, Hans Hübner wrote:

 On Mon, Jul 4, 2011 at 11:31 AM, Tamas Papp tkp...@gmail.com wrote:
 Why do some CL library functions have :key arguments?
 [...]
 but it is a bit cumbersome.  I can make my code simpler by relying on
 calls like

 (quantiles (map 'vector key vector) quantiles)
 
 This not only conses a bit more, it also duplicates traversal
 efforts - The original list must be traversed, and the consed-up list
 of key values as well.  I think it is prudent that the CL library
 functions offer ways to reduce consing for cases where a bit is too
 much (and a bit can become a lot if a program operates on long
 lists).

 I understand this.  My main question is: why not do this with compiler
 macros?  Is there any reason for this, other than historical?
 Because it's not easy to do with compiler macros.

 Can you (or someone) please elaborate on that?  I have just started 
 reading up on compiler macros, and I don't understand why.

Suppose you have a function SUM,

(defun sum (list)
  (loop for i in list
sum i))

Now if you want to optimize (sum (map 'list KEY list)), you would
need to write an additional function.

(defun sum-key (list key)
  (loop for i in list
sum (funcall key i)))

And then you would need to write a compiler macro, which would match
(map 'list KEY sequence) and transform it into (sum-key sequence KEY),
which is not that hard, if that's only what you have. Now what if you
want it to work with (mapcar KEY list) too. It becomes more and more
cumbersome as it gets more general.

And now you need two functions, a clever compiler macro (perhaps using
some pattern matching library), and which may not do what the user
expects.

While you could have all that with just one function:

(defun sum (list key key)
  (loop for i in list
sum (if key
(funcall key i)
i)))
Other disadvantages of compiler-macros:

* They are not guaranteed to be expanded. Some implementations may
  ignore them.
* They can't be used with APPLY or FUNCALL.
* You can't compose them
  e.g. if you wanted to write
  (defun two-sum (list1 list2)
 (+ (sum list1) (sum list2)))
  You would need to write a second compiler macro. While with KEY
  keyword you could just pass it along.

-- 
With best regards, Stas.

___
pro mailing list
pro@common-lisp.net
http://lists.common-lisp.net/cgi-bin/mailman/listinfo/pro


Re: [pro] why :key arguments?

2011-07-04 Thread Nikodemus Siivola
On 4 July 2011 19:42, Pascal J. Bourguignon p...@informatimago.com wrote:

 I don't think so.

I disagree strongly, and I'm pretty sure CLHS agrees with me, since it
goes to the trouble of specifying what happens with FUNCALL.

CLHS, DEFINE-COMPILER-MACRO: The whole argument is bound to the form
argument that is passed to the compiler macro function. The remaining
lambda-list parameters are specified as if this form contained the
function name in the car and the actual arguments in the cdr, but if
the car of the actual form is the symbol funcall, then the
destructuring of the arguments is actually performed using its cddr
instead.

(I'm not really interested in fencing re. compiler-macros here, just
trying to keep the record only moderately crooked. This is a sidetrack
of epic proportions already: the OP asked about :KEY, not
compiler-macros.)

Cheers,

 -- nikodemus

___
pro mailing list
pro@common-lisp.net
http://lists.common-lisp.net/cgi-bin/mailman/listinfo/pro