Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

KDP_book

.pdf
Скачиваний:
19
Добавлен:
03.01.2021
Размер:
2.46 Mб
Скачать

Mathematica: Functional and procedural programming

In a number of tasks, substantially including debugging of the user software, there is a necessity of dynamic change of the testing functions for formal arguments of blocks, functions and modules in the moment of their call. The ChangeLc procedure allowing dynamically monitoring influence of testing functions of formal arguments of tools for correctness of their calls is for this purpose programmed. The following fragment submits the source code of the procedure with examples of its application.

In[4478]:= ChangeLc[x_ /; StringQ[x] || ListQ[x],

P_ /; BlockFuncModQ[P], y___] := Module[{a = ArgsBFM2[P], b = Definition1[P], p, c, d, g, n, m = Flatten[{x}], t = ToString[P]},

p = Map[StringTake[#, Flatten[StringPosition[#, "_"]][[1]]] &, m]; b = StringReplace[b, "Global`" <> t <> "`" > ""]; c = StringReplace[b, t <> "[" > ToString[n] <> "[", 1]; a = Select[a, SuffPref[#, p, 1] &]; If[a == {}, P @@ {y},

c = StringReplace[c, Rule1[Riffle[a, m]], 1]; ToExpression[c]; t = n @@ {y}; {If[SuffPref[ToString1[t], ToString[n] <> "[", 1],

"Updating testing functions for formal arguments of " <> ToString[P] <> " is unsuitable", t], Remove[n]}[[1]]]]

In[4479]:= G[x_, y_, z_Integer] := x^2 + y^2 + z^2 In[4480]:= ChangeLc["y_ /;IntegerQ[y]", G, 42, 47, 67]

Out[4480]= 8462

In[4481]:= ChangeLc["y_ /;RationalQ[y]", G, 42, 47, 67]

Out[4481]= "Updating testing functions for formal arguments of G is unsuitable"

In[4482]:= ChangeLc["y_ /;RationalQ[y]", G, 42, 47/72, 67]

Out[4482]= 32417761/5184

The procedure call ChangeLc[x, P, y] returns the result of a call P[y] on condition of replacement of the testing functions of arguments of a block, module or function P by the new functions determined by a string equivalent x or by their list. The string equivalent is coded in shape "x_ /; TestQ[x]", x – an argument.

41

V.Z. Aladjev, M.L. Shishakov, V.A. Vaganov

The task of checking formal arguments of blocks, modules, functions for their validity to admissible values plays a rather important role and this problem is solved by means of the test functions assigned to the corresponding formal arguments of the above objects. The first part of the next fragment represents an example of how to organize a system to test the actual values obtained when the Art procedure is called. At that, if the formal arguments receive invalid actual values, then procedure call is returned unevaluated with the corresponding message printing for the first of such formal arguments. Note that built-in means Mathematica also do quite so. Meantime, the task of checking all invalid values for formal arguments when calling the above objects is more relevant. Below is a possible approach to that.

In[2185]:= Art[x_ /; If[IntegerQ[x], True,

Print["Argument x is not integer, but received: x = " <> ToString1[x]]; False], y_ /; If[ListQ[y], True,

Print["Argument y is not a list, but received: y = " <> ToString1[y]]; False], z_ /; If[StringQ[z], True,

Print["Argument z is not a string, but received: z = " <> ToString1[z]]; False]] := Module[{a = 77}, N[x*(Length[y] + StringLength[z])/a]]

In[2186]:= Art[47, {a, b, c}, "RansIan"]

Out[2186]= 6.1039

In[2187]:= Art[47.42, 67, "abc"]

Argument x is not integer, but received: x = 47.42 Out[2187]= Art[47.42, 67, "abc"]

In[2188]:= Art[47.42, 67, "abc"]

Argument x is not integer, but received: x = 47.42 Out[2188]= Art[47.42, 67, "abc"]

In[2189]:= Art[500, 67, "abc"]

Argument y is not a list, but received: y = 67 Out[2189]= Art[500, 67, "abc"]

In[2190]:= Art[500, {a, b, c}, 77]

Argument z is not a string, but received: z = 77 Out[2190]= Art[500, {a, b, c}, 77]

42

Mathematica: Functional and procedural programming

The above approach is based on the following construction:

G[x_ /; If[Testx, $x$ = True, Print[]; $x$ = False; True], y_ /; If[Testy, $y$ = True, Print[]; $y$ = False; True], …

h_ /; AllTrue[{$x$, $y$, }, # == True &]] := Module[{}, ]

The Kr procedure is an example of using of the above approach.

In[2192]:= Kr[x_ /; If[IntegerQ[x], $x$ = True,

Print["Argument x is not integer, but received: x = " <> ToString1[x]]; $x$ = False; True], y_ /; If[ListQ[y], $y$ = True,

Print["Argument y is not a list, but received: y = " <> ToString1[y]]; $y$ = False; True], z_ /; If[StringQ[z], $z$ = True,

Print["Argument z is not a string, but received: z = " <> ToString1[z]]; $z$ = False; True], h_ /; AllTrue[{$x$, $y$, $z$}, # == True &]] :=

Module[{a = 77}, N[x*(Length[y] + StringLength[z])/a]]

In[2193]:= Kr[47, {a, b, c}, "RansIan", 78]

Out[2193]= 6.1039

In[2194]:= Kr[42.47, 67, "abc", 78]

Argument x is not integer, but received: x = 42.47 Argument y is not a list, but received: y = 67

Out[2194]= Kr[42.47, 67, "abc", agn] In[2195]:= Kr[42.47, 67, 90, 78]

Argument x is not integer, but received: x = 42.47 Argument y is not a list, but received: y = 67 Argument z is not a string, but received: z = 90

Out[2195]= Kr[42.47, 67, 90, agn]

The final part of the above fragment represents the Kr procedure equipped with a system for testing the values of the actual arguments passed to the formal arguments when the procedure is called. At that, only three {x, y, z} arguments are used as the formal arguments used by the procedure body, whereas the 4th additional argument h, when the procedure is called obtains an arbitrary expression, and in the Kr body is not used. Argument h at the procedure header level tests the validity of the actual values obtained by formal arguments when the procedure is called. The argument allows to obtain information on all inadmissible values for formal arguments at the procedure call. The fragment is fairly transparent and does not require any clarifying.

43

V.Z. Aladjev, M.L. Shishakov, V.A. Vaganov

Based on the form of the heading shown above, it is possible to represent its rather useful extension, that allows to form the function body at the level of such heading. In general, the format of such header takes the following form:

G[x_ /; If[Test[x], Save["#", a, b, c, ]; a = {x, True}; True,

Save["#", a, b, c, ]; Print["Argument x is not integer, but received: x = " <> ToString1[x]]; a = {x, False}; True],

y_ /; If[Test[y], b = {y, True}; True,

Print["Argument y is not integer, but received: y = " <> ToString1[y]]; b = {y, False}; True],

z_ /; If[Test[z], c = {z, True}; True,

Print["Argument z is not integer, but received: z = " <> ToString1[z]]; c = {z, False}; True],

………………………………………………………………………

h_ /; If[AllTrue[{a[[2]],b[[2]], c[[2]]}, # == True &],

Result = Function_Body[a[[1]], b[[1]], c[[1]]]; Get["#"]; DeleteFile["#"]; True, Get["#"]; DeleteFile["#"]; False]] := Result

where Test[g] – a testing expression, testing an expression g on admissibility to be g as a valid value for the g argument and Result is the result of Function_body evaluation on formal arguments {x,y,z,…}. Let give a concrete filling of the above structural form as an example:

In[3338]:= ArtKr[x_ /; If[IntegerQ[x], Save["#", a, b, c];

a = {x, True}; True, Save["#", a, b, c];

Print["Argument x is not Integer, but received: x = " <> ToString1[x]]; a = {x, False}; True], y_ /; If[RealQ[y], b = {y, True}; True,

Print["Argument y is not Real, but received: y = " <> ToString1[y]]; b = {y, False}; True], z_ /; If[RationalQ[z], c = {z, True}; True,

Print["Argument z is not Rational, but received: z = " <> ToString1[z]]; c = {z, False}; True], h_ /; If[AllTrue[{a[[2]], b[[2]], c[[2]]}, # == True &],

$Result$ = N[a[[1]]*b[[1]]*c[[1]]]; Get["#"]; DeleteFile["#"]; True, Get["#"]; DeleteFile["#"]; False]] := $Result$

In[3339]:= $Result$ = ArtKr[72, 77.8, 50/9, vsv]

Out[3339]= 31120.

44

Mathematica: Functional and procedural programming

In[3340]:= $Result$ =ArtKr[72, 77.8, 78, vsv]

Argument z is not Rational, but received: z = 78 Out[3340]= ArtKr[72, 77.8, 78, vsv]

In[3341]:= ArtKr[72, 778, 78, vsv]

Argument y is not Real, but received: y = 778 Argument z is not Rational, but received: z = 78

Out[3341]= ArtKr[72, 778, 78, vsv] In[3342]:= ArtKr[7.2, 77.8, 78, vsv]

Argument x is not Integer, but received: x = 7.2 Argument z is not Rational, but received: z = 78

Out[3342]= ArtKr[7.2, 77.8, 78, vsv] In[3343]:= ArtKr[7.2, 900, 50, vsv]

Argument x is not Integer, but received: x = 7.2 Argument y is not Real, but received: y = 900 Argument z is not Rational, but received: z = 50

Out[3343]= ArtKr[7.2, 900, 50, vsv]

The function call ArtKr[x,y,z,h] returns the result of evaluation of a Function_Body on actual arguments {x, y, z}. In addition, the call ArtKr[x, y, z, h] as a value for formal h argument obtains an arbitrary expression. Through the global variable $Result$, the call ArtKr[x,y, z,h] additionally returns the same value. At that, the above structural form is based on the processing principle of actual arguments passed to an object (block, function, module), namely the values of actual arguments are processed by the corresponding testing functions from left to right until the first calculation False that is obtained by a testing function, after that the call of the object is returned as unevaluated, otherwise the result of the object calculation is returned.

Therefore, the test functions for the leading formal arguments of the form are formed in such way that they return True when fixing the real value of the test functions and the values passed to them. Whereas the test function for the auxiliary argument h determines both the evaluation of the object body and validity of the factual values for the principal arguments {x,y,z}, solving the task of returning the result of the calculation of the body of the object or returns the result of the call unevaluated. Fragment above is fairly transparent and does not require any clarifying.

45

V.Z. Aladjev, M.L. Shishakov, V.A. Vaganov

According to the scheme described above, Mathematica allows a number of useful modifications. In particular, the following form of organizing headers {block, function, module} having the following format may well be of interest:

S[x_ /; {X1; …; Xp; {Testx|True}}[[1]], y_ /; {Y1; … ; Yn; Testy}[[1]],

………………………………………………………..

z___ /; {Z1; …; Zt; Testz}[[1]] := Object

In addition as {X1,…,Xp; Y1,…,Yn; ...; Z1,…,Zt} act admissible offers of the Mathematica language, including calls of blocks, functions and modules whereas as {Testx,Testy,…,Testz} act the testing functions ascribed to the corresponding formal {x,y,…,z} arguments. In a case if there is no the test function for a formal argument (i.e. argument allows any type of actual value) instead of it is used True. Moreover, the test function for pattern "x___" or "x__" should refer to all formal arguments relating thereto. In a case of such patterns used in an object header should be used or True, or a special test function, for example, that is presented below. In a number of cases similar approach can be interesting enough. The procedure call TrueListQ[x, y] returns True if a list x has elements whose types match to the corresponding elements of a list y, otherwise False is returned. If lengths of both lists are different, then the first Min[x, y] their elements are analyzed. At that, the call with the third argument z – an indefinite symbol additionally returns the list of elements positions of list x whose types different from the corresponding types from the y list. The source code of the TrueListQ procedure is represented below.

In[942]:= TrueListQ[x_ /; ListQ[x], y_ /; ListQ[y], z___] := Module[{a = Length[x], b = Length[y], c = {}, t = {}},

Do[If[SameQ[Head[x[[j]]], y[[j]]], AppendTo[c, True], AppendTo[t, j]; AppendTo[c, False]], {j, Min[a, b]}]; If[{z} != {} && SymbolQ[z], z = t; If[t == {}, True, False], If[t == {}, True, False]]]

In[943]:= TrueListQ[{77, 6, "abc"}, {Integer, Symbol, String}]

Out[943]= False

46

Mathematica: Functional and procedural programming

In[944]:= TrueListQ[{77, 7.8, "a"}, {Integer, Real, String}]

Out[944]= True

In[945]:= {TrueListQ[{a, 78, "2020"}, {Integer, Real, String}, g], g} Out[945]= {False, {1, 2}}

The following simple GW procedure uses the TrueListQ in heading for definition of its optional formal y argument along with illustrative examples of the procedure calls.

In[63]:= GW[x_, y___ /; TrueListQ[{y}, {Integer, Real, Rational}, agn]] := Module[{a = 78}, x*(y + a)]

In[64]:= GW[77]

Out[64]= 6006

In[65]:= GW[77, 78, 7.8, 7/8]

Out[65]= 12680.

In[66]:= {GW[77, 78, 42, 47], agn} Out[66]= {GW[77, 78, 42, 47], {2, 3}}

As some instructive information, an example of a procedure whose heading is based on the principles mentioned above is presented. The procedure call Mn[x, y, z] returns the list of all arguments passed to the procedure without reference to them.

In[3349]:= Mn[x_ /; {WriteLine["#", x]; IntegerQ[x]}[[1]],

y_ /; {WriteLine["#", y]; ListQ[y]}[[1]], z___ /; {WriteLine["#", z]; True}[[1]]] := Module[{Arg}, Close["#"]; Arg = ReadString["#"]; DeleteFile["#"];

Arg = StringReplace[Arg, "\n" –> ","]; Arg = StringTake[Arg, {1, –2}];

Arg = ToExpression[StringToList[Arg]]; Arg] In[3350]:= Mn[77, {42, 47, 67}]

Out[3350]= {77, {42, 47, 67}} In[3351]:= Mn[77, {42, 47, 67}, a + b]

Out[3351]= {77, {42, 47, 67}, a + b} In[3352]:= Mn[m, {42, 47, 67}, a + b]

Out[3352]= Mn[m, {42, 47, 67}, a + b] In[3353]:= Mn[m, {42, 47, 67}, a + b, 77, 78]

Out[3353]= Mn[m, {42, 47, 67}, a + b, 77, 78]

Another point should be emphasized. As already mentioned, when calling an object (block, function or module), the factual arguments passed to it in its heading are processed in the list

47

V.Z. Aladjev, M.L. Shishakov, V.A. Vaganov

order, i.e., from left to right in the list of formal arguments. In this regard, and in view of the foregoing, there is a rather real possibility of setting in definition of some formal argument of a test function that can be used as test functions for subsequent formal arguments of the object heading. The following fragment represents a rather simple Av function whose heading contains definitions of two Boolean functions, used by a testing function for the last formal z argument of the Av function. At the same time, when you call function Av, both Boolean functions B and V become active in the current session, as it is well shown in the examples below.

In[3442]:= Av[x_ /; {SetDelayed[B[t_], If[t <= 90, True, False]]; NumberQ[x]}[[1]],

y_ /; {SetDelayed[V[t_], ! RealQ[t]]; RealQ[y]}[[1]], z_ /; B[z] && V[z]||RationalQ[z]] := N[Sqrt[x*y/z]]

In[3443]:= {Av[77, 7.8`, 42/47], Av[77, 90, 500]} Out[3443]= {25.9249, Av[77, 90, 500]} In[3444]:= Definition[B]

Out[3444]= B[t_] := If[t <= 90, True, False] In[3445]:= Definition[V]

Out[3445]= V[t_] := ! RealQ[t] In[3446]:= {B[42], V[47]} Out[3446]= {True, True}

In[3447]:= Ag[x_ /; {SetDelayed[W[t_], Module[{a = 77}, a*t^2]]; IntegerQ[x]}[[1]]] := W[x] + x^2

In[3448]:= {Ag[42], W[42]}

Out[3448]= {137592, 135828}

In[3449]:= Definition[W]

Out[3449]= W[t_] := Module[{a = 77}, a*t^2]

Thus, in particular, the last example of the previous fragment also shows that the object W (block, function, module) defined in the header of another Ag object allows a correct call in the body of the Ag object yet when it is first called. When Ag is called, W becomes active in the current session. The above fragments are quite transparent and do not require any clarifying. Technique, presented in them are of some practical interest in programming of the objects headings and objects as a whole.

48

Mathematica: Functional and procedural programming

On the assumption of the general definition of a procedure, in particular, of modular type

M[x_ /; Testx, y_ /; Testy, ...] := Module[{Locals}, Procedure body]

and of the fact that concrete definition of procedure is identified not by its name, but its heading we will consider a set of useful enough tools [16] that provide the various manipulations with the headings of procedures and functions and play a important part in the procedural and functional programming and, above all, of programming of problems of the system character.

Having defined such object useful in many appendices as the heading of a procedure or function in the form "Name[List of formal arguments with the testing tools ascribed to them]", naturally arises the question of creating of means for testing of the objects regarding their relation to the `Heading` type. We have created a number of tools [15,16] in the form of procedures HeadingQ ÷ HeadingQ3. At the same time, between pairs of the procedures {HeadingQ, HeadingQ1} and {HeadingQ2, HeadingQ3} principal distinctions exist. Meantime, considering standard along with some other unlikely encoding formats of the procedures and functions headings, the above four tools can be considered as rather useful testing tools in programming. In addition, from experience of their use and their time characteristics it became clear that it is quite enough to be limited oneself only by tools HeadingQ and HeadingQ1 which cover a rather wide range of erroneous coding of headings. Meantime, taking into account the mechanism of expressions parse for their correctness, that Mathematica uses, creation of comprehensive means of testing of the headings is a rather difficult. Naturally, it is possible to use the nonstandard receptions for receiving the testing tools for the headings having a rather wide set of the deviations from the standard ones, but such outlay often do not pay off by the received benefits.

In particular, the possibilities of the HeadingQ ÷ HeadingQ3 procedures are overlapped by opportunities of the means whose call TestHeadingQ returns True if a x represents the heading in

49

V.Z. Aladjev, M.L. Shishakov, V.A. Vaganov

string format of a block, module, function, and False otherwise. Whereas the procedure call TestHeadingQ[x, y] with the second optional y argument an indefinite variable thru it additionally returns information specifying the reasons of the incorrectness of a heading x. At that, on x objects of the same name (multiple objects) the procedure call returns $Failed; the extension of the procedure to case of the multiple objects does not cause much difficulty. The source code of the TestHeadingQ procedure and the comparative analysis of its use concerning the HeadingQ ÷ HeadingQ3 tools say about its preference at programming in Mathematica [8,9,15]. So, of the detailed analysis follows, that the TestHeadingQ procedure on the opportunities surpasses the above testing tools of the same purpose.

In the light of real correctness of formal arguments that has been defined above and is caused by mutual arrangements of formal arguments in the context of patterns {"_", "__", "___"} we can significantly generalize the above group of procedures HeadingQ ÷ HeadingQ3, intended for testing of correctness of headings of the user blocks, functions & modules, additionally using the CorrTupleArgs procedure. For this purpose the tool

HeadingsUQ can be used.

Calling the HeadingsUQ[x] procedure returns True if all components composing an object x in the context of the call Definition[x] have correct headings, and False otherwise. While the call HeadingsUQ[x, y] in addition through the 2nd optional argument an undefined y symbol returns the list the elements of which are True if the heading of the appropriate component of x object is correct, or the list whose first element determines incorrect heading of a component of the x object, whereas the second element determines of the component number with this heading concerning the call Definition[x]. The source code of the TestHeadingQ procedure with examples of its use can be found in [15,16]. The HeadingsUQ procedure rather exhaustively tests the correctness of the headings of blocks, functions and modules.

It is appropriate to make one quite substantial remark here. As you know, Mathematica does not allow to use the testing

50

Соседние файлы в предмете Математические пакеты