diff options
-rw-r--r-- | ChangeLog | 14 | ||||
-rw-r--r-- | eval.c | 4 | ||||
-rw-r--r-- | lib.c | 35 | ||||
-rw-r--r-- | lib.h | 2 | ||||
-rw-r--r-- | txr.1 | 51 |
5 files changed, 102 insertions, 4 deletions
@@ -1,3 +1,17 @@ +2014-06-26 Kaz Kylheku <kaz@kylheku.com> + + * eval.c (mapcarv): Use mapcar_listout, so list_of_lists can be + a non-list sequence. + (eval_init): Register transpose and zip as intrinsics. + + * lib.c (curry_12_1_v): New static function. + (transpose, mapcar_listout): New functions. + (mapcar): Redefined in terms of mapcar_listout. + + * lib.h (transpose, mapcar_listout): Declared. + + * txr.1: Documented transpose and zip. + 2014-06-20 Kaz Kylheku <kaz@kylheku.com> Bugfix: macros not being expanded in expansions embedded in @@ -2686,7 +2686,7 @@ val mapcarv(val fun, val list_of_lists) if (!cdr(list_of_lists)) { return mapcar(fun, nullify(car(list_of_lists))); } else { - val lofl = mapcar(func_n1(nullify), list_of_lists); + val lofl = mapcar_listout(func_n1(nullify), list_of_lists); val list_orig = car(list_of_lists); list_collect_decl (out, otail); @@ -3315,6 +3315,8 @@ void eval_init(void) reg_fun(call_s, func_n1v(call)); reg_fun(intern(lit("reduce-left"), user_package), func_n4o(reduce_left, 2)); reg_fun(intern(lit("reduce-right"), user_package), func_n4o(reduce_right, 2)); + reg_fun(intern(lit("transpose"), user_package), func_n1(transpose)); + reg_fun(intern(lit("zip"), user_package), func_n0v(transpose)); reg_fun(intern(lit("second"), user_package), func_n1(second)); reg_fun(intern(lit("third"), user_package), func_n1(third)); @@ -3894,6 +3894,11 @@ val curry_12_1(val fun2, val arg2) return func_f1(cons(fun2, arg2), do_curry_12_1); } +static val curry_12_1_v(val fun2, val arg2) +{ + return func_f0v(cons(fun2, arg2), do_curry_12_1); +} + static val do_curry_123_3(val fcons, val arg3) { return funcall3(car(fcons), car(cdr(fcons)), cdr(cdr(fcons)), arg3); @@ -3944,6 +3949,26 @@ val curry_1234_34(val fun4, val arg1, val arg2) return func_f2(cons(fun4, cons(arg1, arg2)), do_curry_1234_34); } +val transpose(val list) +{ + val func = list_f; + + switch (type(car(list))) { + case STR: + case LSTR: + case LIT: + func = curry_12_1_v(func_n2(cat_str), nil); + break; + case VEC: + func = func_n0v(vector_list); + break; + default: + break; + } + + return make_like(mapcarv(func, list), list); +} + static val do_chain(val fun1_list, val args) { val arg = nil; @@ -4821,17 +4846,21 @@ val copy_alist(val list) return mapcar(func_n1(copy_cons), list); } -val mapcar(val fun, val list) +val mapcar_listout(val fun, val list) { list_collect_decl (out, iter); - val list_orig = list; list = nullify(list); for (; list; list = cdr(list)) iter = list_collect(iter, funcall1(fun, car(list))); - return make_like(out, list_orig); + return out; +} + +val mapcar(val fun, val list) +{ + return make_like(mapcar_listout(fun, list), list); } val mapcon(val fun, val list) @@ -673,6 +673,7 @@ val funcall3(val fun, val arg1, val arg2, val arg3); val funcall4(val fun, val arg1, val arg2, val arg3, val arg4); val reduce_left(val fun, val list, val init, val key); val reduce_right(val fun, val list, val init, val key); +val transpose(val lists); /* The notation curry_12_2 means take some function f(arg1, arg2) and fix a value for argument 1 to create a g(arg2). Other variations follow by analogy. */ @@ -733,6 +734,7 @@ val alist_nremove(val list, val keys); val alist_nremove1(val list, val key); val copy_cons(val cons); val copy_alist(val list); +val mapcar_listout(val fun, val list); val mapcar(val fun, val list); val mapcon(val fun, val list); val mappend(val fun, val list); @@ -8056,6 +8056,57 @@ Examples: (mappend (lambda (item) (if (evenp x) (list x))) '(1 2 3 4 5)) -> (2 4) +.SS Functions tranpose and zip + +.TP +Syntax: + + (transpose <sequence>) + (zip <sequence>*) + +.TP +Description: + +The transpose function performs a transposition on <sequence>. This means that the +elements of <sequence> must be sequences. These sequences are understood to be +columns; transpose exchanges rows and columns, returning a sequence of the rows +which make up the columns. The returned sequence is of the same kind as +<sequence>, and the rows are also the same kind of sequence as the first column +of the original sequence. The number of rows returned is limited by the +shortest column among the sequences. + +All of the input sequences (the elements of <sequence>) must have elements +which are compatible with the first sequence. This means that if the first +element of <sequence> is a string, then the remaining sequences must be +strings, or else sequences of characters, or of strings. + +The zip function takes variable arguments, and is equivalent to calling +transpose on a list of the arguments. The following equivalences hold: + + (zip . x) <--> (transpose x) + + [apply zip x] <--> (transpose x) + +.TP +Examples: + + ;; transpose list of lists + (transpose '((a b c) (c d e))) -> ((a c) (b d) (c e)) + + ;; transpose vector of strings: + ;; - string columns become string rows + ;; - vector input becomes vector output + (transpose #("abc" "def" "ghij")) -> #("adg" "beh" "cfi") + + ;; error: transpose wants to make a list of strings + ;; but 1 is not a character + (transpose #("abc" "def" '(1 2 3))) ;; error! + + ;; String elements are catenated: + (transpose #("abc" "def" ("UV" "XY" "WZ"))) -> #("adUV" "beXY" "cfWZ") + + (zip '(a b c) '(c d e)) -> ((a c) (b d) (c e)) + .SS Functions conses and conses* .TP |