3
$\begingroup$

I'm seeing some unexpected (to me) behavior from SelectionEvaluate. I've got a notebook with two sections. In section 2, I've got code that clears the notebook's context, selects and executes section 1 (using NotebookLocate with a cell tag on the section's header, SelectionMove to grab the entire cell group, and SelectionEvaluate to execute the whole group). Successive cells in section 2 then do things on the assumption that whatever code is in section 1 have in fact been evaluated.

What I'm finding is that when I select my entire section 2 and evaluate it, section 1 does in fact get correctly selected and executed, but the effects of that code on the notebook's context do not take effect until after the remainder of section 2 have been evaluated. In other words, it seems that cells coming after the SelectionEvaluate function take effect before the cells being evaluated by SelectionEvaluate.

BTW, I've set my Mathematica preferences to give each notebook a unique context. I don't think this should matter, but I mention it just in case.

Here's a link to a proof-of-concept notebook:

(Because it relies on some notebook properties like cell tags and groups, I didn't know how to put the demo code directly into this post.)

So, my questions are: (1) Is this expected behavior in some way I'm not understanding, or is it a bug? and (2) Is there any way to get the effect I'm looking for, which is to programmatically force execution of a chunk of code elsewhere in the notebook and then do subsequent actions that depend on that code having been evaluated?

(For context, I'm trying to engineer a training notebook that lets students take a shot at writing some Mathematica code in one area of the notebook, and then execute code built into the notebook that tests out their code and offers feedback.)

$\endgroup$
  • 1
    $\begingroup$ I think this is what happens: When you are "evaluating" a cell in the front end it really just queues that cell for evaluation. When you evaluate section 2, you first queue all cells there for evaluation. The SelectionEvaluate queues some more cells, from section 1. But at this point the queue is not empty yet, it still contains cells from section 2, to be evaluated later. The SelectionEvaluate function inserts section 1 cells only at the end of the queue, after the section 2 cells. $\endgroup$ – Szabolcs Jul 10 '15 at 19:10
  • $\begingroup$ @Szabolcs, that makes perfect senses. So now I'm wondering whether Mathematica has any other infrastructure that would let me get the effect I want. I can't seem to find much documentation on working with the evaluation queue. I suppose I might be able to use SelectionEvaluate twice, once for the Section 1 bit and again for everything else I'll need to execute after it, but I'm hoping to eventually move most of the Section 2 code off into a package, and have a very minimal "Test it!" button that invokes it. I don't think I could use the same named-cell trick very easily for that. $\endgroup$ – ibeatty Jul 10 '15 at 19:19
  • $\begingroup$ Instead of asking the front end to send those cells to the evaluation queue, you can just read the cell expression directly and let it evaluate. I'm not experiences enough with notebook programming to know how to do that off-hand. Something with NotebookRead and ToExpression, probably. $\endgroup$ – Szabolcs Jul 10 '15 at 19:24
  • 2
    $\begingroup$ You could replace the SelectionEvaluate with this function: ev[nb_] := Module[{r}, r = NotebookRead[nb]; ToExpression /@ Cases[r, Cell[boxes_, "Input", ___] :> boxes, {0, Infinity}] ]. It will evaluate the contents of those cells. But: 1. This is very likely not the right way, and might break in some cases. I'm not experienced with notebook programming. 2. While it evaluates the contents of those cells, it does not link that content to the cells themselves. You won't get new In/Out labels and you won't get associated output cells. $\endgroup$ – Szabolcs Jul 10 '15 at 19:30
  • $\begingroup$ I tried NotebookRead and ToExpression, and ran into a problem with the fact that the chunk of notebook content was "not a string or box". I suspect that's what your Cases bit is for. I'll mess around with your latest suggestion, while hoping a notebook-programming wizard happens by. Thanks! $\endgroup$ – ibeatty Jul 10 '15 at 19:45
3
$\begingroup$

In the interest of showcasing a functional answer to an interesting question, I take the liberty of turning @Szabolcs comments above to an answer, since the OP has confirmed that this approach has solved his problem.

Szabolcs pointed out that when one is "evaluating" a cell in the front end it really just queues that cell for evaluation. When one evaluates section 2 in the OP's question, one first queues all cells there for evaluation. The SelectionEvaluate queues some more cells, from section 1. But at this point the queue is not empty yet, it still contains cells from section 2, to be evaluated later. The SelectionEvaluate function inserts section 1 cells only at the end of the queue, after the section 2 cells.

As a workaround, the following function was proposed as a substitute to SelectionEvaluate:

ev[nb_] := Module[
  {r},
  r = NotebookRead[nb];
  ToExpression /@ Cases[r, Cell[boxes_, "Input", ___] :> boxes, {0, Infinity}]
 ]

The function will evaluate the contents of those cells. A few caveats are in order:

  1. This might break in some cases!
  2. While it evaluates the contents of those cells, it does not link that content to the cells themselves. You won't get new In / Out labels and you won't get associated output cells.
$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.