Другой способ ответить на это, вместо того, чтобы увязнуть в тайных деталях точек последовательности и неопределенного поведения, - это просто спросить, , что они должны означать? Что было программистом пытаясь сделать?
Первый фрагмент, о котором спросили, i = i++ + ++i
, в моей книге довольно сумасшедший. Никто никогда не напишет его в реальной программе, не очевидно, что он делает, нет никакого мыслимого алгоритма, который кто-то мог бы попытаться закодировать, что привело бы к этой конкретной надуманной последовательности операций. И поскольку для вас и меня не очевидно, что он должен делать, это прекрасно в моей книге, если компилятор не может понять, что он должен делать.
Второй фрагмент i = i++
, немного легче понять. Кто-то явно пытается увеличить i и присвоить результат i. Но есть несколько способов сделать это в C. Самый простой способ добавить 1 к i и присвоить результат обратно i, тот же почти для любого языка программирования:
i = i + 1
C , конечно, имеет удобный ярлык:
i++
Это означает, что «добавьте 1 к i и присвойте результат обратно i». Итак, если мы построим мешанину из двух, написав
i = i++
, то, что мы действительно говорим, это «добавить 1 к i» и присвоить результат обратно i, и присвоить результат обратно i ». Мы сбиты с толку, поэтому меня это слишком беспокоит, если компилятор тоже запутался.
Реально, единственный раз, когда эти сумасшедшие выражения получаются написанными, когда люди используют их как искусственные примеры того, как ++ должен работать. И, конечно же, важно понимать, как работает ++. Но одно практическое правило для использования ++: «Если не очевидно, что означает выражение, использующее ++, не пишите».
Мы проводили бесчисленные часы на comp.lang.c, обсуждая выражения, подобные этим, и , почему они не определены. Два моих более длинных ответа, которые пытаются объяснить, почему, заархивированы в Интернете:
У вас есть вектор, а не массив. Вы можете использовать функцию rollapply
из пакета zoo, чтобы получить то, что вам нужно.
> x <- c(1, 2, 3, 10, 20, 30)
> #library(zoo)
> rollapply(x, 3, sum)
[1] 6 15 33 60
Взгляните на ?rollapply
для получения дополнительной информации о том, что делает rollapply
и как его использовать.
Я собрал пакет для обработки этих видов функций rolling, которые предлагают функциональные возможности, подобные zoo
's rollapply
, но с Rcpp на бэкэнд. Проверьте RcppRoll на CRAN.
library(microbenchmark)
library(zoo)
library(RcppRoll)
x <- rnorm(1E5)
all.equal( m1 <- rollapply(x, 3, sum), m2 <- roll_sum(x, 3) )
## from flodel
rsum.cumsum <- function(x, n = 3L) {
tail(cumsum(x) - cumsum(c(rep(0, n), head(x, -n))), -n + 1)
}
microbenchmark(
unit="ms",
times=10,
rollapply(x, 3, sum),
roll_sum(x, 3),
rsum.cumsum(x, 3)
)
дает мне
Unit: milliseconds
expr min lq median uq max neval
rollapply(x, 3, sum) 1056.646058 1068.867550 1076.550463 1113.71012 1131.230825 10
roll_sum(x, 3) 0.405992 0.442928 0.457642 0.51770 0.574455 10
rsum.cumsum(x, 3) 2.610119 2.821823 6.469593 11.33624 53.798711 10
Вам может показаться полезным, если скорость является проблемой.
Используя только базу R, которую вы могли бы сделать:
v <- c(1, 2, 3, 10, 20, 30)
grp <- 3
res <- sapply(1:(length(v)-grp+1),function(x){sum(v[x:(x+grp-1)])})
> res
[1] 6 15 33 60
Другой способ, более быстрый, чем sapply (сравнимый с @ flodel's rsum.cumsum
), заключается в следующем:
res <- rowSums(outer(1:(length(v)-grp+1),1:grp,FUN=function(i,j){v[(j - 1) + i]}))
< hr> Обновлен тест flodel:
x <- sample(1:1000)
rsum.rollapply <- function(x, n = 3L) rollapply(x, n, sum)
rsum.sapply <- function(x, n = 3L) sapply(1:(length(x)-n+1),function(i){sum(x[i:(i+n-1)])})
rsum.filter <- function(x, n = 3L) filter(x, rep(1, n))[-c(1, length(x))]
rsum.cumsum <- function(x, n = 3L) tail(cumsum(x) - cumsum(c(rep(0, n), head(x, -n))), -n + 1)
rsum.outer <- function(x, n = 3L) rowSums(outer(1:(length(x)-n+1),1:n,FUN=function(i,j){x[(j - 1) + i]}))
library(microbenchmark)
microbenchmark(
rsum.rollapply(x),
rsum.sapply(x),
rsum.filter(x),
rsum.cumsum(x),
rsum.outer(x)
)
# Unit: microseconds
# expr min lq median uq max neval
# rsum.rollapply(x) 9464.495 9929.4480 10223.2040 10752.7960 11808.779 100
# rsum.sapply(x) 3013.394 3251.1510 3466.9875 4031.6195 7029.333 100
# rsum.filter(x) 161.278 178.7185 229.7575 242.2375 359.676 100
# rsum.cumsum(x) 65.280 70.0800 88.1600 95.1995 181.758 100
# rsum.outer(x) 66.880 73.7600 82.8795 87.0400 131.519 100
Если скорость вызывает беспокойство, вы можете использовать фильтр свертки и отрубать концы:
rsum.filter <- function(x, n = 3L) filter(x, rep(1, n))[-c(1, length(x))]
Или даже быстрее, напишите его как разницу между двумя суммарными суммами:
rsum.cumsum <- function(x, n = 3L) tail(cumsum(x) - cumsum(c(rep(0, n), head(x, -n))), -n + 1)
Оба используют только базовые функции. Некоторые тесты:
x <- sample(1:1000)
rsum.rollapply <- function(x, n = 3L) rollapply(x, n, sum)
rsum.sapply <- function(x, n = 3L) sapply(1:(length(x)-n+1),function(i){
sum(x[i:(i+n-1)])})
library(microbenchmark)
microbenchmark(
rsum.rollapply(x),
rsum.sapply(x),
rsum.filter(x),
rsum.cumsum(x)
)
# Unit: microseconds
# expr min lq median uq max neval
# rsum.rollapply(x) 12891.315 13267.103 14635.002 17081.5860 28059.998 100
# rsum.sapply(x) 4287.533 4433.180 4547.126 5148.0205 12967.866 100
# rsum.filter(x) 170.165 208.661 269.648 290.2465 427.250 100
# rsum.cumsum(x) 97.539 130.289 142.889 159.3055 449.237 100
Также я предполагаю, что все методы будут быстрее, если x
и все применяемые веса были целыми числами, а не числами.