How to break a loop after a certain elapsed time? I have a function that collects observational data from a user. The user should have a pre-defined time limit, when the data ar
I was curious to see if anyone had a real Rland solution to this problem, but it looks like not.
One possible solution is to shell out with system() and run a command that allows reading input with a time limit. This is inherently platform-specific. The Unix bash shell provides a read builtin that is perfect for this purpose, and this will also work on the Cygwin emulation layer on Windows. Unfortunately, I haven't ever come across a command available on the vanilla native Windows platform that provides sufficient functionality for this. set /p can read arbitrary string input but does not provide a timeout, while choice.exe provides a timeout (accurate to the second) but only supports selection of an item from a finite list of (single-character!) items, as opposed to arbitrary string input. Fun fact: choice.exe has its own Wikipedia article.
Here's how you can use read
to do this:
LIMIT <- 10; ## conceptual constant
end <- Sys.time()+(left <- LIMIT); ## precompute end of input window and init left
repeat {
input <- suppressWarnings(system(intern=T,sprintf(
'read -r -t %.2f; rc=$?; echo "$REPLY"; exit $rc;',
left
))); ## suppress warnings on non-zero return codes
left <- difftime(end,Sys.time(),units='secs');
cat(sprintf('got input: \"%s\" [%d] with %.2fs left\n',
input,
if ('status'%in%names(attributes(input))) attr(input,'status') else 0L,
left
));
if (left<=0) break;
};
## asdf
## got input: "asdf" [0] with 9.04s left
## xcv
## got input: "xcv" [0] with 8.15s left
## a
## got input: "a" [0] with 6.89s left
## b
## got input: "b" [0] with 6.68s left
## c
## got input: "c" [0] with 6.44s left
##
## got input: "" [0] with 5.88s left
##
## got input: "" [1] with 4.59s left
## got input: "" [1] with 3.70s left
##
## got input: "" [0] with 0.86s left
##
## got input: "" [0] with 0.15s left
## got input: "" [142] with -0.03s left
The sample output I've shown above was me playing around during the input window. I mostly typed some random lines and pressed enter to submit them, giving a return code of 0. The two lines of output that show a return code of 1 were me pressing ^d, which causes read
to return 1 immediately, leaving whatever input that was in the buffer in $REPLY
(nothing, in those two cases). The final line of output was read
terminating immediately upon hitting the timeout, which I believe is the functionality you're looking for. You can use the return code of 142 to distinguish the timeout event from other input events. I'm not completely certain that the return code of 142 is consistent and reliable on all Unix systems, but there's also another way to detect the timeout event: check the current time against end
(i.e. the left
calculation), as I do in the code. Although I suppose that approach introduces a race condition between a possible last-moment submission and the time check in Rland, but you probably don't need that level of design criticality.