KDP_book
.pdf
Mathematica: Functional and procedural programming
The procedure call AdjunctionToMx[y, f] returns full path to a mx–file with a context updated by new y tools. At that, a single y symbol or their list can be as the first argument whereas their definitions and usage should be previously evaluated. Besides, if the mx–file had been already uploaded, it remains, otherwise it will be unloaded of the current session. The procedure call on a mx–file f without context returns $Failed.
In[2216]:= AdjunctionToMx[y_ /; SymbolQ[y] || ListQ[y] && DeleteDuplicates[Map[SymbolQ[#] &, Flatten[{y}]]] == {True},
f_ /; FileExistsQ[f] && FileExtension[f] == "mx"] := Module[{a = ContextInMxFile[f], b},
If[a === $Failed, $Failed, If[! FreeQ[$Packages, a], b = 77, Get[f]]; Map[Quiet[ContextToSymbol2[#, a]] &, Flatten[{y}]];
ToExpression["DumpSave[" <> ToString1[f] <> "," <> ToString1[a] <> "]"]; If[! SameQ[b, 77], RemovePackage[a], 72]; f]]
In[2217]:= G72[x_, y_] := x^2 + y^2; G72::usage = "Help on G72 function."; V77[x_, y_] := x^3 + y^3; V77::usage = "Help on V77 function."; Get["avzagn.mx"]
In[2218]:= ContextInMxFile["avzagn.mx"]
Out[2218]= "Tampere`"
In[2219]:= CNames["Tampere`"]
Out[2219]= {"ArtKr", "GSV"}
In[2220]:= AdjunctionToMx[{G72, V77}, "avzagn.mx"]
Out[2220]= "avzagn.mx"
In[2221]:= ClearAll[ArtKr, GSV, G72, V77] In[2222]:= Get["avzagn.mx"]; CNames["Tampere`"]
Out[2222]= {"ArtKr", "G72", "GSV", "V77"}
In addition to the previous procedure the procedure call CreationMx[x, y, f] creates a new mx–file f with tools defined by the y argument (a single symbol or their list) and with a context x, returning full path to the f file. At that, definitions and usage of the y symbols should be previously evaluated. Furthermore, if mx–file f already exists, it remains, but instead of it a new mx– file is created. The procedure call for a context x that exists in the $Packages variable returns $Failed [8-10,15,16].
Sometimes, it is expedient to replace a context of means of the user package uploaded into the current session. This task is
361
V.Z. Aladjev, M.L. Shishakov, V.A. Vaganov
solved by means of the RemoveContext procedure, whose call RemoveContext[j, x, y, z, h,…] returns nothing, replacing in the current session a context j ascribed to the means {x, y, z, h,…} of the user package by the "Global'" context. In the absence of the {x, y, z, h,…} means with the j context the procedure call returns $Failed. In addition, a mx–file contained the means {x, y, z, h,…} remains without change. The fragment represents source code of the procedure RemoveContext with an example of its use.
In[18]:= RemoveContext[at_ /; ContextQ[at], x__] :=
Module[{a = {}, b, c = {}, d = Map[ToString, {x}], f = "$$$", Attr}, d = Intersection[CNames[at], d]; b = Flatten[Map[PureDefinition, d]];
If[b == {}, $Failed, Attr := Map[{#, Attributes[#]} &, d];
Do[AppendTo[a, StringReplace[b[[k]], at <> d[[k]] <> "`" –> ""]], {k, 1, Length[d]}];
Write[f, a]; Close[f]; Do[AppendTo[c, at <> d[[k]]], {k, 1, Length[d]}]; c = Flatten[c]; Map[{ClearAttributes[#, Protected], Remove[#]} &, d];
Map[ToExpression, Get[f]]; DeleteFile[f]; Map[ToExpression["SetAttributes[" <> #[[1]] <> "," <> ToString[#[[2]]] <> "]"] &, Attr]; ]]
In[19]:= Get["avzagn.mx"] In[20]:= CNames["Tampere`"]
Out[20]= {"Art", "G72", "G721", "GSV", "V77"}
In[21]:= RemoveContext["Tampere`", Art, G72, G721, GSV, V77] In[22]:= Map[Context, {Art, G72, G721, GSV, V77}]
Out[22]= {"Global`", "Global`", "Global`", "Global`", "Global`"}
In connection with the context processing means discussed above, it quite is reasonably note a procedure for testing of the correctness of mx-files. Calling CorrectMxQ[f] procedure returns True if the existing a mx-file is correctly loaded into the current session by means of the call Get[f], and False otherwise. While the call CorrectMxQ[f, t] additionally through the 2nd optional t argument – an undefined variable – returns two-element list of the format {c, "Yes"} where c – a context of the f file if the f file was already loaded into the current session, {c, "No"} if f file is not loaded and the message "File f is corrupted" if f file is corrupted.
362
Mathematica: Functional and procedural programming
Following fragment represents source code of the CorrectMxQ procedure with some examples of its typical application.
CorrectMxQ[f_ /; FileExistsQ[f] && FileExtension[f] == "mx", t___] := Module[{a = ContextInMxFile[f], b, c, d}, {If[ContextQ[a],
If[FreeQ[$ContextPath, a], d = {a, "No"}, d = {a, "Yes"}]; True,
If[! SameQ[a, $Failed], d = "File " <> f <> " is corrupted"; False, b = CNames["Global`"]; DumpSave["##.mx", b]; Map[ClearAll, b]; Get[f]; c = CNames["Global`"]; If[MemberQ3[b, c], d = {a, "Yes"}; True, d = {a, "No"};
Map[ClearAll, CNames["Global`"]]; Get["##.mx"]; True]]], Quiet[DeleteFile["##.mx"]], If[{t} != {} && SymbolQ[t], t = d, Null]}[[1]]]
In[2331]:= CorrectMxQ["c:\\mathematica\\mathtoolbox.mx"]
Out[2331]= True
In[2332]:= CorrectMxQ["c:/mathematica/mathtoolbox.mx", h]
Out[2332]= True In[2333]:= h
Out[2333]= {"AladjevProcedures`", "Yes"} In[2334]:= CorrectMxQ["gsv.mx", g]
Out[2334]= False In[2335]:= g
Out[2335]= "File gsv.mx is corrupted"
In[2336]:= CorrectMxQ["vsv.mx", t]
Out[2336]= True In[2337]:= t
Out[2337]= {$Failed, "No"}
Interconnection of contexts and packages in Mathematica.
Because of the importance of the relationships of contexts and packages, the necessity of defining of contexts of symbols arises. Meanwhile, in the current session, there may be symbols of the same name with different contexts whereas the built-in function Context returns only the 1st context located in the $ContextPath list. Meantime the complete result is provided by the procedure whose call ContextDef[x] returns the list of contexts assigned to a symbol x. Following fragment represents source code of the procedure with some examples of its typical application.
363
V.Z. Aladjev, M.L. Shishakov, V.A. Vaganov
In[7]:= BeginPackage["RansIan`"]
GSV::usage = "help on GSV." Begin["`GSV`"]
GSV[x_, y_, z_] := Module[{a = 72}, x*y*z*a]
End[]
EndPackage[];
In[8]:= BeginPackage["Tampere`"]
GSV::usage = "help on GSV." Begin["`GSV`"]
GSV[x_, y_, z_] := Module[{a = 77}, x*y*z*a]
End[]
EndPackage[];
… GSV: Symbol GSV appears in multiple contexts {Tampere`, RansIan`}; definitions in context Tampere` may shadow or be … .
In[13]:= GSV[x_Integer, z_Integer] := Module[{a = 42}, (x + z)*a] In[14]:= Context[GSV]
Out[14]= "Tampere`"
In[15]:= $ContextPath
Out[15]= {"Tampere`", "RansIan`", "DocumentationSearch`", "ResourceLocator`", "URLUtilities`", "AladjevProcedures`",
"PacletManager`", "System`", "Global`"}
In[16]:= ContextDef[x_ /; SymbolQ[x]] :=
Module[{a = $ContextPath, b = ToString[x], c = {}},
Do[If[! SameQ[ToString[Quiet[Check[ToExpression[ "Definition1[" <> ToString1[a[[k]] <> b] <> "]"], "Null"]]], "Null"], AppendTo[c, {a[[k]] <> b, If[ToString[Definition[b]] != "Null", "Global`" <> b, Nothing]}]], {k, 1, Length[a]}];
Map[StringReplace[#, "`" <> ToString[x] –> "`"] &, DeleteDuplicates[Flatten[c]]]]
In[17]:= ContextDef[GSV]
Out[17]= {"Tampere`", "Global`", "RansIan`"}
In[78]:= DefContext[x_ /; ContextQ[x], y_Symbol] := Module[{a = $ContextPath, b, c, d},
If[Set[d, ContextDef[y]] == {}||d == {"Global`"}, Definition[y],
If[FreeQ[d, x] || FreeQ[a, x], $Failed, a = PrependTo[a, x]; b = ToExpression[c = x <> ToString[y]]; If[x == "Global`" && ContextDef[y] != {},
Quiet[Definition2[y][[–2]]], StringReplace[Definition2[b][[1]], {x –> "", ToString[y] <> "`" –> ""}]], $Failed]]]
364
Mathematica: Functional and procedural programming
In[79]:= DefContext["RansIan`", GSV]
Out[79]= "GSV[x_, y_, z_] := Module[{a = 72}, x*y*z*a]"
In[80]:= DefContext["Tampere`", GSV]
Out[80]= "GSV[x_, y_, z_] := Module[{a = 77}, x*y*z*a]"
In[81]:= DefContext["Global`", GSV]
Out[81]= "GSV[x_Integer, z_Integer] := Module[{a = 42}, (x+z)*a]"
Because of the multiplicity admissibility of contexts for a symbol, it is advantageous to have a means for determining the symbol depending on its context. The task is solved by means of a procedure whose source code is represented in the previous fragment and whose call DefContext[c, s] returns the definition of a symbol s having a context c. If s is an undefined symbol, the procedure call returns Null; if c is not a context of the s symbol, then the procedure call returns $Failed; if s is not a symbol, then the procedure call is returned as unevaluated. Thus, at using of the objects of the same name, to avoid misunderstandings it is necessary, generally, to associate them with the contexts which have been ascribed to them.
For receiving access to the package tools it is necessary that a package containing them was uploaded to the current session, and the list defined by means of the $ContextPath variable has to include the context corresponding to the package. A package can be loaded in any place of the current document by means of the call Get["context'"] or by means of the call Needs["context'"] to define uploading of a package if the context associated with the package is absent in the list defined by $Packages variable. In a case if package begins with BeginPackage["Package'"], at its uploading to the lists defined by the variables $ContextPath and $Packages only context "Package'" is placed, providing the access to exports of the package and system means.
If a package uses means of other packages, then the package should begin with the construction BeginPackage["Package'", {"Package1'", …, "Package2'"}] with indication of the list of the contexts associated with such packages. It allows to include, in addition, in the system lists $ContextPath and $Packages the demanded contexts. With features of uploading of packages the
365
V.Z. Aladjev, M.L. Shishakov, V.A. Vaganov
reader can familiarize oneself, in particular, in [6-8,10-15].
When operating with contexts, the question often arises of testing a string expression as a potentially possible context. The system does not have such a tool and it is possible use a simple function whose call ContextQ[j] returns True if j is a potentially possible context and False otherwise.
In[78]:= ContextQ[j_] := StringQ[j] && StringLength[j] > 1 &&
StringFreeQ[j, "``"] && SymbolQ[StringReplace[j, "`" –> ""]] && StringTake[j, –1] == "`" && ! StringTake[j, 1] === "`"
In[79]:= ContextQ["a2a`b1bb`ccc1`"]
Out[79]= True
In[80]:= ContextQ["aa``bbb`ccc`"]
Out[80]= False
A package similarly the procedure allows a nesting; at that, in Mathematica all sub-packages composing it are distinguished and registered. The means defined in the main package and in its sub-packages are fully accessible in the current session after uploading of the nested package as quite visually illustrates a number of simple examples [8-10]. Meanwhile, for performance of the aforesaid it is necessary to redefine system $ContextPath variable after uploading of the nested package, having added all contexts of sub-packages of the main package to the list defined by the $ContextPath variable. In this context the ToContextPath procedure automatizes this task, whose call ToContextPath[x] provides updating of contents of the current list determined by $ContextPath variable by adding to its end of all contexts of m– file x containing in a simple or nested package. Fragment below represents source code of the procedure with an example of use.
In[3333]:= ToContextPath[x_ /; FileExistsQ[x] && FileExtension[x] == "m"] := Module[{a = ReadString[x], b},
b = StringReplace[a, "\n" –> ""]; b = StringCases[b, "["~~ Shortest[__] ~~"]"]; b = Map[StringTrim[#, ("[" | "]")] &, b];
b = Select[b, ContextQ[Quiet[ToExpression[#]]] &]; b = ToExpression[DeleteDuplicates[b]]; $ContextPath = Flatten[Insert[$ContextPath, b, –3]]; DeleteDuplicates[$ContextPath]]
366
Mathematica: Functional and procedural programming
In[3334]:= ToContextPath[Directory[] <> "\\init.m"]
Out[3334]= {"DocumentationSearch`", "ResourceLocator`", "URLUtilities`", "AladjevProcedures`", "PacletManager`", "NeuralNetworks`", "NeuralNetworks`Bootstrap`Private`", "GeneralUtilities`", "MXNetLink`", "System`", "Global`"}
However, because of certain features the use of the nested packages doesn't make a special sense.
Thus, if the call Context[x] of built-in function returns the context associated with a symbol x, then an interesting enough question of detecting of a m–file with a package containing the given context arises. The call FindFileContext[x] returns the list of full paths to m–files with packages containing the x context; in a case of absence of such files the call returns the empty list. In addition, the call FindFileContext[x, y, z, …] with optional {y, z, …} arguments – the names in string format of devices of external memory of direct access – provides search of required files on the specified devices instead of all file system of the computer in a case of the procedure call with one actual argument. The search of the required m-files is done also in the $Recycle.bin directory of Windows 7 system [12-16]. However, it must be kept in mind, that search within all file system of the computer can demand a rather essential temporal expenditure.
We have created a number of tools [8] for processing of the user packages located in files of type {cdf, nb, m, mx}. At that, of particular interest is the procedure for determining whether a file of type mx with user package contains the specified tools.
In[3332]:= ToolsInMxQ[x_, y_ /; FileExistsQ[y] &&
FileExtension[y] == "mx", t___] := Module[{a = Map[ToString, Flatten[{x}]], b = ContextsInFiles[y], c, d},
If[MemberQ[$ContextPath, b], MemberQ6[CNames[b], a, t], Quiet[Get[y]]; c = MemberQ6[Set[d, CNames[b]], a, t]; Map[ClearAll[#] &, d]; $ContextPath = ReplaceAll[$ContextPath, b –> Nothing]; Unprotect[$Packages]; $Packages = Complement[$Packages, Contexts[StringTake[b, {1, –2}] <> "*"]];
SetAttributes[$Packages, Protected]; c]]
367
V.Z. Aladjev, M.L. Shishakov, V.A. Vaganov
In[3333]:= ToolsInMxQ[{Vz, mm, Gn, nn}, "Agn.mx", t42]
Out[3333]= False In[3334]:= t42 Out[3334]= {mm, nn}
The procedure call ToolsInMxQ[x, y, t] returns True if a tool x or their list belong to a file y of mx-type with the user package, and False otherwise. At that, through the 3rd optional argument t – an undefined symbol – the list of elements of x that are absent in the y file are returned. The procedure uses an useful enough MemberQ6 procedure whose source code is represented below.
In[2]:= MemberQ6[x_ /; ListQ[x], y_ /; ListQ[y], t___] := Module[{a = {}, b = {}}, Do[If[MemberQ[x, y[[j]]],
AppendTo[a, True], AppendTo[a, False]; AppendTo[b, y[[j]]]], {j, Length[y]}]; a = AllTrue[a, # == True &]; If[{t} != {} && ! HowAct[t], t = b, 77]; a]
In[3]:= {MemberQ6[{a, b, c, d, g, h}, {a, d, h, u, t, s, w, g, p}, t], t} Out[3]= {False, {u, t, s, w, p}}
The call MemberQ6[x, y, t] returns True if all elements of a list y belong to a list x, and False otherwise. In addition, through the third optional argument t – an undefined symbol – the list of elements of the y list that are not in the x list are returned.
In a sense the procedures ContextMfile and ContextNBfile are inverse to the procedures FindFileContext, FindFileContext1 and ContextInFile, their successful calls ContextMfile[w] and
ContextNBfile[w] return the context associated with a package located in a file w of formats ".m" and {".nb", ".cdf"} accordingly; the w file is set by means of name or full path to it [8,10-16].
Unlike of the procedure DeletePackage [16] the procedure RemovePackage is that the symbols determined by the package, removes completely so that their names are no longer recognized in the current session. The call DeletePackage[x] returns Null, i. e. nothing, providing removal from the current session of the package, determined by a context x, including all its exported symbols and accordingly updating the lists determined by the $Packages, $ContextPath and Contexts[] variables. Fragment
368
Mathematica: Functional and procedural programming
below represents source code of the DeletePackage procedure along with accompanying examples.
In[3331]:= BeginPackage["Package7`"]
W::usage = "Help on W." Begin["`W`"]
W[x_Integer, y_Integer] := x^2 + y^2 End[]
EndPackage[]; In[3337]:= $Packages
Out[3337]= {"Package7`", "DocumentationSearch`", …, "Global`"} In[3338]:= $ContextPath
Out[3338]= {"Package7`", "DocumentationSearch`", …, "Global`"} In[3339]:= MemberQ[Contexts[], "Package7`"]
Out[3339]= True
In[3340]:= CNames["Package7`"]
Out[3340]= {"W"}
In[3341]:= ? W Out[3341]= "Help on W."
In[3342]:= DeletePackage[x_] := Module[{a},
If[! MemberQ[$Packages, x], $Failed, a = Names[x <> "*"]; Map[ClearAttributes[#, Protected] &, Flatten[{"$Packages", "Contexts", a}]]; Quiet[Map[Remove, a]]; $Packages = Select[$Packages, # != x &]; $ContextPath = Select[$ContextPath, # != x &];
Contexts[] = Select[Contexts[], StringCount[#, x] == 0 &]; Quiet[Map[Remove, Names[x <> "*"]]]; Map[SetAttributes[#, Protected] &, {"$Packages", "Contexts"}]; ]]
In[3343]:= DeletePackage["Package7`"] In[3344]:= $Packages
Out[3344]= {"DocumentationSearch`", …,"System`", "Global`"} In[3345]:= $ContextPath
Out[3345]= {"DocumentationSearch`", …, "System`", "Global`"} In[3346]:= CNames["Package7`"]
Out[3346]= {} In[3347]:= ?W
Out[3347]= Missing["UnknownSymbol", "W"]
In[3348]:= MemberQ[Contexts[], "Package7`"]
Out[3348]= False
369
V.Z. Aladjev, M.L. Shishakov, V.A. Vaganov
In certain cases, it makes sense to change the context that is assigned to the user's package contained in the mx–format file. This problem is solved by the procedure presented below. The procedure call ChangeContextInMx[x, y] returns the list of the form {dir, y}, where dir is the path to the $Name file formed in the same directory as the user's original Name mx–file in path x with the package that will have a new y context. If a file x does not have a context, then the procedure call returns $Failed with printing of the corresponding message. In addition, if the Name mx–file is already loaded into the Mathematica current session, it remains loaded (whereas the $Name file is not loaded), otherwise both the Name and $Name files are not loaded into the current session. The fragment represents source code of the procedure along with typical examples of its application.
In[2336]:= ChangeContextInMx[x_ /; FileExistsQ[x] &&
FileExtension[x] == "mx", y_ /; ContextQ[y]] := Module[{a, b, c, d}, b = ContextInMxFile[x];
If[SameQ[b, $Failed], Print["File " <> x <> " has no a context"]; $Failed, If[! MemberQ[$Packages, b], Get[x]; c = 78, c = 80]; a = CNames[b]; Map[ContextToSymbol3[#, y] &, a]; d = FileNameSplit[x]; d[[–1]] = "$" <> d[[–1]]; d = FileNameJoin[Flatten[{d[[1 ;; –2]], d[[–1]]}]];
DumpSave[d, y]]; Unprotect[Contexts]; Contexts[] = ReplaceAll[Contexts[], Flatten[{b –> Nothing, y –> Nothing, Map[b <> # –> Nothing &, a], Map[y <> # –> Nothing &, a]}]]; Protect[Contexts];
Unprotect[$Packages]; $Packages = Complement[$Packages, {b, y}]; $ContextPath = Complement[$ContextPath, {b, y}];
Protect[$Packages]; Map[ClearAll[#] &, Map[y <> # &, a]]; If[c == 80, Get[x], Null]; {d, y}]
In[2337]:= ContextInMxFile["avzagn.mx"]
Out[2337]= "RansIan`"
In[2338]:= ChangeContextInMx["avzagn.mx", "NewContext`"]
Out[2338]= {"$avzagn.mx", "NewContext`"}
In[2339]:= Get["$avzagn.mx"]
In[2340]:= CNames["NewContext`"]
Out[2340]= {"ArtKr", "GS", "GSV"}
370
