summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--ChangeLog14
-rw-r--r--eval.c4
-rw-r--r--lib.c35
-rw-r--r--lib.h2
-rw-r--r--txr.151
5 files changed, 102 insertions, 4 deletions
diff --git a/ChangeLog b/ChangeLog
index 597c1950..3f54b517 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -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
diff --git a/eval.c b/eval.c
index ec10d1e1..f62f87c9 100644
--- a/eval.c
+++ b/eval.c
@@ -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));
diff --git a/lib.c b/lib.c
index 61390692..422396f0 100644
--- a/lib.c
+++ b/lib.c
@@ -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)
diff --git a/lib.h b/lib.h
index 3e792bd7..0ea13787 100644
--- a/lib.h
+++ b/lib.h
@@ -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);
diff --git a/txr.1 b/txr.1
index e325c471..279db6e6 100644
--- a/txr.1
+++ b/txr.1
@@ -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