Skip to content

Commit 51221fe

Browse files
Add na.last in sortcache, sortordercache and ordercache (#227)
* add `na.last` for `sortcache`, `sortordercache` and `ordercache` also styling of cache.R * man page * fix tests * fix-up merge --------- Co-authored-by: Michael Chirico <chiricom@google.com>
1 parent df72e74 commit 51221fe

4 files changed

Lines changed: 171 additions & 141 deletions

File tree

NEWS.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@
5454
- `as.Date`, `as.POSIXct`, `as.POSXlt`, `as.complex`, and `as.raw` get an `integer64` method.
5555
- `as.integer64` gets `Date`, `POSIXct`, `POSXlt`, `complex`, `raw`, and `difftime` methods.
5656
1. `as.integer64.character` now supports hexadecimal (base 16) input when prefixed with "0x" or "-0x", e.g. `as.integer64("0x7FFFFFFFFFFFFFFF")`. Thanks @hcirellu for a PR which completes work begun by @marcpaterno.
57+
1. `sortcache`, `sortordercache` and `ordercache` get a new argument `na.last`.
5758

5859
## BUG FIXES
5960

R/cache.R

Lines changed: 136 additions & 138 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
# Licence: GPL2
66
# Provided 'as is', use at your own risk
77
# Created: 2011-12-11
8-
# Last changed: 2011-12-11
8+
# Last changed: 2026-01-09
99
# */
1010

1111
#' Atomic Caching
@@ -63,13 +63,13 @@ NULL
6363
#' @describeIn cache creates a new cache referencing `x`
6464
#' @export
6565
newcache = function(x) {
66-
env = new.env()
67-
vmode = typeof(x)
68-
if (vmode=="double" && is.integer64(x))
69-
vmode = "integer64"
70-
setattr(env, "class", c(paste("cache", vmode, sep="_"), "cache", "environment"))
71-
assign("x", x, envir=env)
72-
env
66+
env = new.env()
67+
vmode = typeof(x)
68+
if (vmode=="double" && is.integer64(x))
69+
vmode = "integer64"
70+
setattr(env, "class", c(paste("cache", vmode, sep="_"), "cache", "environment"))
71+
assign("x", x, envir=env)
72+
env
7373
}
7474

7575
#' @describeIn cache forces `x` to have a cache
@@ -106,41 +106,41 @@ cache = function(x) {
106106
#' @param value An object to be stored in the cache
107107
#' @export
108108
setcache = function(x, which, value) {
109-
env = jamcache(x)
110-
assign(which, value, envir=env)
111-
env
109+
env = jamcache(x)
110+
assign(which, value, envir=env)
111+
env
112112
}
113113

114114
#' @describeIn cache gets cache value 'which' from `x`
115115
#' @export
116116
getcache = function(x, which) {
117-
cache = attr(x, "cache")
118-
if (is.null(cache))
119-
return(NULL)
120-
if (still.identical(x, get("x", envir=cache, inherits=FALSE))) {
121-
if (exists(which, envir=cache, inherits=FALSE))
122-
get(which, envir=cache, inherits=FALSE)
123-
else
124-
NULL
125-
} else {
126-
remcache(x)
127-
warning("removed outdated cache")
117+
cache = attr(x, "cache")
118+
if (is.null(cache))
119+
return(NULL)
120+
if (still.identical(x, get("x", envir=cache, inherits=FALSE))) {
121+
if (exists(which, envir=cache, inherits=FALSE))
122+
get(which, envir=cache, inherits=FALSE)
123+
else
128124
NULL
129-
}
125+
} else {
126+
remcache(x)
127+
warning("removed outdated cache")
128+
NULL
129+
}
130130
}
131131

132132
#' @describeIn cache removes the cache from `x`
133133
#' @export
134134
remcache = function(x) {
135-
setattr(x, "cache", NULL)
136-
invisible()
135+
setattr(x, "cache", NULL)
136+
invisible()
137137
}
138138

139139
#' @rdname cache
140140
#' @param all.names,pattern passed to [ls()] when listing the cache content
141141
#' @param ... ignored
142142
#' @export
143-
print.cache= function(x, all.names=FALSE, pattern, ...) {
143+
print.cache = function(x, all.names=FALSE, pattern, ...) {
144144
l = ls(x, all.names, pattern=pattern)
145145
cat(class(x)[1L], ": ", paste(l, collapse=" - "), "\n", sep="")
146146
invisible(l)
@@ -180,101 +180,100 @@ print.cache= function(x, all.names=FALSE, pattern, ...) {
180180
#'
181181
#' @keywords environment
182182
#' @export
183-
hashcache =function(x, nunique=NULL, ...) {
184-
env = jamcache(x)
185-
if (is.null(nunique))
186-
nunique = env$nunique
187-
env = hashmap(x, nunique=nunique, cache=env, ...)
188-
if (is.null(nunique) && env$nunique<sqrt(length(x)))
189-
env = hashmap(x, nunique=env$nunique, cache=env, ...)
190-
na.count(x) # since x has cache, na.count() will update the cache, unless its already there
191-
# different from sortcache, ordercache and sortordercache we do not set nties: hastab is too expensive
192-
invisible(env)
183+
hashcache = function(x, nunique=NULL, ...) {
184+
env = jamcache(x)
185+
if (is.null(nunique))
186+
nunique = env$nunique
187+
env = hashmap(x, nunique=nunique, cache=env, ...)
188+
if (is.null(nunique) && env$nunique<sqrt(length(x)))
189+
env = hashmap(x, nunique=env$nunique, cache=env, ...)
190+
na.count(x) # since x has cache, na.count() will update the cache, unless its already there
191+
# different from sortcache, ordercache and sortordercache we do not set nties: hastab is too expensive
192+
invisible(env)
193193
}
194194

195195
#' @rdname hashcache
196196
#' @param has.na boolean scalar defining whether the input vector might contain
197197
#' `NA`s. If we know we don't have `NA`s, this may speed-up. _Note_ that you
198198
#' risk a crash if there are unexpected `NA`s with `has.na=FALSE`.
199+
#' @param na.last boolean scalar defining whether NA should be last.
199200
#' @export
200-
sortcache = function(x, has.na = NULL) {
201-
if (is.null(has.na)) {
202-
na.count = getcache(x, "na.count")
203-
if (is.null(na.count))
204-
has.na = TRUE
205-
else
206-
has.na = na.count > 0L
207-
}
208-
s = clone(x)
209-
na.count = ramsort(s, has.na = has.na, na.last = FALSE, decreasing = FALSE, stable = FALSE, optimize = "time")
210-
nut = .Call(C_r_ram_integer64_sortnut, x = s)
211-
setcache(x, "sort", s)
212-
setcache(x, "na.count", na.count)
213-
setcache(x, "nunique", nut[[1L]])
214-
setcache(x, "nties", nut[[2L]])
215-
invisible(x)
201+
sortcache = function(x, has.na=NULL, na.last=FALSE) {
202+
if (is.null(has.na)) {
203+
na.count = getcache(x, "na.count")
204+
if (is.null(na.count))
205+
has.na = TRUE
206+
else
207+
has.na = na.count > 0L
208+
}
209+
s = clone(x)
210+
na.count = ramsort(s, has.na=has.na, na.last=na.last, decreasing=FALSE, stable=FALSE, optimize="time")
211+
nut = .Call(C_r_ram_integer64_sortnut, x=s)
212+
setcache(x, "sort", s)
213+
setcache(x, "na.count", na.count)
214+
setcache(x, "nunique", nut[[1L]])
215+
setcache(x, "nties", nut[[2L]])
216+
invisible(x)
216217
}
217218

218219
#' @rdname hashcache
219220
#' @param stable boolean scalar defining whether stable sorting is needed. Allowing
220221
#' non-stable may speed-up.
221222
#' @export
222-
sortordercache = function(x, has.na = NULL, stable = NULL) {
223-
if (is.null(has.na)) {
224-
na.count = getcache(x, "na.count")
225-
if (is.null(na.count))
226-
has.na = TRUE
227-
else
228-
has.na = na.count > 0L
229-
}
230-
if (is.null(stable)) {
231-
nunique = getcache(x, "nunique")
232-
if (is.null(nunique))
233-
stable = TRUE
234-
else
235-
stable = nunique < length(x)
236-
}
237-
s = clone(x)
238-
o = seq_along(x)
239-
na.count =
240-
ramsortorder(s, o, has.na = has.na, na.last = FALSE, decreasing = FALSE, stable = stable, optimize = "time")
241-
nut = .Call(C_r_ram_integer64_sortnut, x = s)
242-
setcache(x, "sort", s)
243-
setcache(x, "order", o)
244-
setcache(x, "na.count", na.count)
245-
setcache(x, "nunique", nut[[1L]])
246-
setcache(x, "nties", nut[[2L]])
247-
invisible(x)
223+
sortordercache = function(x, has.na=NULL, stable=NULL, na.last=FALSE) {
224+
if (is.null(has.na)) {
225+
na.count = getcache(x, "na.count")
226+
if (is.null(na.count))
227+
has.na = TRUE
228+
else
229+
has.na = na.count > 0L
230+
}
231+
if (is.null(stable)) {
232+
nunique = getcache(x, "nunique")
233+
if (is.null(nunique))
234+
stable = TRUE
235+
else
236+
stable = nunique < length(x)
237+
}
238+
s = clone(x)
239+
o = seq_along(x)
240+
na.count = ramsortorder(s, o, has.na=has.na, na.last=na.last, decreasing=FALSE, stable=stable, optimize="time")
241+
nut = .Call(C_r_ram_integer64_sortnut, x=s)
242+
setcache(x, "sort", s)
243+
setcache(x, "order", o)
244+
setcache(x, "na.count", na.count)
245+
setcache(x, "nunique", nut[[1L]])
246+
setcache(x, "nties", nut[[2L]])
247+
invisible(x)
248248
}
249249

250250
#' @rdname hashcache
251251
#' @param optimize by default ramsort optimizes for 'time' which requires more RAM,
252252
#' set to 'memory' to minimize RAM requirements and sacrifice speed.
253253
#' @export
254-
ordercache = function(x, has.na = NULL, stable = NULL, optimize = "time") {
255-
if (is.null(has.na)) {
256-
na.count = getcache(x, "na.count")
257-
if (is.null(na.count))
258-
has.na = TRUE
259-
else
260-
has.na = na.count > 0L
261-
}
262-
if (is.null(stable)) {
263-
nunique = getcache(x, "nunique")
264-
if (is.null(nunique))
265-
stable = TRUE
266-
else
267-
stable = nunique < length(x)
268-
}
269-
o = seq_along(x)
270-
na.count =
271-
ramorder(x, o, has.na = has.na, na.last = FALSE, decreasing = FALSE, stable = stable, optimize = optimize)
272-
nut = .Call(C_r_ram_integer64_ordernut, table = x, order = o)
273-
setcache(x, "order", o)
274-
setcache(x, "na.count", na.count)
275-
setcache(x, "nunique", nut[[1L]])
276-
setcache(x, "nties", nut[[2L]])
277-
invisible(x)
254+
ordercache = function(x, has.na=NULL, stable=NULL, optimize="time", na.last=FALSE) {
255+
if (is.null(has.na)) {
256+
na.count = getcache(x, "na.count")
257+
if (is.null(na.count))
258+
has.na = TRUE
259+
else
260+
has.na = na.count > 0L
261+
}
262+
if (is.null(stable)) {
263+
nunique = getcache(x, "nunique")
264+
if (is.null(nunique))
265+
stable = TRUE
266+
else
267+
stable = nunique < length(x)
268+
}
269+
o = seq_along(x)
270+
na.count = ramorder(x, o, has.na=has.na, na.last=na.last, decreasing=FALSE, stable=stable, optimize=optimize)
271+
nut = .Call(C_r_ram_integer64_ordernut, table=x, order=o)
272+
setcache(x, "order", o)
273+
setcache(x, "na.count", na.count)
274+
setcache(x, "nunique", nut[[1L]])
275+
setcache(x, "nties", nut[[2L]])
276+
invisible(x)
278277
}
279278

280279
#' Small cache access methods
@@ -320,10 +319,10 @@ NULL
320319
na.count.integer64 = function(x, ...) {
321320
env = cache(x)
322321
if (is.null(env))
323-
return(.Call(C_r_ram_integer64_nacount, x = x))
322+
return(.Call(C_r_ram_integer64_nacount, x=x))
324323
if (exists("na.count", envir=env, inherits=FALSE))
325324
return(get("na.count", envir=env, inherits=FALSE))
326-
ret = .Call(C_r_ram_integer64_nacount, x = x)
325+
ret = .Call(C_r_ram_integer64_nacount, x=x)
327326
assign("na.count", ret, envir=env)
328327
ret
329328
}
@@ -332,61 +331,60 @@ na.count.integer64 = function(x, ...) {
332331
#' usually [length()] minus `na.count`.
333332
#' @export
334333
nvalid.integer64 = function(x, ...) {
335-
length(x) - na.count(x)
334+
length(x) - na.count(x)
336335
}
337336

338337
#' @describeIn is.sorted.integer64 checks for sortedness of `x` (NAs sorted first)
339338
#' @export
340339
is.sorted.integer64 = function(x, ...) {
341340
env = cache(x)
342341
if (is.null(env))
343-
return(.Call(C_r_ram_integer64_issorted_asc, x = x))
342+
return(.Call(C_r_ram_integer64_issorted_asc, x=x))
344343
if (exists("is.sorted", envir=env, inherits=FALSE))
345344
return(get("is.sorted", envir=env, inherits=FALSE))
346-
ret = .Call(C_r_ram_integer64_issorted_asc, x = x)
345+
ret = .Call(C_r_ram_integer64_issorted_asc, x=x)
347346
assign("is.sorted", ret, envir=env)
348347
ret
349348
}
350349

351350
#' @describeIn is.sorted.integer64 returns the number of unique values
352351
#' @export
353352
nunique.integer64 = function(x, ...) {
354-
env = cache(x)
355-
if (is.null(env))
356-
has.cache = FALSE
357-
else if (exists("nunique", envir=env, inherits=FALSE))
358-
return(get("nunique", envir=env, inherits=FALSE))
359-
else # nolint: unreachable_code_linter. TODO(r-lib/lintr#2710): Re-enable.
360-
has.cache = TRUE
361-
if (is.sorted(x)) {
362-
ret = .Call(C_r_ram_integer64_sortnut, x = x)
363-
if (has.cache) {
364-
assign("nunique", ret[1L], envir=env)
365-
assign("nties", ret[2L], envir=env)
366-
}
367-
ret[1L]
368-
} else {
369-
h = hashmap(x)
370-
if (has.cache)
371-
assign("nunique", h$nunique, envir=env)
372-
h$nunique
353+
env = cache(x)
354+
if (is.null(env))
355+
has.cache = FALSE
356+
else if (exists("nunique", envir=env, inherits=FALSE))
357+
return(get("nunique", envir=env, inherits=FALSE))
358+
else # nolint: unreachable_code_linter. TODO(r-lib/lintr#2710): Re-enable.
359+
has.cache = TRUE
360+
if (is.sorted(x)) {
361+
ret = .Call(C_r_ram_integer64_sortnut, x=x)
362+
if (has.cache) {
363+
assign("nunique", ret[1L], envir=env)
364+
assign("nties", ret[2L], envir=env)
373365
}
366+
ret[1L]
367+
} else {
368+
h = hashmap(x)
369+
if (has.cache)
370+
assign("nunique", h$nunique, envir=env)
371+
h$nunique
372+
}
374373
}
375374

376375
#' @describeIn is.sorted.integer64 returns the number of tied values.
377376
#' @export
378377
nties.integer64 = function(x, ...) {
379-
cv = getcache(x, "nties")
380-
if (is.null(cv)) {
381-
if (is.sorted(x)) {
382-
cv = .Call(C_r_ram_integer64_sortnut, x = x)[2L]
383-
} else {
384-
s = clone(x)
385-
# nolint next: object_usage_linter. Keep the output of in-place ramsort for debugging.
386-
na.count =
387-
ramsort(s, has.na = TRUE, na.last = FALSE, decreasing = FALSE, stable = FALSE, optimize = "time")
388-
cv = .Call(C_r_ram_integer64_sortnut, x = s)[[2L]]
389-
}
378+
cv = getcache(x, "nties")
379+
if (is.null(cv)) {
380+
if (is.sorted(x)) {
381+
cv = .Call(C_r_ram_integer64_sortnut, x=x)[2L]
382+
} else {
383+
s = clone(x)
384+
# nolint next: object_usage_linter. Keep the output of in-place ramsort for debugging.
385+
na.count = ramsort(s, has.na=TRUE, na.last=FALSE, decreasing=FALSE, stable=FALSE, optimize="time")
386+
cv = .Call(C_r_ram_integer64_sortnut, x=s)[[2L]]
390387
}
391-
cv
388+
}
389+
cv
392390
}

0 commit comments

Comments
 (0)