@@ -191,7 +191,8 @@ arithmetic expression must be of the same type (for example they should be both
191191
192192The lhs and the rhs can either have the same size or one of them could
193193be a primitive value of the appropriate type, for example adding a
194- ` NumericVector ` and a ` double ` .
194+ ` NumericVector ` and a ` double ` . Unlike \proglang{R}, \sugar does not recycle
195+ vectors of different sizes; see the section on expressions and assignment.
195196
196197## Binary logical operators
197198
@@ -518,6 +519,69 @@ functions (\textsl{i.e.} you) should place an \code{RNGScope} at the
518519appropriate level of your code.
519520
520521
522+ # Expressions and assignment
523+
524+ \sugar expressions are evaluated lazily: ` x + y ` builds an object describing
525+ the computation, and its elements are only computed when the expression is
526+ assigned to a vector or otherwise consumed, for example by ` sum() ` .
527+
528+ ## Operand lengths
529+
530+ Unlike \proglang{R}, \sugar does not recycle: the vectors combined by an
531+ expression must all have the same length, and an error is signalled
532+ otherwise. Likewise, a vector assigned into a range, row or column (as in
533+ ` m(_, 0) = x ` ) must have the length of that target. Single values, like the
534+ ` 2.0 ` in ` x + 2.0 ` , can be combined with vectors of any length.
535+
536+ ## Storing expressions
537+
538+ An expression holds the vectors it reads by reference, and any nested
539+ expressions by value. It can be stored, for example in an ` auto ` variable,
540+ and evaluated later, as long as the vectors it reads are still alive:
541+
542+ ``` cpp
543+ NumericVector x, y;
544+
545+ // fine: x and y outlive e
546+ auto e = x * 2.0 + y;
547+ NumericVector z = e;
548+ ```
549+
550+ An expression reading from a temporary vector, however, has to be
551+ evaluated in the statement that creates it:
552+
553+ ``` cpp
554+ // wrong: the clone is destroyed at the end of
555+ // the statement, while e still refers to it
556+ auto e = clone(x) + 1.0 ;
557+
558+ // fine
559+ NumericVector z = clone(x) + 1.0 ;
560+ ```
561+
562+ ## Assignment
563+
564+ Assigning an expression to an existing vector of the same length writes the
565+ result into that vector's storage. As with any modification made through an
566+ \pkg{Rcpp} vector, this is visible through every other reference to the same
567+ \proglang{R} object, including the argument passed in from \proglang{R}:
568+
569+ ``` cpp
570+ // [[Rcpp::export]]
571+ void twice (NumericVector x) {
572+ // modifies the caller's vector
573+ x = x * 2.0;
574+ }
575+ ```
576+
577+ When the lengths differ, a new vector is allocated instead.
578+
579+ An expression may read from the vector it is assigned to, as in
580+ `x = rev(x)`. Expressions whose elements only depend on the corresponding
581+ elements of their operands (arithmetic, comparisons, mathematical functions,
582+ `ifelse()`, and so on) are written directly; others are first evaluated into
583+ a temporary vector.
584+
521585# Performance
522586\label{sec:performance}
523587
@@ -658,7 +722,7 @@ public:
658722 RESULT_R_TYPE>::type STORAGE;
659723
660724 Sapply(const VEC& vec_, Function fun_) :
661- vec(vec_), fun(fun_){}
725+ vec(vec_.get_ref() ), fun(fun_){}
662726
663727 inline STORAGE operator []( int i ) const {
664728 return converter_type::get(fun(vec[i]));
@@ -669,7 +733,7 @@ public:
669733 }
670734
671735private:
672- const VEC& vec;
736+ typename Rcpp::traits::sugar_operand< VEC >::type vec;
673737 Function fun;
674738};
675739
@@ -801,18 +865,22 @@ is the manifestation of the _CRTP_.
801865
802866
803867The constructor of the `Sapply` class template is straightforward, it
804- simply consists of holding the reference to the input expression and the
805- function.
868+ simply consists of holding the input expression and the function.
806869
807870```cpp
808871Sapply(const VEC& vec_, Function fun_):
809- vec(vec_), fun(fun_){}
872+ vec(vec_.get_ref() ), fun(fun_){}
810873
811874private:
812- const VEC& vec;
875+ typename Rcpp::traits::sugar_operand< VEC>::type vec;
813876 Function fun;
814877```
815878
879+ The input is held through ` sugar_operand ` : by reference when it is a vector,
880+ and by value when it is itself a \sugar expression. Those are typically
881+ temporaries, so holding them by reference would leave ` Sapply ` referring to
882+ destroyed objects once the statement that created it ends.
883+
816884### Implementation
817885
818886The indexing operator and the ` size ` member function is what
@@ -830,6 +898,13 @@ inline int size() const {
830898}
831899```
832900
901+ Expressions whose $i^{\text{th}}$ element only depends on the
902+ $i^{\text{th}}$ elements of their operands can say so by declaring
903+ ` typedef Rcpp::traits::elementwise_operands<VEC> rcpp_elementwise ` , which
904+ lets them be written in place when assigned to one of those operands.
905+ ` Sapply ` does not, since the function it applies may read any element of
906+ the vector being assigned to.
907+
833908# Summary
834909
835910TBD
0 commit comments