Mercurial > hg > xemacs-beta
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); |