Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Terminating an apply-based function early (similar to break?)

Tags:

r

I am searching for a way to terminate an apply function early on some condition. Using a for loop, something like:

FDP_HCFA = function(FaultMatrix, TestCosts, GenerateNeighbors, RandomSeed) {    
  set.seed(RandomSeed)

  ## number of tests, mind the summary column
  nT = ncol(FaultMatrix) - 1
  StartingSequence = sample(1:nT)
  BestAPFD = APFD_C(StartingSequence, FaultMatrix, TestCosts)
  BestPrioritization = StartingSequence
  MakingProgress = TRUE
  NumberOfIterations = 0
  while(MakingProgress) {
    BestPrioritizationBefore = BestPrioritization
    AllCurrentNeighbors = GenerateNeighbors(BestPrioritization)

    for(CurrentNeighbor in AllCurrentNeighbors) {
      CurrentAPFD = APFD_C(CurrentNeighbor, FaultMatrix, TestCosts)

      if(CurrentAPFD > BestAPFD) {
        BestAPFD = CurrentAPFD
        BestPrioritization = CurrentNeighbor            
        break
      }
    }

    if(length(union(list(BestPrioritizationBefore),
                    list(BestPrioritization))) == 1)
      MakingProgress = FALSE

    NumberOfIterations = NumberOfIterations + 1
  }
}

I would like to rewrite this function using some derivation of apply. In particular, terminating the evaluation of the first individual with increased fitness, thereby avoiding the cost of considering the rest of the population.

like image 707
user602319 Avatar asked Feb 06 '11 21:02

user602319


2 Answers

I reckon that you don't really grasp the apply family and its purpose. Contrary to the general idea, they're not the equivalent of any for-loop. One can say that most for-loops are the equivalent of an apply, but that's another matter.

Apply does exactly as it says: it applies a function on a number of similar arguments sequentially, and returns the result. Hence, by definition you cannot break out of an apply. You're not operating in the global environment any more, so in principle you cannot keep global counters, check after each execution some condition and adapt the loop. You can access the global environment and even change variables using assign or <<-, but this is pretty dangerous.

To understand the difference, don't read apply(1:3,afunc) as for(i in 1:3) afunc(i), but as

afunc(1)
afunc(2)
afunc(3)

in one (block) statement. That reflects better what you're doing exactly. An equivalent for break in an apply simply doesn't make sense, as it is more a block of code than a loop.

like image 189
Joris Meys Avatar answered Nov 01 '22 03:11

Joris Meys


Aside from getting your sample code to work* I think this is a clear case where a loop is the right choice. Although R can apply a function to a whole vector of variables [EDIT: but you have to decide what they are before applying], in this case I'd use a while loop to avoid the cost of running unnecessary repetitions. Caveat: I know for loops have compared favorably with apply in timing tests, but I have not seen a similar test for while. Check out some of the options at http://cran.r-project.org/doc/manuals/R-lang.html#Control-structures.

while ( *statement1* ) *statement2*

like image 38
J. Win. Avatar answered Nov 01 '22 04:11

J. Win.