Mercurial > hg > xemacs-beta
changeset 5227:fbd1485af104
Move #'reduce to fns.c from cl-seq.el.
src/ChangeLog addition:
2010-06-06 Aidan Kehoe <kehoea@parhasard.net>
* fns.c (Freduce):
Move this here from cl-seq.el, avoiding the need to cons. This
has been tested using Paul Dietz' test suite, and everything
applicable passes, with the exception that the
ALLOW-OTHER-KEYS-NIL Common Lisp issue (functions with &key must
*always* accept :allow-other-keys nil) hasn't been implemented.
lisp/ChangeLog addition:
2010-06-06 Aidan Kehoe <kehoea@parhasard.net>
* cl-seq.el (reduce):
Move this to fns.c.
author | Aidan Kehoe <kehoea@parhasard.net> |
---|---|
date | Sun, 06 Jun 2010 13:24:31 +0100 |
parents | 7789ae555c45 |
children | 5efbd1253905 |
files | lisp/ChangeLog lisp/cl-seq.el src/ChangeLog src/fns.c |
diffstat | 4 files changed, 381 insertions(+), 44 deletions(-) [+] |
line wrap: on
line diff
--- a/lisp/ChangeLog Wed Jun 02 16:18:50 2010 +0100 +++ b/lisp/ChangeLog Sun Jun 06 13:24:31 2010 +0100 @@ -1,3 +1,8 @@ +2010-06-06 Aidan Kehoe <kehoea@parhasard.net> + + * cl-seq.el (reduce): + Move this to fns.c. + 2010-06-02 Aidan Kehoe <kehoea@parhasard.net> * cl-macs.el (complement):
--- a/lisp/cl-seq.el Wed Jun 02 16:18:50 2010 +0100 +++ b/lisp/cl-seq.el Sun Jun 06 13:24:31 2010 +0100 @@ -142,36 +142,6 @@ (defvar cl-if) (defvar cl-if-not) (defvar cl-key) - -(defun reduce (cl-func cl-seq &rest cl-keys) - "Combine the elements of sequence using FUNCTION, a binary operation. -For example, `(reduce #'+ SEQUENCE)' returns the sum of all elements in -SEQUENCE, and `(reduce #'union SEQUENCE)' returns the union of all elements -in SEQUENCE. -Keywords supported: :start :end :from-end :initial-value :key -See `remove*' for the meaning of :start, :end, :from-end and :key. -:initial-value specifies an element (typically an identity element, such as 0) -that is conceptually prepended to the sequence (or appended, when :from-end -is given). -If the sequence has one element, that element is returned directly. -If the sequence has no elements, :initial-value is returned if given; -otherwise, FUNCTION is called with no arguments, and its result returned." - (cl-parsing-keywords (:from-end (:start 0) :end :initial-value :key) () - (or (listp cl-seq) (setq cl-seq (append cl-seq nil))) - (setq cl-seq (subseq cl-seq cl-start cl-end)) - (if cl-from-end (setq cl-seq (nreverse cl-seq))) - (let ((cl-accum (cond ((memq :initial-value cl-keys) cl-initial-value) - (cl-seq (cl-check-key (pop cl-seq))) - (t (funcall cl-func))))) - (if cl-from-end - (while cl-seq - (setq cl-accum (funcall cl-func (cl-check-key (pop cl-seq)) - cl-accum))) - (while cl-seq - (setq cl-accum (funcall cl-func cl-accum - (cl-check-key (pop cl-seq)))))) - cl-accum))) - (defun replace (cl-seq1 cl-seq2 &rest cl-keys) "Replace the elements of SEQ1 with the elements of SEQ2. SEQ1 is destructively modified, then returned.
--- a/src/ChangeLog Wed Jun 02 16:18:50 2010 +0100 +++ b/src/ChangeLog Sun Jun 06 13:24:31 2010 +0100 @@ -1,3 +1,12 @@ +2010-06-06 Aidan Kehoe <kehoea@parhasard.net> + + * fns.c (Freduce): + Move this here from cl-seq.el, avoiding the need to cons. This + has been tested using Paul Dietz' test suite, and everything + applicable passes, with the exception that the + ALLOW-OTHER-KEYS-NIL Common Lisp issue (functions with &key must + *always* accept :allow-other-keys nil) hasn't been implemented. + 2010-06-01 Aidan Kehoe <kehoea@parhasard.net> * fns.c (Fsubstring_no_properties):
--- a/src/fns.c Wed Jun 02 16:18:50 2010 +0100 +++ b/src/fns.c Sun Jun 06 13:24:31 2010 +0100 @@ -56,7 +56,7 @@ Lisp_Object Qstring_lessp, Qsort, Qmerge, Qfill; Lisp_Object Qidentity; -Lisp_Object Qvector, Qarray, Qbit_vector, QsortX; +Lisp_Object Qvector, Qarray, Qbit_vector, QsortX, Qreduce, Q_from_end, Q_initial_value; Lisp_Object Qbase64_conversion_error; @@ -2432,22 +2432,17 @@ /* This macro might eventually find a better home than here. */ -#define CHECK_KEY_ARGUMENT(key, c_predicate) \ +#define CHECK_KEY_ARGUMENT(key) \ do { \ if (NILP (key)) \ { \ key = Qidentity; \ } \ - \ - if (EQ (key, Qidentity)) \ - { \ - c_predicate = c_merge_predicate_nokey; \ - } \ - else \ - { \ - key = indirect_function (key, 1); \ - c_predicate = c_merge_predicate_key; \ - } \ + \ + if (!EQ (key, Qidentity)) \ + { \ + key = indirect_function (key, 1); \ + } \ } while (0) DEFUN ("merge", Fmerge, 4, MANY, 0, /* @@ -2473,7 +2468,10 @@ CHECK_SEQUENCE (sequence_one); CHECK_SEQUENCE (sequence_two); - CHECK_KEY_ARGUMENT (key, c_predicate); + CHECK_KEY_ARGUMENT (key); + + c_predicate = EQ (key, Qidentity) ? + c_merge_predicate_nokey : c_merge_predicate_key; if (EQ (type, Qlist) && (LISTP (sequence_one) || LISTP (sequence_two))) { @@ -2721,7 +2719,10 @@ CHECK_SEQUENCE (sequence); - CHECK_KEY_ARGUMENT (key, c_predicate); + CHECK_KEY_ARGUMENT (key); + + c_predicate = EQ (key, Qidentity) ? + c_merge_predicate_nokey : c_merge_predicate_key; if (LISTP (sequence)) { @@ -4844,6 +4845,353 @@ /* Extra random functions */ +DEFUN ("reduce", Freduce, 2, MANY, 0, /* +Combine the elements of sequence using FUNCTION, a binary operation. + +For example, `(reduce #'+ SEQUENCE)' returns the sum of all elements in +SEQUENCE, and `(reduce #'union SEQUENCE)' returns the union of all elements +in SEQUENCE. + +Keywords supported: :start :end :from-end :initial-value :key +See `remove*' for the meaning of :start, :end, :from-end and :key. + +:initial-value specifies an element (typically an identity element, such as +0) that is conceptually prepended to the sequence (or appended, when +:from-end is given). + +If the sequence has one element, that element is returned directly. +If the sequence has no elements, :initial-value is returned if given; +otherwise, FUNCTION is called with no arguments, and its result returned. + +arguments: (FUNCTION SEQUENCE &key (START 0) (END (length SEQUENCE)) FROM-END INITIAL-VALUE (KEY #'identity)) +*/ + (int nargs, Lisp_Object *args)) +{ + Lisp_Object function = args[0], sequence = args[1], accum = Qunbound; + Elemcount starting, ending = EMACS_INT_MAX, ii = 0; + + PARSE_KEYWORDS (Qreduce, nargs, args, 2, 5, + (start, end, from_end, initial_value, key), + (start = Qzero, initial_value = Qunbound), 0); + + CHECK_SEQUENCE (sequence); + CHECK_NATNUM (start); + + CHECK_KEY_ARGUMENT (key); + +#define KEY(key, item) (EQ (key, Qidentity) ? item : call1 (key, item)) + + starting = XINT (start); + if (!NILP (end)) + { + CHECK_NATNUM (end); + ending = XINT (end); + } + + if (VECTORP (sequence)) + { + Lisp_Vector *vv = XVECTOR (sequence); + ending = min (ending, vv->size); + + if (!UNBOUNDP (initial_value)) + { + accum = initial_value; + } + else if (ending - starting && starting < ending) + { + if (NILP (from_end)) + { + accum = KEY (key, vv->contents[starting]); + starting++; + } + else + { + accum = KEY (key, vv->contents[ending - 1]); + ending--; + } + } + + if (NILP (from_end)) + { + for (ii = starting; ii < ending; ++ii) + { + accum = call2 (function, accum, KEY (key, vv->contents[ii])); + } + } + else + { + for (ii = ending - 1; ii >= starting; --ii) + { + accum = call2 (function, KEY (key, vv->contents[ii]), accum); + } + } + } + else if (BIT_VECTORP (sequence)) + { + Lisp_Bit_Vector *bv = XBIT_VECTOR (sequence); + + ending = min (ending, bv->size); + + if (!UNBOUNDP (initial_value)) + { + accum = initial_value; + } + else if (ending - starting && starting < ending) + { + if (NILP (from_end)) + { + accum = KEY (key, make_int (bit_vector_bit (bv, starting))); + starting++; + } + else + { + accum = KEY (key, make_int (bit_vector_bit (bv, ending - 1))); + ending--; + } + } + + if (NILP (from_end)) + { + for (ii = starting; ii < ending; ++ii) + { + accum = call2 (function, accum, + KEY (key, make_int (bit_vector_bit (bv, ii)))); + } + } + else + { + for (ii = ending - 1; ii >= starting; --ii) + { + accum = call2 (function, KEY (key, + make_int (bit_vector_bit (bv, + ii))), + accum); + } + } + + } + else if (STRINGP (sequence)) + { + if (NILP (from_end)) + { + Bytecount byte_len = XSTRING_LENGTH (sequence); + Bytecount cursor_offset = 0; + const Ibyte *startp = XSTRING_DATA (sequence); + const Ibyte *cursor = startp; + + for (ii = 0; ii != starting && cursor_offset < byte_len; ++ii) + { + INC_IBYTEPTR (cursor); + cursor_offset = cursor - startp; + } + + if (!UNBOUNDP (initial_value)) + { + accum = initial_value; + } + else if (ending - starting && starting < ending) + { + accum = KEY (key, make_char (itext_ichar (cursor))); + starting++; + startp = XSTRING_DATA (sequence); + cursor = startp + cursor_offset; + INC_IBYTEPTR (cursor); + cursor_offset = cursor - startp; + } + + while (cursor_offset < byte_len && starting < ending) + { + if (cursor_offset > XSTRING_LENGTH (sequence)) + { + invalid_state ("sequence modified during reduce", sequence); + } + + startp = XSTRING_DATA (sequence); + cursor = startp + cursor_offset; + accum = call2 (function, accum, + KEY (key, make_char (itext_ichar (cursor)))); + INC_IBYTEPTR (cursor); + cursor_offset = cursor - startp; + ++starting; + } + } + else + { + Elemcount len = string_char_length (sequence); + Bytecount cursor_offset; + const Ibyte *cursor; + + ending = min (ending, len); + cursor = string_char_addr (sequence, ending - 1); + cursor_offset = cursor - XSTRING_DATA (sequence); + + if (!UNBOUNDP (initial_value)) + { + accum = initial_value; + } + else if (ending - starting && starting < ending) + { + accum = KEY (key, make_char (itext_ichar (cursor))); + ending--; + if (ending > 0) + { + DEC_IBYTEPTR (cursor); + cursor_offset = cursor - XSTRING_DATA (sequence); + } + } + + for (ii = ending - 1; ii >= starting; --ii) + { + if (cursor_offset > XSTRING_LENGTH (sequence)) + { + invalid_state ("sequence modified during reduce", sequence); + } + + cursor = XSTRING_DATA (sequence) + cursor_offset; + accum = call2 (function, KEY (key, + make_char (itext_ichar (cursor))), + accum); + if (ii > 1) + { + cursor = XSTRING_DATA (sequence) + cursor_offset; + DEC_IBYTEPTR (cursor); + cursor_offset = cursor - XSTRING_DATA (sequence); + } + } + } + } + else if (LISTP (sequence)) + { + if (NILP (from_end)) + { + if (!UNBOUNDP (initial_value)) + { + accum = initial_value; + } + else if (ending - starting && starting < ending) + { + Elemcount counting = 0; + EXTERNAL_LIST_LOOP_3 (elt, sequence, tail) + { + if (counting == starting) + { + accum = KEY (key, elt); + starting++; + break; + } + ++counting; + } + } + + if (ending - starting && starting < ending) + { + Elemcount counting = 0; + + EXTERNAL_LIST_LOOP_3 (elt, sequence, tail) + { + if (counting >= starting) + { + if (counting < ending) + { + accum = call2 (function, accum, KEY (key, elt)); + } + else if (counting == ending) + { + break; + } + } + ++counting; + } + } + } + else + { + Boolint need_accum = 0; + Lisp_Object *subsequence = NULL; + Elemcount counting = 0, len = 0; + struct gcpro gcpro1; + + if (ending - starting && starting < ending && EMACS_INT_MAX == ending) + { + ending = XINT (Flength (sequence)); + } + + /* :from-end with a list; make an alloca copy of the relevant list + data, attempting to go backwards isn't worth the trouble. */ + if (!UNBOUNDP (initial_value)) + { + accum = initial_value; + if (ending - starting && starting < ending) + { + subsequence = alloca_array (Lisp_Object, ending - starting); + } + } + else if (ending - starting && starting < ending) + { + subsequence = alloca_array (Lisp_Object, ending - starting); + need_accum = 1; + } + + if (ending - starting && starting < ending) + { + EXTERNAL_LIST_LOOP_3 (elt, sequence, tail) + { + if (counting >= starting) + { + if (counting < ending) + { + subsequence[ii++] = elt; + } + else if (counting == ending) + { + break; + } + } + ++counting; + } + } + + if (subsequence != NULL) + { + len = ending - starting; + /* If we could be sure that neither FUNCTION nor KEY modify + SEQUENCE, this wouldn't be necessary, since all the + elements of SUBSEQUENCE would definitely always be + reachable via SEQUENCE. */ + GCPRO1 (subsequence[0]); + gcpro1.nvars = len; + } + + if (need_accum) + { + accum = KEY (key, subsequence[len - 1]); + --len; + } + + for (ii = len; ii != 0;) + { + --ii; + accum = call2 (function, KEY (key, subsequence[ii]), accum); + } + + if (subsequence != NULL) + { + UNGCPRO; + } + } + } + + /* At this point, if ACCUM is unbound, SEQUENCE has no elements; we + need to return the result of calling FUNCTION with zero + arguments. */ + if (UNBOUNDP (accum)) + { + accum = call0 (function); + } + + return accum; +} + DEFUN ("replace-list", Freplace_list, 2, 2, 0, /* Destructively replace the list OLD with NEW. This is like (copy-sequence NEW) except that it reuses the @@ -5528,6 +5876,10 @@ DEFSYMBOL (Qlist); DEFSYMBOL (Qbit_vector); defsymbol (&QsortX, "sort*"); + DEFSYMBOL (Qreduce); + + DEFKEYWORD (Q_from_end); + DEFKEYWORD (Q_initial_value); DEFSYMBOL (Qyes_or_no_p); @@ -5624,6 +5976,7 @@ DEFSUBR (Fmapl); DEFSUBR (Fmapcon); + DEFSUBR (Freduce); DEFSUBR (Freplace_list); DEFSUBR (Fload_average); DEFSUBR (Ffeaturep);