summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--eval.c1
-rw-r--r--lib.c12
-rw-r--r--lib.h1
-rw-r--r--stdlib/place.tl72
-rw-r--r--tests/012/seq.tl85
-rw-r--r--txr.1137
6 files changed, 308 insertions, 0 deletions
diff --git a/eval.c b/eval.c
index 42a7d67f..a1409fb8 100644
--- a/eval.c
+++ b/eval.c
@@ -7749,6 +7749,7 @@ void eval_init(void)
reg_fun(intern(lit("replace"), user_package), func_n4o(replace, 2));
reg_fun(intern(lit("dwim-set"), system_package), func_n2v(dwim_set));
reg_fun(intern(lit("dwim-del"), system_package), func_n3(dwim_del));
+ reg_fun(intern(lit("mref"), user_package), func_n1v(mref));
reg_fun(intern(lit("update"), user_package), func_n2(update));
reg_fun(intern(lit("search"), user_package), func_n4o(search, 2));
reg_fun(intern(lit("rsearch"), user_package), func_n4o(rsearch, 2));
diff --git a/lib.c b/lib.c
index 515d5439..4ce12556 100644
--- a/lib.c
+++ b/lib.c
@@ -13476,6 +13476,18 @@ val dwim_del(val place_p, val seq, val ind_range)
}
}
+val mref(val obj, varg args)
+{
+ cnum index = 0;
+
+ while (args_more(args, index)) {
+ val idx = args_get(args, &index);
+ obj = funcall1(obj, idx);
+ }
+
+ return obj;
+}
+
val butlast(val seq, val idx)
{
if (listp(seq)) {
diff --git a/lib.h b/lib.h
index b86e85bd..beef16ee 100644
--- a/lib.h
+++ b/lib.h
@@ -1394,6 +1394,7 @@ val ref(val seq, val ind);
val refset(val seq, val ind, val newval);
val dwim_set(val place_p, val seq, varg);
val dwim_del(val place_p, val seq, val ind_range);
+val mref(val obj, varg args);
val butlast(val seq, val idx);
val replace(val seq, val items, val from, val to);
val update(val seq, val fun);
diff --git a/stdlib/place.tl b/stdlib/place.tl
index 3b3e011d..62316fd6 100644
--- a/stdlib/place.tl
+++ b/stdlib/place.tl
@@ -766,6 +766,67 @@
,',oldval-sym)))))
,body))))))
+(defplace (mref1 seq index) body
+ (getter setter
+ (with-gensyms (obj-sym ind-sym val-sym)
+ (if (place-form-p seq sys:*pl-env*)
+ (with-update-expander (seq-getter seq-setter) seq sys:*pl-env*
+ ^(alet ((,obj-sym (,seq-getter))
+ (,ind-sym ,index))
+ (macrolet ((,getter () ^(mref ,',obj-sym ,',ind-sym))
+ (,setter (val)
+ ^(alet ((,',val-sym ,val))
+ (,',seq-setter (sys:dwim-set t
+ ,',obj-sym
+ ,',ind-sym
+ ,',val-sym))
+ ,',val-sym)))
+ ,body)))
+ ^(rlet ((,obj-sym ,seq)
+ (,ind-sym ,index))
+ (macrolet ((,getter () '(mref ,obj-sym ,ind-sym))
+ (,setter (val)
+ ^(alet ((,',val-sym ,val))
+ (sys:dwim-set nil
+ ,',obj-sym
+ ,',ind-sym
+ ,',val-sym)
+ ,',val-sym)))
+ ,body)))))
+ (ssetter
+ (with-gensyms (val-sym)
+ (if (place-form-p seq sys:*pl-env*)
+ (with-update-expander (seq-getter seq-setter) seq sys:*pl-env*
+ ^(macrolet ((,ssetter (val)
+ ^(alet ((,',val-sym ,val))
+ (,',seq-setter
+ (sys:dwim-set t
+ (,',seq-getter)
+ ,',index
+ ,',val-sym))
+ ,',val-sym)))
+ ,body))
+ ^(macrolet ((,ssetter (val)
+ ^(alet ((,',val-sym ,val))
+ (sys:dwim-set nil
+ ,',seq
+ ,',index
+ ,',val-sym)
+ ,',val-sym)))
+ ,body))))
+ (deleter
+ (with-gensyms (obj-sym ind-sym)
+ (with-update-expander (seq-getter seq-setter) seq sys:*pl-env*
+ ^(alet ((,obj-sym (,seq-getter))
+ (,ind-sym ,index))
+ (macrolet ((,deleter ()
+ ^(prog1 (mref ,',obj-sym ,',ind-sym)
+ (,',seq-setter
+ (sys:dwim-del ,',(place-form-p seq sys:*pl-env*)
+ ,',obj-sym
+ ,',index)))))
+ ,body))))))
+
(defplace (force promise) body
(getter setter
(with-gensyms (promise-sym)
@@ -1011,3 +1072,14 @@
(define-place-macro nth (index obj)
^(car (nthcdr ,index ,obj)))
+
+(define-place-macro mref (obj . indices)
+ (tree-case indices
+ (() obj)
+ ((x) ^(mref1 ,obj ,x))
+ ((x y) ^(mref1 (ref ,obj ,x) ,y))
+ (t (let* ((l2 (nthlast 2 indices))
+ (bl (ldiff indices l2))
+ (x (car l2))
+ (y (cadr l2)))
+ ^(mref1 (ref (mref ,obj ,*bl) ,x) ,y)))))
diff --git a/tests/012/seq.tl b/tests/012/seq.tl
index 28186dbb..407af84a 100644
--- a/tests/012/seq.tl
+++ b/tests/012/seq.tl
@@ -671,3 +671,88 @@
(test l (1 3 4))
(del (second l))
(test l (1 4)))
+
+(let ((nl (list (list (list 1 2)
+ (list 3 4)
+ (list 5 6))
+ (list (list 7 8)
+ (list 9 10)
+ (list 11 12)))))
+ (mtest
+ (mref nl 0 0 0) 1
+ (mref nl 0 0 1) 2
+ (mref nl 0 1 0) 3
+ (mref nl 0 1 1) 4
+ (mref nl 0 2 0) 5
+ (mref nl 0 2 1) 6
+ (mref nl 1 0 0) 7
+ (mref nl 1 0 1) 8
+ (mref nl 1 1 0) 9
+ (mref nl 1 1 1) 10
+ (mref nl 1 2 0) 11
+ (mref nl 0 2 1) 6)
+
+ (mtest
+ (set (mref nl 0 0 0) 101) 101
+ (mref nl 0 0 0) 101
+
+ (del (mref nl 0 0 0..:)) (101 2)
+ nl ((nil (3 4) (5 6)) ((7 8) (9 10) (11 12)))
+
+ (set (mref nl 1 0..2) '(4)) (4)
+ nl ((nil (3 4) (5 6)) (4 (11 12)))
+
+ (del (mref nl 1)) (4 (11 12))
+ nl ((nil (3 4) (5 6)))
+
+ (set (mref nl 1..:) '(a b c)) (a b c)
+ nl ((nil (3 4) (5 6)) a b c)
+
+ (set (mref nl 1..3) '(e f)) (e f)
+ nl ((nil (3 4) (5 6)) e f c)))
+
+(flet ((get-vec () (vec 1 2 3))
+ (get-list () (list 1 2 3)))
+ (mtest
+ (inc (mref (get-vec) 0)) 2
+ (set (mref (get-vec) 0) 10) 10
+ (inc (mref (get-list) 0)) 2
+ (set (mref (get-list) 0) 10) 10
+ (push 3 (mref (get-vec) 1..2)) (3 . #(2))
+ (set (mref (get-vec) 1..2) '(30)) (30)
+ (push 3 (mref (get-list) 1..2)) :error
+ (set (mref (get-list) 1..2) '(30)) :error))
+
+
+(let ((nv (nested-vec 4 4 4)))
+ (let ((x 0))
+ (each-prod ((i 0..4)
+ (j 0..4)
+ (k 0..4))
+ (vtest (set (mref nv i j k) (inc x)) (succ x))))
+ (mtest
+ nv #(#(#( 1 2 3 4) #( 5 6 7 8) #( 9 10 11 12) #(13 14 15 16))
+ #(#(17 18 19 20) #(21 22 23 24) #(25 26 27 28) #(29 30 31 32))
+ #(#(33 34 35 36) #(37 38 39 40) #(41 42 43 44) #(45 46 47 48))
+ #(#(49 50 51 52) #(53 54 55 56) #(57 58 59 60) #(61 62 63 64)))
+ (set (mref nv 0 0 1..3) #(20 30)) #(20 30)
+ nv #(#(#( 1 20 30 4) #( 5 6 7 8) #( 9 10 11 12) #(13 14 15 16))
+ #(#(17 18 19 20) #(21 22 23 24) #(25 26 27 28) #(29 30 31 32))
+ #(#(33 34 35 36) #(37 38 39 40) #(41 42 43 44) #(45 46 47 48))
+ #(#(49 50 51 52) #(53 54 55 56) #(57 58 59 60) #(61 62 63 64)))
+ (set (mref nv 1 1..3) "AB") "AB"
+ nv #(#(#( 1 20 30 4) #( 5 6 7 8) #( 9 10 11 12) #(13 14 15 16))
+ #(#(17 18 19 20) #\A #\B #(29 30 31 32))
+ #(#(33 34 35 36) #(37 38 39 40) #(41 42 43 44) #(45 46 47 48))
+ #(#(49 50 51 52) #(53 54 55 56) #(57 58 59 60) #(61 62 63 64)))
+ (set (mref nv 1..3) '(B C)) (B C)
+ nv #(#(#( 1 20 30 4) #( 5 6 7 8) #( 9 10 11 12) #(13 14 15 16))
+ B
+ C
+ #(#(49 50 51 52) #(53 54 55 56) #(57 58 59 60) #(61 62 63 64)))))
+
+(let ((cf (lambda (x)
+ (lambda (y)
+ (lambda (z)
+ (+ x y z))))))
+ (test [mref cf 1 2 3] 6))
diff --git a/txr.1 b/txr.1
index 18011ce5..5600bcd3 100644
--- a/txr.1
+++ b/txr.1
@@ -35465,6 +35465,143 @@ including lists. In the case of hashes, a
.code refset
of a nonexistent key creates the key.
+.coNP Accessor @ mref
+.synb
+.mets (mref < sequence << index *)
+.mets (set (mref < sequence << index +) new-value)
+.syne
+.desc
+The
+.code mref
+accessor provides a mechanism for invoking a curried function. Its name
+reflects its usefulness for multi-dimensional indexing into nested sequences.
+
+The associated
+.code mref
+place which makes the operator an accessor provides in-place replacement of
+values in multi-dimensional sequences. There are some restrictions on the
+.meta index
+arguments when
+.code mref
+is used as a place.
+
+The
+.meta sequence
+argument is not necessarily a sequence, but may be object that can be called as
+a function with one argument. Except that
+.code call
+isn't a place, the expression
+.code "(mref x i)"
+is equivalent to
+.codn "(call x i)" :
+invoke the function/object
+.code x
+with argument
+.codn i .
+
+When multiple
+.meta index
+arguments are present, the return value of each previous application
+is expected to be another callable object, to which the next
+.meta index
+argument is applied. Thus
+.code "(mref x i j k)"
+is equivalent to
+.codn "(call (call (call x i) j) k)" .
+This is also equivalent to
+.codn "[[[x i] j] k]" ,
+provided that under the Lisp-1-style name resolution semantics of the DWIM
+brackets, the symbols
+.codn x ,
+.codn i ,
+.code j
+and
+.code k
+all resolve to bindings in the variable namespace.
+
+The expression
+.code "(mref x)"
+is not equivalent to
+.codn "(call x)" ;
+rather, it is equivalent to
+.codn x :
+there are no
+.meta index
+arguments and so the
+.code x
+object is taken as-is, not being applied to any index.
+
+In more detail, the
+.code mref
+function begins by taking
+.meta sequence
+as its an accumulator object. Then if there are
+.meta index
+arguments, it iterates over them. At each iteration step, it
+replaces the accumulator by treating the accumulator as a callable object
+and applying it to
+.meta index
+value and taking the resulting value as the new accumulator.
+After the iteration, the accumulator becomes the return value of
+the function.
+
+When
+.code mref
+is used as a place, only the rightmost
+.meta index
+argument may be a range. If any other argument is a range object,
+the behavior is unspecified.
+
+When
+.code mref
+is used as a place, and there is only one
+.meta index
+which is a range object, then the
+.meta sequence
+expression is also required to be a place, if it denotes a list or
+range object. If there are no
+.meta index
+augments then
+.meta sequence
+is unconditionally required to be a place.
+
+Note: the functions
+.code nested-vec
+and
+.code nested-vec-of
+may be used to create nested vectors which simulate multi-dimensional arrays.
+
+.TP* Examples:
+
+.verb
+ ;; Indexing:
+ (let ((ar '((1 2 3)
+ (4 5 6)
+ (7 8 9))))
+ (mref ar 1 1))
+ --> 5
+
+ ;; Updating value in nested sequence:
+ (let ((ar (vec (vec (vec 0 1 2 3)
+ (vec 4 5 6 7))
+ (vec (vec 8 9 10 11)
+ (vec 12 13 14 15)))))
+ (set (mref ar 0 0 1..3) "AB")
+ ar)
+ --> #(#(#( 0 #\eA #\eB 3)
+ #( 4 5 6 7))
+ #(#( 8 9 10 11)
+ #(12 13 14 15)))
+
+ ;; Invoking curried function:
+ (let ((cf (lambda (x)
+ (lambda (y)
+ (lambda (z)
+ (+ x y z))))))
+ [mref cf 1 2 3])
+ --> 6
+.brev
+
.coNP Function @ update
.synb
.mets (update < sequence << function )