comparison src/fns.c @ 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 35c2b7e9c03f
children d579d76f3dcc
comparison
equal deleted inserted replaced
5226:7789ae555c45 5227:fbd1485af104
54 /* NOTE: This symbol is also used in lread.c */ 54 /* NOTE: This symbol is also used in lread.c */
55 #define FEATUREP_SYNTAX 55 #define FEATUREP_SYNTAX
56 56
57 Lisp_Object Qstring_lessp, Qsort, Qmerge, Qfill; 57 Lisp_Object Qstring_lessp, Qsort, Qmerge, Qfill;
58 Lisp_Object Qidentity; 58 Lisp_Object Qidentity;
59 Lisp_Object Qvector, Qarray, Qbit_vector, QsortX; 59 Lisp_Object Qvector, Qarray, Qbit_vector, QsortX, Qreduce, Q_from_end, Q_initial_value;
60 60
61 Lisp_Object Qbase64_conversion_error; 61 Lisp_Object Qbase64_conversion_error;
62 62
63 Lisp_Object Vpath_separator; 63 Lisp_Object Vpath_separator;
64 64
2430 } \ 2430 } \
2431 } while (0) 2431 } while (0)
2432 2432
2433 /* This macro might eventually find a better home than here. */ 2433 /* This macro might eventually find a better home than here. */
2434 2434
2435 #define CHECK_KEY_ARGUMENT(key, c_predicate) \ 2435 #define CHECK_KEY_ARGUMENT(key) \
2436 do { \ 2436 do { \
2437 if (NILP (key)) \ 2437 if (NILP (key)) \
2438 { \ 2438 { \
2439 key = Qidentity; \ 2439 key = Qidentity; \
2440 } \ 2440 } \
2441 \ 2441 \
2442 if (EQ (key, Qidentity)) \ 2442 if (!EQ (key, Qidentity)) \
2443 { \ 2443 { \
2444 c_predicate = c_merge_predicate_nokey; \ 2444 key = indirect_function (key, 1); \
2445 } \ 2445 } \
2446 else \
2447 { \
2448 key = indirect_function (key, 1); \
2449 c_predicate = c_merge_predicate_key; \
2450 } \
2451 } while (0) 2446 } while (0)
2452 2447
2453 DEFUN ("merge", Fmerge, 4, MANY, 0, /* 2448 DEFUN ("merge", Fmerge, 4, MANY, 0, /*
2454 Destructively merge SEQUENCE-ONE and SEQUENCE-TWO, producing a new sequence. 2449 Destructively merge SEQUENCE-ONE and SEQUENCE-TWO, producing a new sequence.
2455 2450
2471 PARSE_KEYWORDS (Qmerge, nargs, args, 4, 1, (key), NULL, 0); 2466 PARSE_KEYWORDS (Qmerge, nargs, args, 4, 1, (key), NULL, 0);
2472 2467
2473 CHECK_SEQUENCE (sequence_one); 2468 CHECK_SEQUENCE (sequence_one);
2474 CHECK_SEQUENCE (sequence_two); 2469 CHECK_SEQUENCE (sequence_two);
2475 2470
2476 CHECK_KEY_ARGUMENT (key, c_predicate); 2471 CHECK_KEY_ARGUMENT (key);
2472
2473 c_predicate = EQ (key, Qidentity) ?
2474 c_merge_predicate_nokey : c_merge_predicate_key;
2477 2475
2478 if (EQ (type, Qlist) && (LISTP (sequence_one) || LISTP (sequence_two))) 2476 if (EQ (type, Qlist) && (LISTP (sequence_one) || LISTP (sequence_two)))
2479 { 2477 {
2480 if (NILP (sequence_two)) 2478 if (NILP (sequence_two))
2481 { 2479 {
2719 2717
2720 PARSE_KEYWORDS (QsortX, nargs, args, 2, 1, (key), NULL, 0); 2718 PARSE_KEYWORDS (QsortX, nargs, args, 2, 1, (key), NULL, 0);
2721 2719
2722 CHECK_SEQUENCE (sequence); 2720 CHECK_SEQUENCE (sequence);
2723 2721
2724 CHECK_KEY_ARGUMENT (key, c_predicate); 2722 CHECK_KEY_ARGUMENT (key);
2723
2724 c_predicate = EQ (key, Qidentity) ?
2725 c_merge_predicate_nokey : c_merge_predicate_key;
2725 2726
2726 if (LISTP (sequence)) 2727 if (LISTP (sequence))
2727 { 2728 {
2728 sequence = list_sort (sequence, c_predicate, predicate, key); 2729 sequence = list_sort (sequence, c_predicate, predicate, key);
2729 } 2730 }
4842 return maplist (args[0], nargs - 1, args + 1, 0, 1); 4843 return maplist (args[0], nargs - 1, args + 1, 0, 1);
4843 } 4844 }
4844 4845
4845 /* Extra random functions */ 4846 /* Extra random functions */
4846 4847
4848 DEFUN ("reduce", Freduce, 2, MANY, 0, /*
4849 Combine the elements of sequence using FUNCTION, a binary operation.
4850
4851 For example, `(reduce #'+ SEQUENCE)' returns the sum of all elements in
4852 SEQUENCE, and `(reduce #'union SEQUENCE)' returns the union of all elements
4853 in SEQUENCE.
4854
4855 Keywords supported: :start :end :from-end :initial-value :key
4856 See `remove*' for the meaning of :start, :end, :from-end and :key.
4857
4858 :initial-value specifies an element (typically an identity element, such as
4859 0) that is conceptually prepended to the sequence (or appended, when
4860 :from-end is given).
4861
4862 If the sequence has one element, that element is returned directly.
4863 If the sequence has no elements, :initial-value is returned if given;
4864 otherwise, FUNCTION is called with no arguments, and its result returned.
4865
4866 arguments: (FUNCTION SEQUENCE &key (START 0) (END (length SEQUENCE)) FROM-END INITIAL-VALUE (KEY #'identity))
4867 */
4868 (int nargs, Lisp_Object *args))
4869 {
4870 Lisp_Object function = args[0], sequence = args[1], accum = Qunbound;
4871 Elemcount starting, ending = EMACS_INT_MAX, ii = 0;
4872
4873 PARSE_KEYWORDS (Qreduce, nargs, args, 2, 5,
4874 (start, end, from_end, initial_value, key),
4875 (start = Qzero, initial_value = Qunbound), 0);
4876
4877 CHECK_SEQUENCE (sequence);
4878 CHECK_NATNUM (start);
4879
4880 CHECK_KEY_ARGUMENT (key);
4881
4882 #define KEY(key, item) (EQ (key, Qidentity) ? item : call1 (key, item))
4883
4884 starting = XINT (start);
4885 if (!NILP (end))
4886 {
4887 CHECK_NATNUM (end);
4888 ending = XINT (end);
4889 }
4890
4891 if (VECTORP (sequence))
4892 {
4893 Lisp_Vector *vv = XVECTOR (sequence);
4894 ending = min (ending, vv->size);
4895
4896 if (!UNBOUNDP (initial_value))
4897 {
4898 accum = initial_value;
4899 }
4900 else if (ending - starting && starting < ending)
4901 {
4902 if (NILP (from_end))
4903 {
4904 accum = KEY (key, vv->contents[starting]);
4905 starting++;
4906 }
4907 else
4908 {
4909 accum = KEY (key, vv->contents[ending - 1]);
4910 ending--;
4911 }
4912 }
4913
4914 if (NILP (from_end))
4915 {
4916 for (ii = starting; ii < ending; ++ii)
4917 {
4918 accum = call2 (function, accum, KEY (key, vv->contents[ii]));
4919 }
4920 }
4921 else
4922 {
4923 for (ii = ending - 1; ii >= starting; --ii)
4924 {
4925 accum = call2 (function, KEY (key, vv->contents[ii]), accum);
4926 }
4927 }
4928 }
4929 else if (BIT_VECTORP (sequence))
4930 {
4931 Lisp_Bit_Vector *bv = XBIT_VECTOR (sequence);
4932
4933 ending = min (ending, bv->size);
4934
4935 if (!UNBOUNDP (initial_value))
4936 {
4937 accum = initial_value;
4938 }
4939 else if (ending - starting && starting < ending)
4940 {
4941 if (NILP (from_end))
4942 {
4943 accum = KEY (key, make_int (bit_vector_bit (bv, starting)));
4944 starting++;
4945 }
4946 else
4947 {
4948 accum = KEY (key, make_int (bit_vector_bit (bv, ending - 1)));
4949 ending--;
4950 }
4951 }
4952
4953 if (NILP (from_end))
4954 {
4955 for (ii = starting; ii < ending; ++ii)
4956 {
4957 accum = call2 (function, accum,
4958 KEY (key, make_int (bit_vector_bit (bv, ii))));
4959 }
4960 }
4961 else
4962 {
4963 for (ii = ending - 1; ii >= starting; --ii)
4964 {
4965 accum = call2 (function, KEY (key,
4966 make_int (bit_vector_bit (bv,
4967 ii))),
4968 accum);
4969 }
4970 }
4971
4972 }
4973 else if (STRINGP (sequence))
4974 {
4975 if (NILP (from_end))
4976 {
4977 Bytecount byte_len = XSTRING_LENGTH (sequence);
4978 Bytecount cursor_offset = 0;
4979 const Ibyte *startp = XSTRING_DATA (sequence);
4980 const Ibyte *cursor = startp;
4981
4982 for (ii = 0; ii != starting && cursor_offset < byte_len; ++ii)
4983 {
4984 INC_IBYTEPTR (cursor);
4985 cursor_offset = cursor - startp;
4986 }
4987
4988 if (!UNBOUNDP (initial_value))
4989 {
4990 accum = initial_value;
4991 }
4992 else if (ending - starting && starting < ending)
4993 {
4994 accum = KEY (key, make_char (itext_ichar (cursor)));
4995 starting++;
4996 startp = XSTRING_DATA (sequence);
4997 cursor = startp + cursor_offset;
4998 INC_IBYTEPTR (cursor);
4999 cursor_offset = cursor - startp;
5000 }
5001
5002 while (cursor_offset < byte_len && starting < ending)
5003 {
5004 if (cursor_offset > XSTRING_LENGTH (sequence))
5005 {
5006 invalid_state ("sequence modified during reduce", sequence);
5007 }
5008
5009 startp = XSTRING_DATA (sequence);
5010 cursor = startp + cursor_offset;
5011 accum = call2 (function, accum,
5012 KEY (key, make_char (itext_ichar (cursor))));
5013 INC_IBYTEPTR (cursor);
5014 cursor_offset = cursor - startp;
5015 ++starting;
5016 }
5017 }
5018 else
5019 {
5020 Elemcount len = string_char_length (sequence);
5021 Bytecount cursor_offset;
5022 const Ibyte *cursor;
5023
5024 ending = min (ending, len);
5025 cursor = string_char_addr (sequence, ending - 1);
5026 cursor_offset = cursor - XSTRING_DATA (sequence);
5027
5028 if (!UNBOUNDP (initial_value))
5029 {
5030 accum = initial_value;
5031 }
5032 else if (ending - starting && starting < ending)
5033 {
5034 accum = KEY (key, make_char (itext_ichar (cursor)));
5035 ending--;
5036 if (ending > 0)
5037 {
5038 DEC_IBYTEPTR (cursor);
5039 cursor_offset = cursor - XSTRING_DATA (sequence);
5040 }
5041 }
5042
5043 for (ii = ending - 1; ii >= starting; --ii)
5044 {
5045 if (cursor_offset > XSTRING_LENGTH (sequence))
5046 {
5047 invalid_state ("sequence modified during reduce", sequence);
5048 }
5049
5050 cursor = XSTRING_DATA (sequence) + cursor_offset;
5051 accum = call2 (function, KEY (key,
5052 make_char (itext_ichar (cursor))),
5053 accum);
5054 if (ii > 1)
5055 {
5056 cursor = XSTRING_DATA (sequence) + cursor_offset;
5057 DEC_IBYTEPTR (cursor);
5058 cursor_offset = cursor - XSTRING_DATA (sequence);
5059 }
5060 }
5061 }
5062 }
5063 else if (LISTP (sequence))
5064 {
5065 if (NILP (from_end))
5066 {
5067 if (!UNBOUNDP (initial_value))
5068 {
5069 accum = initial_value;
5070 }
5071 else if (ending - starting && starting < ending)
5072 {
5073 Elemcount counting = 0;
5074 EXTERNAL_LIST_LOOP_3 (elt, sequence, tail)
5075 {
5076 if (counting == starting)
5077 {
5078 accum = KEY (key, elt);
5079 starting++;
5080 break;
5081 }
5082 ++counting;
5083 }
5084 }
5085
5086 if (ending - starting && starting < ending)
5087 {
5088 Elemcount counting = 0;
5089
5090 EXTERNAL_LIST_LOOP_3 (elt, sequence, tail)
5091 {
5092 if (counting >= starting)
5093 {
5094 if (counting < ending)
5095 {
5096 accum = call2 (function, accum, KEY (key, elt));
5097 }
5098 else if (counting == ending)
5099 {
5100 break;
5101 }
5102 }
5103 ++counting;
5104 }
5105 }
5106 }
5107 else
5108 {
5109 Boolint need_accum = 0;
5110 Lisp_Object *subsequence = NULL;
5111 Elemcount counting = 0, len = 0;
5112 struct gcpro gcpro1;
5113
5114 if (ending - starting && starting < ending && EMACS_INT_MAX == ending)
5115 {
5116 ending = XINT (Flength (sequence));
5117 }
5118
5119 /* :from-end with a list; make an alloca copy of the relevant list
5120 data, attempting to go backwards isn't worth the trouble. */
5121 if (!UNBOUNDP (initial_value))
5122 {
5123 accum = initial_value;
5124 if (ending - starting && starting < ending)
5125 {
5126 subsequence = alloca_array (Lisp_Object, ending - starting);
5127 }
5128 }
5129 else if (ending - starting && starting < ending)
5130 {
5131 subsequence = alloca_array (Lisp_Object, ending - starting);
5132 need_accum = 1;
5133 }
5134
5135 if (ending - starting && starting < ending)
5136 {
5137 EXTERNAL_LIST_LOOP_3 (elt, sequence, tail)
5138 {
5139 if (counting >= starting)
5140 {
5141 if (counting < ending)
5142 {
5143 subsequence[ii++] = elt;
5144 }
5145 else if (counting == ending)
5146 {
5147 break;
5148 }
5149 }
5150 ++counting;
5151 }
5152 }
5153
5154 if (subsequence != NULL)
5155 {
5156 len = ending - starting;
5157 /* If we could be sure that neither FUNCTION nor KEY modify
5158 SEQUENCE, this wouldn't be necessary, since all the
5159 elements of SUBSEQUENCE would definitely always be
5160 reachable via SEQUENCE. */
5161 GCPRO1 (subsequence[0]);
5162 gcpro1.nvars = len;
5163 }
5164
5165 if (need_accum)
5166 {
5167 accum = KEY (key, subsequence[len - 1]);
5168 --len;
5169 }
5170
5171 for (ii = len; ii != 0;)
5172 {
5173 --ii;
5174 accum = call2 (function, KEY (key, subsequence[ii]), accum);
5175 }
5176
5177 if (subsequence != NULL)
5178 {
5179 UNGCPRO;
5180 }
5181 }
5182 }
5183
5184 /* At this point, if ACCUM is unbound, SEQUENCE has no elements; we
5185 need to return the result of calling FUNCTION with zero
5186 arguments. */
5187 if (UNBOUNDP (accum))
5188 {
5189 accum = call0 (function);
5190 }
5191
5192 return accum;
5193 }
5194
4847 DEFUN ("replace-list", Freplace_list, 2, 2, 0, /* 5195 DEFUN ("replace-list", Freplace_list, 2, 2, 0, /*
4848 Destructively replace the list OLD with NEW. 5196 Destructively replace the list OLD with NEW.
4849 This is like (copy-sequence NEW) except that it reuses the 5197 This is like (copy-sequence NEW) except that it reuses the
4850 conses in OLD as much as possible. If OLD and NEW are the same 5198 conses in OLD as much as possible. If OLD and NEW are the same
4851 length, no consing will take place. 5199 length, no consing will take place.
5526 DEFSYMBOL (Qarray); 5874 DEFSYMBOL (Qarray);
5527 DEFSYMBOL (Qstring); 5875 DEFSYMBOL (Qstring);
5528 DEFSYMBOL (Qlist); 5876 DEFSYMBOL (Qlist);
5529 DEFSYMBOL (Qbit_vector); 5877 DEFSYMBOL (Qbit_vector);
5530 defsymbol (&QsortX, "sort*"); 5878 defsymbol (&QsortX, "sort*");
5879 DEFSYMBOL (Qreduce);
5880
5881 DEFKEYWORD (Q_from_end);
5882 DEFKEYWORD (Q_initial_value);
5531 5883
5532 DEFSYMBOL (Qyes_or_no_p); 5884 DEFSYMBOL (Qyes_or_no_p);
5533 5885
5534 DEFERROR_STANDARD (Qbase64_conversion_error, Qconversion_error); 5886 DEFERROR_STANDARD (Qbase64_conversion_error, Qconversion_error);
5535 5887
5622 Ffset (intern ("mapcar"), Fsymbol_function (intern ("mapcar*"))); 5974 Ffset (intern ("mapcar"), Fsymbol_function (intern ("mapcar*")));
5623 DEFSUBR (Fmaplist); 5975 DEFSUBR (Fmaplist);
5624 DEFSUBR (Fmapl); 5976 DEFSUBR (Fmapl);
5625 DEFSUBR (Fmapcon); 5977 DEFSUBR (Fmapcon);
5626 5978
5979 DEFSUBR (Freduce);
5627 DEFSUBR (Freplace_list); 5980 DEFSUBR (Freplace_list);
5628 DEFSUBR (Fload_average); 5981 DEFSUBR (Fload_average);
5629 DEFSUBR (Ffeaturep); 5982 DEFSUBR (Ffeaturep);
5630 DEFSUBR (Frequire); 5983 DEFSUBR (Frequire);
5631 DEFSUBR (Fprovide); 5984 DEFSUBR (Fprovide);