Skip to content

Commit 0ebe7f3

Browse files
committed
Allow IntegerVector subsetting (0-based); add unit tests; use VectorBase so sugar exprs work
1 parent d94ee6b commit 0ebe7f3

File tree

4 files changed

+131
-18
lines changed

4 files changed

+131
-18
lines changed

inst/include/Rcpp/vector/Subsetter.h

Lines changed: 18 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -72,20 +72,30 @@ class Subsetter {
7272
inline Vector<RTYPE, StoragePolicy> subset_impl( const VECTOR& this_, const Vector<LGLSXP, OtherStoragePolicy>& x ) const {
7373
Vector<INTSXP, StoragePolicy> tmp = which_na(x);
7474
if (!tmp.size()) return Vector<RTYPE, StoragePolicy>(0);
75-
else return subset_impl__hidden(this_, tmp);
75+
else return subset_impl(this_, tmp);
7676
}
7777

7878
// Subsetting for characters
7979
template <template <class> class OtherStoragePolicy>
8080
inline Vector<RTYPE, StoragePolicy> subset_impl( const VECTOR& this_, const Vector<STRSXP, OtherStoragePolicy>& x ) const {
81+
82+
if (Rf_isNull( Rf_getAttrib(this_, R_NamesSymbol) )) {
83+
int n = x.size();
84+
Vector<RTYPE, StoragePolicy> output(n);
85+
for (int i=0; i < n; ++i) {
86+
output[i] = traits::get_na<RTYPE>();
87+
}
88+
return output;
89+
}
90+
8191
Vector<STRSXP, StoragePolicy> names = as< Vector<STRSXP, StoragePolicy> >(Rf_getAttrib(this_, R_NamesSymbol));
8292
Vector<INTSXP, StoragePolicy> idx = match(x, names); // match returns 1-based index
8393
// apparently, we don't see sugar, so we have to do this manually
8494
Vector<INTSXP, StoragePolicy> idxm1 = no_init(idx.size());
8595
for (int i=0; i < idx.size(); ++i) {
8696
idxm1[i] = idx[i] - 1;
8797
}
88-
Vector<RTYPE, StoragePolicy> output = subset_impl__hidden(this_, idxm1);
98+
Vector<RTYPE, StoragePolicy> output = subset_impl(this_, idxm1);
8999
int n = output.size();
90100
if (n == 0) return Vector<RTYPE, StoragePolicy>(0);
91101
Vector<STRSXP, StoragePolicy> out_names = no_init(n);
@@ -100,21 +110,11 @@ class Subsetter {
100110
const VECTOR& vec;
101111
const T& other;
102112

103-
private:
104-
105-
// Subsetting for integers -- note that it is 0-based
106-
template <template <class> class OtherStoragePolicy>
107-
inline Vector<RTYPE, StoragePolicy>
108-
subset_impl( const VECTOR this_, const Vector<INTSXP, OtherStoragePolicy>& x ) const {
109-
stop("Subset is not yet implemented for IntegerVectors");
110-
return Vector<RTYPE, StoragePolicy>();
111-
}
112-
113113
// Subsetting for integers -- note that it is 0-based
114114
// hidden until we decide whether to go with 0 or 1 based indexing
115115
template <template <class> class OtherStoragePolicy>
116116
inline Vector<RTYPE, StoragePolicy>
117-
subset_impl__hidden( const VECTOR this_, const Vector<INTSXP, OtherStoragePolicy>& x ) const {
117+
subset_impl( const VECTOR this_, const Vector<INTSXP, OtherStoragePolicy>& x ) const {
118118
int n = x.size();
119119
if (n == 0) return this_;
120120
Vector<RTYPE, StoragePolicy> output = no_init(n);
@@ -124,8 +124,12 @@ class Subsetter {
124124
else if (x[i] > this_.size() - 1) output[i] = traits::get_na<RTYPE>();
125125
else output[i] = (this_)[ x[i] ];
126126
}
127+
127128
if (!Rf_isNull( Rf_getAttrib( this_, R_NamesSymbol) )) {
128-
Vector<STRSXP, StoragePolicy> thisnames = as<Vector<STRSXP, StoragePolicy> >(Rf_getAttrib(this_, R_NamesSymbol));
129+
130+
Vector<STRSXP, StoragePolicy> thisnames =
131+
as<Vector<STRSXP, StoragePolicy> >(Rf_getAttrib(this_, R_NamesSymbol));
132+
129133
Vector<STRSXP, StoragePolicy> outnames = no_init(n);
130134
for (int i=0; i < n; ++i) {
131135
if (x[i] == NA_INTEGER) outnames[i] = NA_STRING;

inst/include/Rcpp/vector/Vector.h

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -313,11 +313,12 @@ class Vector :
313313
return RObject( Storage::get__() );
314314
}
315315

316-
template <int RTYPE2>
317-
Vector operator[](const Vector<RTYPE2>& rhs) const {
318-
return Subsetter<RTYPE, StoragePolicy, Vector<RTYPE2, StoragePolicy> >(*this, rhs);
316+
// sugar subsetting requires dispatch on VectorBase
317+
template <int RTYPE2, bool na, typename T>
318+
Vector operator[](const VectorBase<RTYPE2, na, T>& rhs) const {
319+
return Subsetter<RTYPE, StoragePolicy, Vector<RTYPE2, PreserveStorage> >(*this, rhs);
319320
}
320-
321+
321322
Vector& sort(){
322323
typename traits::storage_type<RTYPE>::type* start = internal::r_vector_start<RTYPE>( Storage::get__() ) ;
323324
std::sort(

inst/unitTests/cpp/Subset.cpp

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
#include <Rcpp.h>
2+
using namespace Rcpp;
3+
4+
// [[Rcpp::export]]
5+
NumericVector subset_test_int(NumericVector x, IntegerVector y) {
6+
return x[y];
7+
}
8+
9+
// [[Rcpp::export]]
10+
NumericVector subset_test_lgcl(NumericVector x, LogicalVector y) {
11+
return x[y];
12+
}
13+
14+
// [[Rcpp::export]]
15+
NumericVector subset_test_char(NumericVector x, CharacterVector y) {
16+
return x[y];
17+
}
18+
19+
// [[Rcpp::export]]
20+
List subset_test_list(List x, CharacterVector y) {
21+
return x[y];
22+
}
23+
24+
// [[Rcpp::export]]
25+
List subset_test_list_int(List x, IntegerVector y) {
26+
return x[y];
27+
}
28+
29+
// [[Rcpp::export]]
30+
List subset_test_list_lgcl(List x, LogicalVector y) {
31+
return x[y];
32+
}
33+
34+
// [[Rcpp::export]]
35+
NumericVector subset_test_greater_0(NumericVector x) {
36+
return x[ x > 0 ];
37+
}
38+
39+
// [[Rcpp::export]]
40+
List subset_test_literal(List x) {
41+
return x["foo"];
42+
}

inst/unitTests/runit.subset.R

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
#!/usr/bin/r -t
2+
# -*- mode: R; tab-width: 4; -*-
3+
#
4+
# Copyright (C) 2014 Dirk Eddelbuettel, Romain Francois and Kevin Ushey
5+
#
6+
# This file is part of Rcpp.
7+
#
8+
# Rcpp is free software: you can redistribute it and/or modify it
9+
# under the terms of the GNU General Public License as published by
10+
# the Free Software Foundation, either version 2 of the License, or
11+
# (at your option) any later version.
12+
#
13+
# Rcpp is distributed in the hope that it will be useful, but
14+
# WITHOUT ANY WARRANTY; without even the implied warranty of
15+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16+
# GNU General Public License for more details.
17+
#
18+
# You should have received a copy of the GNU General Public License
19+
# along with Rcpp. If not, see <http://www.gnu.org/licenses/>.
20+
21+
.runThisTest <- Sys.getenv("RunAllRcppTests") == "yes"
22+
23+
if (.runThisTest) {
24+
25+
.setUp <- Rcpp:::unitTestSetup("Subset.cpp")
26+
27+
test.subset <- function() {
28+
29+
x <- rnorm(5)
30+
names(x) <- letters[1:5]
31+
32+
checkIdentical( x[c(1, 2, 10)], subset_test_int(x, c(0L, 1L, 9L)),
33+
"integer subsetting")
34+
35+
checkIdentical( x[ c('b', 'a') ], subset_test_char(x, c('b', 'a')),
36+
"character subsetting")
37+
38+
checkIdentical( c(1, 2, 3)['a'], subset_test_char( c(1, 2, 3), 'a' ),
39+
"character subsetting -- no names on x")
40+
41+
lgcl <- c(TRUE, FALSE, NA, TRUE, TRUE)
42+
checkIdentical(
43+
x[lgcl],
44+
subset_test_lgcl(x, lgcl),
45+
"logical subsetting" )
46+
47+
names(x) <- c('a', 'b', 'b', 'c', 'd')
48+
checkIdentical(
49+
x['b'],
50+
subset_test_char(x, 'b'),
51+
"character subsetting -- duplicated name")
52+
53+
l <- as.list(x)
54+
checkIdentical(
55+
l[c('b', 'Q')],
56+
subset_test_list(x, c('b', 'Q')),
57+
"list subsetting")
58+
59+
checkIdentical(
60+
x[ x > 0 ],
61+
subset_test_greater_0(x),
62+
"sugar subsetting (x[x > 0])")
63+
64+
}
65+
66+
}

0 commit comments

Comments
 (0)