
KDP_book
.pdf
Mathematica: Functional and procedural programming
the procedure whose call MessagesOut[x] returns the message generated by a procedure or function G whose call given in the format x="G[z]". Whereas the procedure call MessagesOut[x,y] with the 2nd optional y argument – an indefinite symbol – thru it returns the messages in the format {MessageName, "Message"}. In the absence of any messages the empty list, i.e. {}, is returned. An unsuccessful procedure call returns $Failed.
All messages assigned to a symbol x (Function,Module,Block) along with its usage (if any) are returned by calling Messages[x]. Meantime, the returned result is not very convenient for further processing in program mode. Therefore, we have created some useful tools for this purpose [1-16]. In particular, the procedure call Usage[x] in the string format returns the usage ascribed to a symbol x (built–in or user tool), its source code and application examples are represented below, namely:
In[3107]:= Usage[x_Symbol] := Module[{a, b = Context[x],
c = ToString[x]}, ToExpression["?" <> c]; a = ToExpression[StringJoin["Messages[", b, c, "]"]]; If[a == {}, {}, b = StringSplit[ToString[a], "HoldPattern"]; b = Select[b, ! StringFreeQ[#, c <> "::usage"] &]; b = Flatten[StringSplit[b, " :> "]][[2]];
If[SystemQ[x], StringDrop[b, –3], StringTrim[b, "}"]]]]
In[3108]:= Usage[ProcQ]
Out[3108]= "The call ProcQ[x] returns True if x is a procedure and False otherwise."
In[3109]:= Usage[While]
Out[3109]= "While[test, body] evaluates test, then body, repetitively, until test first fails to give True."
In[3110]:= Usage[Agn]
Out[3110]= {}
In this context, a simple procedure can be noted whose call ToFile[x, 1] in the current session opens a file x to read, to that the system along with the screen writes all messages that occur. Whereas call ToFile[x, 1, z] with the third optional argument z (an arbitrary expression) closes the x file, allowing in future to do analysis of the received messages.
91

V.Z. Aladjev, M.L. Shishakov, V.A. Vaganov
In[42]:= ToFile[x_String, y_ /; MemberQ[{1, 2}, y], z___] := Module[{a, b}, If[{z} == {}, b = OpenWrite[x]; Write["#", b];
Close["#"]; If[y == 1, $Messages = Append[$Messages, b], $Output = Append[$Output, b]];, $Output = ReplaceAll[$Output, Read["#"] –> Nothing]; Close["#"]; $Messages = ReplaceAll[$Messages, Read["#"] –> Nothing]; Close[x]; Quiet[DeleteFile[Close["#"]]; ]]]
In[43]:= MessageFile["C:\\temp\\Error.txt", 1]
====================================
In[145]:= MessageFile["C:\\temp\\ Error.txt", 1, 7]
Out[145]= "C:\\temp\\Error.txt"
Moreover, the procedure calls with the second argument 2 are performed similarly except that all messages output by the Print function are written to the w file until appear a procedure call with the third argument z (an arbitrary expression).
Right there appropriate to note the Print1 procedure which extends the capabilities of the built–in Print function, allowing additionally to output to the file the result of a call of the Print function. The call Print1[x, y, z] prints z as output additionally saving the z expression in the x file if y = 1 or closing the x file if y=2. A simple function IsOpenFile can be used in the procedure implementation, whose call IsOpenFile[x] returns True if a file x exists and is open for writing, and False otherwise.
In[2228]:= Print1[x_, y_ /; MemberQ[{1, 2}, y], z___] := Module[{a}, If[FileExistsQ[x], If[y == 1, Print[z], Close[x];
$Output = ReplaceAll[$Output, Read["#"] –> Nothing]; DeleteFile[Close["#"]]], a = OpenWrite[x]; Write["#", a]; Close["#"]; $Output = Append[$Output, a]; Print[z]]]
In[2229]:= IsOpenFile[x_ /; StringQ[x]] := ! StringFreeQ[
StringReplace[ToString[InputForm[Streams[]]], "\\\\" –> "\\"], StringJoin["OutputStream[\"", x, "\","]]
In[2230]:= IsOpenFile["c:/print2.txt"]
Out[2230]= False
The following procedure generalizes the previous function for testing of openness of files on reading and writing. Namely,
92

Mathematica: Functional and procedural programming
the procedure call IsInOutFile[x] returns False if a file x is closed or missing, whereas the list {True, In, Out}, {True, In}, {True, Out} otherwise, where In – the file for reading and Out – for writing.
In[3242]:= IsInOutFile[x_ /; StringQ[x]] := Module[{a = StringReplace[ToString[InputForm[Streams[]]],"\\\\"->"\\"]},
If[! StringFreeQ[a, StringJoin["OutputStream[\"", x, "\","]] && ! StringFreeQ[a, StringJoin["InputStream[\"", x, "\","]], {True, In, Out},
If[! StringFreeQ[a, StringJoin["OutputStream[\"", x, "\","]], {True, Out},
If[! StringFreeQ[a, StringJoin["InputStream[\"", x, "\","]], {True, In}, False]]]]
In[3243]:= IsInOutFile["C:\\temp/Cinema_2020.doc"]
Out[3243]= {True, In}
However, note that (as shown in [1-15]) the coding of the file path is symbolic–dependent, for example, path "с:/tmp/kino" is not identical to "C:/Tmp\\Kino" i.e. the system will recognize them as different paths. This should be taken into account when programming file access tools. It is useful to use standardized files path view as an easy approach [1-15].
Therefore, the output of messages, including Print function messages, can be ascribed fully to the return of results allowing further program processing along with their rendering on the display. Naturally, all procedure calls that complete by $Failed or $Aborted are processed programmatically. To recognize this return type it is possible to use the system function whose call FailureQ[x] returns True if x is equal to $Failed or $Aborted.
For more successful and flexible program processing of the main possible erroneous and exception situations that may occur in the procedure execution (Block, Module), it is recommended that the messages processing about them be programmed in the procedure, making it more reliable and steady against mistakes.
It should be noted that our tools for processing procedures and functions are oriented to the absence of attributes for them. Otherwise, the tools should be adapted to existence of attributes for the means being processed, which is easy to do.
93

V.Z. Aladjev, M.L. Shishakov, V.A. Vaganov
2.6. Tools for testing of procedures and functions
Having defined procedures of two types (Module and Block) and functions, including pure functions, at the same time we have no standard tools for identification of objects of the given types. In this regard we created a series of means that allow to identify objects of the specified types. In the present section non–standard means for testing of procedural and functional objects are considered. In addition, it should be noted that the Mathematica – a rather closed system in contradistinction, for example, to its main competitor – the Maple system in which it is admissible to browse of source codes of its software which is located both in the main and in the auxiliary libraries. Whereas the Mathematica has no similar opportunity. In this connection the means presented below concerns only to the user functions and procedures loaded into the current session from a package
(m– or mx–file), or a document (nb–file; also may contain a package) and activated in it.
For testing of objects onto procedural type we proposed a number of means among which it is possible to mark out such as ProcQ, ProcQ1, ProcQ2. The procedure call ProcQ[x] provides testing of an object x be as a procedural object {"Module","Block"}, returning accordingly True or False; while the ProcQ1 procedure is a useful enough modification of the ProcQ procedure, its call ProcQ1[x,t] returns True, if x – an object of the type Block, Module or DynamicModule, and "Others" or False otherwise; at that, the type of x object is returned through the factual t argument – an indefinite variable. The source codes of the above procedures, their description along with the most typical examples of their application are presented in our books and in our MathToolBox package [12-16]. A number of receptions used at their creation can be useful enough in the practical programming. The ProcQ procedure is an quite fast, processes attributes and options, but has certain restrictions, first of all, in a case of multiple objects of the same name. The fragment below represents source code the ProcQ procedure along with typical examples of its use.
94

Mathematica: Functional and procedural programming
In[7]:= ProcQ[x_] := Module[{a, atr = Quiet[Attributes[x]], b,
c, d, h}, If[! SymbolQ[x], False, If[SystemQ[x], False,
If[UnevaluatedQ[Definition2, x], False, If[ListQ[atr] && atr != {}, ClearAllAttributes[x]]; a = StringReplace[ToString[InputForm[Definition[x]]], Context[x] <> ToString[x] <> "`" –> ""];
Quiet[b = StringTake[a, {1, First[First[StringPosition[a, {" := Block[{", " :=Block[{"}] – 1]]}]; c = StringTake[a, {1, First[First[StringPosition[a,
{" := Module[{", " :=Module[{"}] – 1]]}]; d = StringTake[a, {1, First[First[StringPosition[a, {" := DynamicModule[{", " :=DynamicModule[{"}] – 1]]}]];
If[b === ToString[HeadPF[x]], SetAttributes[x, atr]; True, If[c === ToString[HeadPF[x]], SetAttributes[x, atr]; True, If[d === ToString[HeadPF[x]], SetAttributes[x, atr]; True, SetAttributes[x, atr]; False]]]]]]]
In[8]:= Map[ProcQ, {Sin, a + b, ProcQ1, ProcQ, 77, ToString1}]
Out[8]= {False, False, True, True, False, True}
Generally, supposing existence of procedures of the above two types (Module and Block) in the Mathematica, for ensuring of the reliability it is recommended to use the procedures of the Module type. From examples represented in [8,10-15] follows, if local variables of a modular object aren't crossed with domain of values of the variables of the same name which are external in relation to it, the absolutely other picture takes place in a case with the local variables of a block object, namely: if initial values or values are ascribed to all local variables of such object in its body, they save effect in the object body; those variables of an object to which such values weren't ascribed accept values of the variables of the same name which are the external in relation to the block object. So, at the listed conditions the modular and block objects relative to the local variables (and in general as procedural objects) can quite be considered as equivalent. Naturally, the told remains in force also for block objects with empty lists of the local variables. The specified reasons were used as a basis for the programming of the RealProcQ procedure presented by the following fragment.
95

V.Z. Aladjev, M.L. Shishakov, V.A. Vaganov
In[2107]:= RealProcQ[x_ /; BlockModQ[x]] := Module[{a, b, c},
If[ModuleQ[x], True, a = StringJoin[Insert[Reverse[Headings[x]], " := ", 2]] <> "["; b = StringReplace[PureDefinition[x], a –> "", 1]; Do[c = StringTake[b, k]; If[SyntaxQ[c], Return[c], 7], {k, StringLength[b]}]; If[c == "{}", {}, c = StrToList[StringTake[c, {2, –2}]]];
c = Map[StringSplit[#, " = "] &, c]; AllTrue[Map[If[Length[#] > 1, True, False] &, c], TrueQ]]]
In[2108]:= B[x_] := Block[{a = 90, b = 50, c = {72, 77}, d = 42}, x*(a*b*c*d)]; RealProcQ[B]
Out[2108]= True
In[2109]:= M[x_] := Block[{a = 90, b = 49, c, h}, h = 77; x*h]; In[2110]:= RealProcQ[M]
Out[2110]= False
Experience of use of the RealProcQ procedure confirmed its efficiency at testing objects of the type Block that are considered as real procedures. At that, we will understand an object of the type {Module, Block} as a real procedure that in the Mathematica software is functionally equivalent to a Module, i.e. is a certain procedure in its classical understanding. The call RealProcQ[x] returns True if a symbol x defines a Module, or a Block which is equivalent to a Module, and False otherwise. In addition, it is supposed that a certain block is equivalent to a module if it has no the local variables or all its local variables have initial values or some local variables have initial values, while others obtain values by means of the operator "=" in the block body. From all our means solving the testing problem for procedural objects, the RealProcQ procedure with the greatest possible reliability identifies the set procedure in its classical understanding; thus, the real procedure can be of the type {Module, Block}.
In the context of providing of an object of the type {Module, Block} to be a real procedure recognized by the testing RealProcQ procedure, the ToRealProc procedure is of certain interest whose call ToRealProc[x, y] returns nothing, converting a module or a block x into the object of the same type and of the same name with the empty list of local variables that are placed in the object
96

Mathematica: Functional and procedural programming
body at once behind the empty list of the local variables. At the same time all local variables of an object x are replaced with the symbols, unique in the current session, which are generated by means of the Unique function. In addition, along with a certain specified purpose the returned procedure provides a possibility of more effective debugging in interactive mode of procedure, allowing to do dynamic control of change of values of its local variables in the procedure run on concrete actual arguments. In addition to our means for testing of procedural objects, we can note a simple procedure, whose call UprocQ[x] returns False if an object x isn’t a procedure or is an object of the same name [8].
Meanwhile, since the structural definitions of modules and blocks are identical, the ToRealProc1 procedure that is based on this circumstance has a simple implementation. The following fragment represents source code of the ToRealProc1 procedure with typical examples of its application.
In[2229]:= B[x_] := Block[{a = 7, b, c = 8, d}, x*a*b*c*d]; In[2230]:= SetAttributes[B, {Protected, Listable, Orderless}]
In[2231]:= ToRealProc1[x_] := Module[{a = Attributes[x]},
If[ModuleQ[x], x, If[BlockQ[x], If[FreeQ[a, Protected], 7, Unprotect[x]]; ToExpression[StringReplace[Definition2[x][[1]], ":= Block[{" –> ":= Module[{", 1]]; SetAttributes[x, a]; x, $Failed]]]
In[2232]:= ToRealProc1[B]
Out[2232]= B
In[2233]:= Definition[B]
Out[2233]= Attributes[B] = {Flat, Listable, Orderless, Protected}
B[x_] := Module[{a = 7, b, c = 8, d}, x*a*b*c*d]
In[2234]:= ToRealProc1[vsv]
Out[2234]= $Failed
Calling the ToRealProc1[x] procedure returns the module name that is equivalent to a module x or a block x, retaining all attributes and structure of the original object x. The scope of the module created is the current session. As it was noted above, in general case between procedures of types "Module" and "Block" exist principal distinctions that do not allow a priori to consider
97

V.Z. Aladjev, M.L. Shishakov, V.A. Vaganov
a block structure as a procedure in its above meaning. Therefore the type of a procedure should be chosen rather circumspectly, giving preference to the procedures of the "Module" type [8,12].
As noted repeatedly, Mathematica does not distinguish the objects by names, but by headers, allowing objects of the same name with different headers and even types (modules, functions, blocks) to exist in the current session. First of all, it is useful to define the function whose call TypeBFM[x] returns the type of an object x ("Block", "Function", "Module"), or $Failed otherwise.
In[7]:= B[x_] := Block[{a=7, b}, x*a*b]; B[x_, y_] := Module[{a, b}, x/y]; B[x_, y_, z_] := x^2 + y^2 + z^2; G[x_] := Module[{}, x*a]
In[8]:= TypeBFM[x_] := If[ModuleQ[x], "Module",
If[FunctionQ[x], "Function", If[BlockQ[x], "Block", $Failed]]]
In[9]:= TypeBFM[G]
Out[9]= "Module"
In[10]:= TypeQ[x_, y___] := Module[{a, b, c = {}, d = {}, t},
If[! SameQ[Head[x], Symbol], $Failed, a = Definition2[x][[1 ;; –2]]; Map[{b = ToString[Unique[]], t = StringReplace[#, ToString[x] <> "[" –> b <> "[", 1],
AppendTo[c, b], AppendTo[d, t]} &, a]; Map[ToExpression, d];
Map[ToExpression, c]; If[{y} != {} && SymbolQ[y], b = Map[Headings, c]; y = Map[{#[[1]], StringReplace[#[[2]],
"$" ~~ Shortest[__] ~~ "[" –> ToString[x] <> "["]} &, b]; y = If[Length[y] == 1, Flatten[y], y], 7]; a = Map[TypeBFM, c]; If[Length[a] == 1, a[[1]], a]]]
In[11]:= TypeQ[B]
Out[11]= {"Block", "Module", "Function"} In[12]:= TypeQ[B, gs]
Out[12]= {"Block", "Module", "Function"} In[13]:= gs
Out[13]= {{"Block", "B[x_]"}, {"Module", "B[x_, y_]"}, {"Function", "B[x_, y_, z_]"}}
In[14]:= {TypeQ[G, gv], gv}
Out[14]= {"Module", {"Module", "G[x_]"}}
Whereas the TypeQ procedure is primarily intended to test procedural and functional objects having multiple definitions of
98

Mathematica: Functional and procedural programming
the same name. Calling TypeQ[w] returns $Failed if w is not a symbol, whereas on w objects which have multiple definitions of the same name, the list is returned containing the subobject types that make up the w object in the form {"Block", "Function", "Module"}. While the calling TypeQ[w, j] with the 2nd optional argument j – an undefined symbol – through j additionally returns the list in which for each sub-object of object w corresponds the 2–element list whose first element defines its type, whereas the 2nd element defines its header in the string format. The previous fragment represents the source code of the TypeQ procedure and typical examples of its application.
Meanwhile, procedures, being generally objects other than functions, allow relatively simply conversion of functions into procedures, whereas under certain assumptions it is possible to convert procedures into functions too. Particularly, on example of the modular objects consider a principal scheme for this type of conversion algorithm that is based on the list structure.
Heading := {Save2[f, {locals, args}], Map[Clear, {locals, args}], {locals}, Procedure body, {Get[f], DeleteFile[f]}}[[–2]]
An algorithm of the ProcToFunc procedure, the original text of which together with examples of applications is represented below, is based on the above scheme. The list view of a module is generated in the format containing the following components:
Heading – the module or block heading;
Save2[f, {locals, args}] – saving of values of the joint list of local variable names without initial values and arguments without testing functions in a file f;
Map[Clear, {locals, args}] – clearing of the above arguments and local variables in the current session;
{locals} – the list of local variables with initial values, if any; Procedure body – body of module or block;
{Get[f], DeleteFile[f]} – loading the f file to the current session, which contains the values of local variable names and arguments that were previously stored in it.
With in mind the told, the procedure call ProcToFunc[j] does not return anything, converting a module or block j into a list
99

V.Z. Aladjev, M.L. Shishakov, V.A. Vaganov
format function of the same name with preserving of attributes of the module or block. The following fragment represents the source code of the procedure with examples of its application.
In[42]:= ProcToFunc[j_ /; ProcQ[j]] := Module[{a, b, c, d, p}, a = Map[ToString[#] <> "@" &, Args[j]]; a = Map[StringReplace[#, "_" ~~ ___ ~~ "@" –> ""] &, a];
If[ModuleQ[j], b = Map[#[[1]] &, Locals4[j]]; c = Join[a, b]; d = Definition2[j][[1]]; d = StringReplace[d, "Module[" –> "{Save2[\"##\"," <> ToString1[c] <> "], Map[Clear, " <> ToString1[c] <> "],", 1]; d = StringReplacePart[d, "", {–1, –1}]; d = d <> ", {Get[\"##\"], DeleteFile[\"##\"]}}[[–2]]";
If[FreeQ[Attributes[j], Protected], ToExpression[d], Unprotect[j]; ToExpression[d]; SetAttributes[j, Protected]], b = Map[#[[1]] &, Locals4[j]]; c = Join[a, b]; p = Map[If[Length[#] != 1, #[[1]], Nothing] &, Locals4[j]]; p = Join[a, p]; d = Definition2[j][[1]]; d = StringReplace[d, "Block[" –>
"{Save2[\"##\"," <> ToString1[c] <> "], Map[Clear, " <> ToString1[p] <> "],", 1]; d = StringReplacePart[d, "", {–1, –1}]; d = d <> ", {Get[\"##\"], DeleteFile[\"##\"]}}[[–2]]";
If[FreeQ[Attributes[j], Protected], ToExpression[d], Unprotect[j]; ToExpression[d]; SetAttributes[j, Protected]]]]
In[43]:= {a, b, c, x, y} = {1, 2, 3, 4, 5};
In[44]:= M[x_ /; IntegerQ[x], y_] := Module[{a = 5, b = 7, c}, a = a*x + b*y; c = a*b/(x + y); If[x*y > 100, a, c]]
In[45]:= SetAttributes[M, {Protected, Listable, Flat}] In[46]:= ProcToFunc[M]
In[47]:= Definition[M]
Out[47]= Attributes[M] = {Flat, Listable, Protected}
M[x_/; IntegerQ[x], y_] := {Save2["##", {"x","y","a","b","c"}],
Clear /@ {"x", "y", "a", "b", "c"}, {a = 5, b = 7, c}, a = a*x + b*y; c = (a*b)/(x + y); If[x*y > 100, a, c], {<< "##", DeleteFile["##"]}}[[–2]]
In[48]:= M[77, 72]
Out[48]= 889
100