diff options
author | Kaz Kylheku <kaz@kylheku.com> | 2017-06-28 21:30:47 -0700 |
---|---|---|
committer | Kaz Kylheku <kaz@kylheku.com> | 2017-06-28 21:30:47 -0700 |
commit | 37d91d954562250aadd3d65819868ae65f1cfacf (patch) | |
tree | 69618009003d69e5c88caa33941c2b2ec51c6eb6 | |
parent | 4d47909dbe082a6a8313f888290543cb0765b969 (diff) | |
download | txr-37d91d954562250aadd3d65819868ae65f1cfacf.tar.gz txr-37d91d954562250aadd3d65819868ae65f1cfacf.tar.bz2 txr-37d91d954562250aadd3d65819868ae65f1cfacf.zip |
New Cartesian product mapping functions.
* eval.c (prod_common): New static function.
(maprodv, maprendv): New functions.
(eval_init): Registered maprod and maprend intrinsics.
* eval.h (maprodv, maprendv): Declared.
* txr.1: Documented.
-rw-r--r-- | eval.c | 59 | ||||
-rw-r--r-- | eval.h | 2 | ||||
-rw-r--r-- | txr.1 | 112 |
3 files changed, 173 insertions, 0 deletions
@@ -4685,6 +4685,63 @@ static val mapdov(val fun, struct args *lists) } } +static val prod_common(val fun, struct args *lists, + loc (*collect_fptr)(loc ptail, val obj)) +{ + if (!args_more(lists, 0)) { + return nil; + } else if (!args_two_more(lists, 0)) { + return mappendv(fun, lists); + } else { + cnum argc = args_count(lists), i; + args_decl(args_reset, max(argc, ARGS_MIN)); + args_decl(args_work, max(argc, ARGS_MIN)); + args_copy(args_reset, lists); + args_normalize(args_reset, argc); + args_copy(args_work, args_reset); + list_collect_decl (out, ptail); + + for (i = 0; i < argc; i++) + args_work->arg[i] = nullify(args_work->arg[i]); + + for (;;) { + args_decl(args_fun, max(argc, ARGS_MIN)); + for (i = 0; i < argc; i++) { + val seq_i = args_work->arg[i]; + if (!seq_i) + goto out; + args_fun->arg[i] = car(seq_i); + } + + args_fun->fill = argc; + ptail = collect_fptr(ptail, generic_funcall(fun, args_fun)); + + for (i = argc - 1; ; i--) { + val cdr_i = cdr(args_work->arg[i]); + if (cdr_i) { + args_work->arg[i] = cdr_i; + break; + } + if (i == 0) + goto out; + args_work->arg[i] = args_reset->arg[i]; + } + } + out: + return make_like(out, args_reset->arg[0]); + } +} + +val maprodv(val fun, struct args *lists) +{ + return prod_common(fun, lists, list_collect); +} + +val maprendv(val fun, struct args *lists) +{ + return prod_common(fun, lists, list_collect_append); +} + static val symbol_value(val sym) { uses_or2; @@ -5645,6 +5702,8 @@ void eval_init(void) reg_fun(intern(lit("mappend"), user_package), func_n1v(mappendv)); reg_fun(intern(lit("mappend*"), user_package), func_n1v(lazy_mappendv)); reg_fun(intern(lit("mapdo"), user_package), func_n1v(mapdov)); + reg_fun(intern(lit("maprod"), user_package), func_n1v(maprodv)); + reg_fun(intern(lit("maprend"), user_package), func_n1v(maprendv)); reg_fun(intern(lit("window-map"), user_package), func_n4(window_map)); reg_fun(intern(lit("window-mappend"), user_package), func_n4(window_mappend)); { @@ -80,6 +80,8 @@ val bindable(val obj); val mapcarv(val fun, struct args *lists); val mapcarl(val fun, val list_of_lists); val lazy_mapcar(val fun, val list); +val maprodv(val fun, struct args *lists); +val maprendv(val fun, struct args *lists); val generate(val while_pred, val gen_fun); val retf(val ret); val prinl(val obj, val stream); @@ -27051,6 +27051,118 @@ and so doesn't return. -> (2 4) .cble +.coNP Functions @ maprod and @ maprend +.synb +.mets (maprod < function << sequence *) +.mets (maprend < function << sequence *) +.syne +.desc +The +.code maprod +and +.code maprend +functions resemble +.code mapcar +and +.codn mappend , +respectively. When given no +.meta sequence +arguments or exactly one +.meta sequence +argument, they behave exactly like those two functions. + +When two or more +.meta sequence +arguments are present, +.code maprod +differs from +.code mapcar +in the following way. Whereas +.code mapcar +iterates over the +.meta sequence +values in parallel, taking successive tuples of element +values and passing them to +.metn function , +the +.code maprod +function iterates over all +.I combinations +of elements from the sequences: the Cartesian product. The +.code prod +suffix stands for "product". + +If one or more +.meta sequence +arguments specify an empty sequence, then the Cartesian product is empty. +In this situation, +.meta function +is not called. The result of the function is then +.code nil +converted to the same kind of sequence as the leftmost +.metn sequence . + +The +.code maprod +function collects the values into a list just as +.code mapcar +does. Just like +.codn mapcar , +it converts the resulting list into the same kind of sequence +as the leftmost +.meta sequence +argument, if possible. For instance, if the resulting list is +a list or vector of characters, and the leftmost +.meta sequence +is a character string, then the list or vector of characters +is converted to a character string and returned. + +The +.code maprend +function ("map product through function and append") iterates the +.meta sequence +element combinations exactly like +.codn maprod , +passing them as arguments to +.metn function . +The values returned by +.meta function +are then treated exactly as by the +.code mappend +function. The return values are expected to be sequences which +are appended together as if by +.codn append , +and the final result is converted to the same kind of sequence as the leftmost +.meta sequence +if possible. + +The combination iteration gives priority to the rightmost +.metn sequence , +which means that the rightmost element of each generated tuple varies +fastest: the tuples are traversed in "rightmost major" order. +This is made clear in the examples. + +.TP* Examples + +.cblk + [maprod list '(0 1 2) '(a b) '(i ii iii)] + -> + ((0 a i) (0 a ii) (0 a iii) (0 b i) (0 b ii) (0 b iii) + (1 a i) (1 a ii) (1 a iii) (1 b i) (1 b ii) (1 b iii) + (2 a i) (2 a ii) (2 a iii) (2 b i) (2 b ii) (2 b iii)) + + ;; Vectors #(#\ea #\ex) #(#\ea #\ey) ... are appended + ;; together resulting in #(#\ea #\ex #\ea #\ey ...) + ;; which is converted to a string: + + [maprend vec "ab" "xy"] -> "axaybxby" + + ;; One of the sequences is empty, so the product is an + ;; empty sequence of the same kind as the leftmost + ;; sequence argument, thus an empty string: + [maprend vec "ab" ""] -> "" +.cble + .coNP Function @ mapdo .synb .mets (mapdo < function << sequence *) |