I'm trying to write some testing programs and I have an idea to run a procedure, and have the procedure call internal functions and procedures in the calling program. As I understand it, you can use RUN x IN y where y is any procedure in the calling stack.
However, I can't seem to find any way of getting the handle for the calling procedure. There doesn't seem to be a means of moving up the call stack.
The only way I can think of doing it is by passing the calling procedure's THIS-PROCEDURE handle as a parameter. It would work, but seems rather inelegant.
You are almost there. Take a look at the example below utilizing the THIS-PROCEDURE:INSTANTIATING-PROCEDURE handle.
INSTANTIATING-PROCEDURE attribute
Returns the handle to the procedure in which an object was instantiated.
parentProgram.p
RUN childProgram.p.
PROCEDURE hello:
DEFINE INPUT PARAMETER pcMessage AS CHARACTER NO-UNDO.
MESSAGE "Child says:" pcMessage VIEW-AS ALERT-BOX INFORMATION.
END.
childProgram.p
MESSAGE "Calling parent. Anybody home?" VIEW-AS ALERT-BOX.
IF VALID-HANDLE(THIS-PROCEDURE:INSTANTIATING-PROCEDURE) THEN
RUN hello IN THIS-PROCEDURE:INSTANTIATING-PROCEDURE (INPUT "I am your child") NO-ERROR.
IF ERROR-STATUS:ERROR THEN DO:
MESSAGE "That didn't work" VIEW-AS ALERT-BOX ERROR.
END.
To get the handle of the calling procedure, use the SOURCE-PROCEDURE handle. It has the handle for the last procedure call. If another procedure is run, then it will change to match that procedure instance's handle, so if you need your program to remember who called it, your code'll need to store that value immediately when it's called.
Related
I think the correct description for what I'm trying to do is be able to pass an expression or function/handler into another handler as a parameter/argument. Some code to be evaluated inside the receiving handler. Similar to Javascript callbacks, I think.
For example, something like this:
on waitFor(theConditionExpression)
timeout_start(5) -- start a 5 second timer
repeat until (theConditionExpression or timeout_isExpired())
delay 0.1
end repeat
return theConditionExpression
end waitFor
theConditionExpression should be some expression or function that evaluates to a boolean result.
not really relevant to the question, but just FYI, timeout_start(…) and timeout_isExpired() are two simple handlers I've written that do exactly what they say. (…start() doesn't return anything, …isExpired() returns a boolean).
Of course, typically if I pass in some boolean expression, it will evaluate that expression once, at the time I pass it in. But I want it to evaluate it every time it's referenced in the code inside the handler.
Some languages (not sure about AS) have some kind of eval() function that you can pass it some code as a string and it will execute that string as code. Theoretically that could solve this, but: (a) I don't know if AS has anything like that, but even if it does, (b) it's not desired for various reasons (performance, injection risks, etc.)
So I'm thinking something more like eg. JavaScript's ability to pass in a function (named or anonymous) as function parameter/argument that can be re-evaluated every iteration in a loop, etc. (eg. like the compareFn argument in JS's Array.sort(compareFn)).
Can AS do anything like this, and if so how?
Thanks!
I'm going to suggest (pro forma) that an AppleScript application with an on idle handler is generally a better solution for wait conditions than a repeat/delay loop. It's more efficient for the system, and doesn't freeze up the script. But that would involve reconceptualizing your script, and I'm not certain it would work in this case, given the way you formed the problem.
There's an old but good site called AppleScript Power Handlers that shows a bunch of nifty-neato tricks for sophisticated use of AppleScript handlers: passing handlers as values or parameters; creating Script Objects within handlers; making closures and constructors. I'm pretty sure the answer to your request is in there. aLikely you'll want to set up a bunch of handlers that serve as condition expressions, then pass them as parameters to the evaluating handler. Or maybe you'll want to set up a script object containing the condition handlers and call it as needed?
At any rate, see what you can do with it, and ask more specific questions if you run into problems.
How should we insert a page break via the code itself in maple program ?
Not insert page break? of menu
As I want to insert the page break as the code runs and with other outputs. In maple code.
Let's suppose that you have the following PutGraph procedure defined and available.
(You could define it in the Startup Code region of your Document/Worksheet. Or you could define it in your Maple initialization file. Or you could define it and store it in a .mla Library archive within your libname path. Or you could simply define it at the start of any Document/Worksheet in which you want to utilize it. I leave that choice to you.)
Then you can utilize this Putgraph procedure instead of DrawGraph, with the extra effect that it inserts a pagebreak right before the inlined plot of the Graph.
It also accepts a 2D or 3D plot as its argument. And if its argument is a Graph then you can also pass additional arguments that would normally be accepted by DrawGraph.
The restriction is that you can only effectively use one such call to this implementation of PutGraph per Execution Group or Document Block. If you want it done otherwise then you will have to show me explicitly how you need to call it once for multiple insertions. If you want specific behaviour then you will have to describe it adequately.
Here's the procedure, as 1D Maple notation code:
PutGraph:=proc(GG::{Graph,specfunc({PLOT,PLOT3D})})
local T,xml;
uses DocumentTools,DocumentTools:-Layout,GraphTheory;
if GG::Graph then
T:=DrawGraph(GG,_rest);
else T:=GG; end if;
xml:=Worksheet(Group(Input(Textfield(InlinePlot(T)))));
InsertContent(subsindets(xml,'specfunc(anything,_XML_Input)',
u->':-_XML_Input'(':-_XML_Pagebreak'(),
op(u))));
return NULL;
end proc:
Here's some example of calling it. (I'll mention again: it needs to be called at most once per Execution Group or Document Block. If you put it more than once in the same Execution Group/Block then only the last instance will work as intended.)
G1 := GraphTheory:-RandomGraphs:-RandomGraph(6,degree=3):
Now, in its own Execution Group/Document Block,
PutGraph(G1);
You can have something else appear before the next page break, or several things like text, other input/output, etc.
somethingelse;
And now, in its own Execution Group/Block,
PutGraph(G1,showlabels);
And so on,
somethingelse;
PutGraph(G1,showweights);
somethingelse;
P := GraphTheory:-DrawGraph(G1):
PutGraph(P);
somethingelse;
I am running a script which calls a function, and if a certain condition is met, inside the function, I want the whole thing just to terminate (and by that I do not mean I want to close matlab using exit). Is that possible? I know I can use return or break to return to the script, however I want the script to stop as well if this condition is met.
The only function I know of that does this is error. This throws an exception, and, if no exception handlers with try and catch are installed in the calling script, will terminate and return to command prompt. Which is what you want, as far as I understand. It prints an error message though. This could be suppressed if you guard all code in the top-level script with a try catch handler. However this will have to be specific to the one error and it makes debugging ("stop-on-error") much more difficult.
The thing is that the only use case I see for this behavior (termination of whole program on certain event) is when a non recoverable error occurs, and in that case printing an error message is indeed appropriate.
In case the script is successful termination of the whole program is not really the right way. All functions should return to give the upper layers of the code to perform some clean-up action, like saving the output data or so.
I have the following question: How do I tell Matlab that it should not wait for the results of a function? Is there a way other than threads?
My problem: I have a function A that is called by a Timer every few seconds. If a specific event is met, another function B is called inside function A. Function B opens a Batch File.
I want function A to go on without waiting for function B to end. Is there a way to easily do it?
I'm sorry if this question was already asked, but I couldn't find a satisfying answer. Please also excuse my bad english.
I would like to thank everyone who answers for their help.
In your function B, just call the batch file with a & at the end of the line.
For example:
!mybatch.bat &
This will run the file mybatch.bat in background mode and will return execution to Matlab immediately after the call.
or if you prefer the complete form:
[status, result] = system('mybatch.bat &')
But in this case it is a bit useless, since the system call mybatch in the background, the result variable is always empty and status is always 0 (whether a file mybatch.bat was found and executed or not)
edit: That is the quick trick in case it is only the batch file execution which is slowing down your program.
If you have more matlab instructions in function B and you really need function A to go on without waiting, you will have to set up a listener object with function B as a callback. Then in your function A, trigger the event (which will activate the listener and call function B).
Is there a way to use the variable of a procedure(.p file) in its internal procedure (.p file)?
The actual problem seems to be: "I have many procedures that call a common procedure. In one special case I need to have the common procedure set a special value in the calling procedure but I cannot modify all of the others."
The simple, but architecturally repugnant, solution is to use a global shared variable.
Many people will tell you that this is a bad coding technique. They are right. But you aren't asking for advice on best practices.
Simply create such a variable in both the caller and the callee. Procedures that don't need it won't miss it.
One of your "normal" programs:
/* p1.p */
message "p1, I'm normal.".
run common.p.
Your "special" program:
/* p2.p */
define new global shared variable special as character no-undo.
message "p2, I'm special!".
run common.p.
message "special = " special.
The common program:
/* common.p */
define new global shared variable special as character no-undo.
message "common stuff...".
if program-name(2) = "p2.p" then special = "special value".
return.
You can define a NEW GLOBAL SHARED variable as many times as you like and you only get one copy of it. The "new" doesn't overwrite any existing variables (if you leave out GLOBAL it behaves differently).
You didn't ask for it and maybe you don't need it but the program-name(2) check peeks at the call stack to see if common.p was called by p2.p.
There are other, more complicated ways to do this but they all boil down to the same problem -- you're creating the basis for some very ugly coupling between your "special" program and the now no longer generic "common" program.
Like this?
define variable i as integer no-undo. /* a variable in the main procedure */
procedure internalProcedure:
message "Internal Procedure (a):" i.
i = 13.
message "Internal Procedure (b):" i.
end.
/* main block
*/
i = 1.
message "Main procedure (a):" i.
run internalProcedure.
message "Main procedure (b):" i.
The more difficult question might be: "Is there anyway to prevent a globally scoped variable from being used in an internal procedure"?
Is this what you mean?
Here we have a common .p file called ShowTime.p that the other .p files will run. You want it to behave in a certain way for one of the .p files, and a different way for the others. For this reason we define a parameter. In this example it is a LOGICAL parameter, so the value can be YES or NO. As you can see, the ShowTime procedure will display the TIME if the lShowDate parameter is NO, and it will display the DATE if the lShowDate parameter is YES.
/* ShowTime.p */
DEFINE INPUT PARAMETER lShowDate AS LOGICAL.
IF lShowDate = NO THEN
MESSAGE STRING(TIME,"HH:MM:SS").
ELSE
MESSAGE STRING(TODAY,"9999/99/99").
Here is one of your five unrelated .p files. It runs the ShowTime.p procedure, passing it a parameter of NO, so ShowTime will show the TIME.
/* p1.p */
MESSAGE "P File 1".
RUN ShowTime(INPUT NO) NO-ERROR.
Another one, why not?
/* p2.p */
MESSAGE "P File 2".
RUN ShowTime(INPUT NO) NO-ERROR.
This .p file is running ShowTime with the lShowDate parameter as YES, which means that the ShowTime procedure will behave differently for this one, by showing the DATE instead of the TIME.
/* p5.p */
MESSAGE "P File 5".
RUN ShowTime(INPUT YES) NO-ERROR.
So basically what you have is one procedure, ShowTime, that can do one of two things. An unrelated procedure that runs this one procedure can tell it what it wants, by passing it a parameter.
To put it in terms of your question: there are five procedures (p1.p, p2.p ... p5.p) which are not related to each other, sharing a single procedure (ShowTime.p file). The shared procedure is modified with a parameter, so that it can have its effect on only one calling procedure (the one with the YES parameter) and not the other four (the ones with the NO parameter).
But if you cannot change the way five unrelated procedures run the shared procedure, if you can only change the shared procedure, you have to make your decision on something other than a parameter. What about this:
/* DoThing.p */
IF SOURCE-PROCEDURE:NAME = "p5.p" THEN
MESSAGE "Do one thing".
ELSE
MESSAGE "Do something else".
This will do one thing for one of the five procedures, and something else for the other four.