使用Rcpp进行就地编辑后的求和函数问题

时间:2019-02-24 21:53:52

标签: r sum rcpp

如果在Rcpp中修改IntegerVector的值:

#include <Rcpp.h>
using namespace Rcpp;

// [[Rcpp::export]]
void test(IntegerVector x) {
  x[5] = 77;
}

在R中运行test()函数后:

x <- 10:1
test(x)
print(x)  #  10  9  8  7  6 77  4  3  2  1
sum(x)  # 55

sum函数返回原始数组10:1的值。 我怎么解决这个问题?

使用例如改为x <- sample(10L)

2 个答案:

答案 0 :(得分:8)

@F.Privé的怀疑是正确的。这是ALTREP的问题,c.f.尚不支持Rcpp。 Rcpp/#812Rcpp/#906。我们可以通过检查变量String html = "<pre class=\"cg-msgbody cg-view-msgbody\">" + "<span class=\"cg-msgspan\">" + "<span>**the text I want to get is present here, " + "how can I get it using JSoup?**</span>" + "</span>" + "</pre>"; org.jsoup.nodes.Document document = Jsoup.parse(html); //a with href Element link = document.select("span").last(); System.out.println("Text: " + link.text()); 来更明确地看到这一点:

x

第一块给出:

#include <Rcpp.h>
using namespace Rcpp;

// [[Rcpp::export]]
void test(IntegerVector x) {
  x[5] = 77;
}

/*** R
x <- 10:1
.Internal(inspect(x))
test(x)
.Internal(inspect(x))
print(x)  #  10  9  8  7  6 77  4  3  2  1
sum(x)  # 55

x <- 10:1
.Internal(inspect(x))
x[6] <- 77L
.Internal(inspect(x))
print(x)  #  10  9  8  7  6 77  4  3  2  1
sum(x)
*/

第二个块给出:

> x <- 10:1

> .Internal(inspect(x))
@55f79a9d6c58 13 INTSXP g0c0 [NAM(3)]  10 : 1 (compact)

> test(x)

> .Internal(inspect(x))
@55f79a9d6c58 13 INTSXP g0c0 [NAM(3)]  10 : 1 (expanded)

> print(x)  #  10  9  8  7  6 77  4  3  2  1
 [1] 10  9  8  7  6 77  4  3  2  1

> sum(x) # 55
[1] 55

因此,在更改向量中的值之后,它仍然声称是> x <- 10:1 > .Internal(inspect(x)) @55f79b1f9018 13 INTSXP g0c0 [NAM(3)] 10 : 1 (compact) > x[6] <- 77L > .Internal(inspect(x)) @55f7a096e5e8 13 INTSXP g0c4 [NAM(1)] (len=10, tl=0) 10,9,8,7,6,... > print(x) # 10 9 8 7 6 77 4 3 2 1 [1] 10 9 8 7 6 77 4 3 2 1 > sum(x) [1] 127 ,为此10 : 1使用快捷方式。有关ALTREP的更多信息(包括参考资料),请参见here

目前,唯一的解决方案似乎是避免更改function参数。

答案 1 :(得分:2)

这是我为此发布的elsewhere

所以这里有几件事,其中@ltierney和/或@kalibera都涉及其中,但也许可以从更具体的可能的编码模式中受益。

问题的症结在于通过写入SEXP的DATAPTR寻址存储器的元素来内联修改SEXP的有效负载。是否可以做(是),是否可以安全地做而不确认/保证可以做(不,从不)。对于在同一块C / C ++代码中创建的对象,您可能会有这样的先验保证,但是您不会想要从R传递下来的对象。

具体来说,我不知道在不先检查INTEGER()的情况下,是否可以在生活在R空间中的SEXP上写入MAYBE_SHARED()返回的指针是否可行。如果MAYBE_SHARED(x)返回FALSE,就可以了,您可以完全按照代码的方式继续写指针。

如果MAYBE_SHARED(x) == TRUE,则需要复制,对副本进行操作,然后将其返回。当您使用C / C ++代码时,在获取指向事物的数据指针的级别上,那么您需要显式地导致该重复,保护新的重复结果等。

现在发生这种特殊情况的原因是在紧凑序列的情况下,除非R本身以特定的非默认方式构建,否则紧凑序列始终具有NAMED(x) == MAXNAMED(即2)创作。正如卢克指出的那样,这可能会改变,但目前是设计使然。因此,即使在.call情况下,闭包并没有强制NAMED计数,紧缩序列在进行内联修改之前也始终需要复制。尽管这是一个选择,我们可以在紧凑序列情况下有所作为,但卢克关于其他ALTREP的观点更为重要。

可能存在ALTREP SEXP,由于某种原因或其他原因,由INTEGER返回的指针所指向的内存实际上不是可写内存。这些ALTREP类声明的方式是通过执行紧凑序列的相同操作,即将自身标记为“ IMMUTABLE”,即通过进行MARK_NOT_MUTABLE(x)(当前将NAMED设置为{{1} },但可以防止将来最终更改为引用计数)。这声明合同,必须在获取数据指针并将其写入的任何代码之前复制SEXP。

最终,我同意这是一种非常奇怪的意外行为,但这是由于未能遵守合同而一直存在的。过去在某些情况下忽略/放松可能是安全的(有点,但我仍然对此表示怀疑),但是由于ALTREP的出现,出于本主题中概述的原因,现在必须始终遵循它。

因此,要从一个既有的(R级)SEXP中获取dataptr并进行写入的所有所有代码,都必须遵循(或相当小心)的模式:

MAXNAMED

任何不执行此操作而写入未从SEXP检索到的数据指针的代码(即,从R端传来的任何事情)都已经违反了C-API合同,但是现在也致命ALTREP不安全,因为显示了激励人的例子。

再记住一次,紧缩序列的行为可能有所不同,因此该代码恰好起作用,但是其他ALTREP类(Luke提到了由内存映射文件支持的ALTREPS)却无法,因此紧缩序列的行为是其实不是这里的问题。

我希望这会有所帮助,并使事情变得更清楚。

最佳