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

KDP_book

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

Mathematica: Functional and procedural programming

3.1. Branching control structures in Mathematica

Conditional branching structures. Complex computation algorithms and/or control (first of all) cannot has with a purely sequential scheme, however include various constructions that change the sequential order of the algorithm depending on the occurrence of certain conditions: conditional and unconditional transitions, loops and branching (structure of such type is called control one). Thus, for the organization of control structures of branching type the Mathematica system has a number of tools provided by so-called If-offer, having the following two coding formats, namely:

If[CE, a, b] returns a, if conditional expression CE gives True, and b if conditional expression CE gives False;

If[CE, a, b, c] returns a, if conditional expression CE gives True, b if expression CE gives False and c if expression CE gives to neither True nor False.

The meaning of the first format of If sentence is transparent, being represent in one form or another in many programming languages. Whereas the second format in the context of other Mathematica tools is not quite correct, namely. If a Boolean w expression does not make sense True or False, then If[w, a, b, c] (by definition) returns c, while call BooleanQ[w] returns False if an expression w does not equal neither True nor False and a call If[BooleanQ[w],a,b,c] will return b, defining a certain dissonance, which illustrates the following rather simple example:

In[3336]:= If[77, a, b, c]

Out[3336]= c

In[3337]:= If[BooleanQ[77], a, b, c]

Out[3337]= b

In[3338]:= BooleanQ[77]

Out[3338]= False

In our view, in order to resolve such situation, it is enough useful to define in Mathematica system a Boolean value other than True and False, similar to the FAIL value in Maple. In the Maple concept, the FAIL symbol is used by logic to define both

111

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

an unknown and undefined value. At the same time, if it is used inside Boolean expressions with operators and, or and not, its behaviour is similar to the case of standard 3–digit logic. Many Maple procedures and functions, primarily of the testing type, return the FAIL symbol if they cannot clearly establish the truth or falsity of a Boolean expression. In addition, the If function allows an arbitrary level of nesting, whereas as a and b can be any sequences of valid Mathematica clauses. In the context of Mathematica syntax, both format of If function can participate in expressions forming.

The If clause is the most typical tool of providing branching algorithms. In this context, it should be noted that the if tool of the Maple language and If of the Mathematica language appear to be largely equivalent, however in the sense of readability it is somewhat easier to perceive rather complex branching algorithms implemented precisely by the if clauses of Maple. It is difficult to give preference to anyone in this regard.

Thus, the If function ris the most typical tool for ensuring of the branching algorithms. In this context it should be noted that If means of the Maple and Mathematica are considerably equivalent, however readability of difficult enough branching algorithms realized by means of if offers of the Maple is being perceived slightly more clearly. So, Maple allows conditional if offer of the following format, namely:

if l1 then v1 elif l2 then v2 elif l3 then v3 elif l4 then v4 … else vk end

where j–th lj a logical condition and vj an expression whose sense is rather transparent and considered, for example, in [38]. This offer is convenient at programming of a lot of conditional structures. For definition of a similar structure in Mathematica, the IFk procedure whose source code with examples of its use represents the following fragment can be used, namely:

In[3212]:= IFk[x__] := Module[{a = {x}, b, c = "", d = "If[", e = "]", h = {}, k = 1}, b = Length[a];

If[For[k, k <= b – 1, k++, AppendTo[h, b >= 2 && ListQ[a[[k]]] && Length[a[[k]]] == 2]]; DeleteDuplicates[h] != {True}, Return[Defer[IFk[x]]], k = 1];

112

Mathematica: Functional and procedural programming

For[k, k <= b – 1, k++, c = c <> d <> ToString[a[[k]][[1]]] <> "," <> ToString[a[[k]][[2]]] <> ","]; c = c <> ToString[a[[b]]] <> StringMultiple[e, b – 1]; ToExpression[c]]

In[3213]:= IFk[{a, b}, {c, d}, {g, s}, {m, n}, {q, p}, h] Out[3213]= If[a, b, If[c, d, If[g, s, If[m, n, If[q, p, h]]]]]

The call of IFk procedure uses any number of the factual arguments more than one; the arguments use the twoelement lists of the form {lj, vj}, except the last. Whereas the last factual argument is a correct expression; in addition, a testing of a lj on Boolean type isnt done. The call of IFk procedure on a tuple of the correct factual arguments returns the result equivalent to execution of the corresponding Maple offer if.

The IFk1 procedure is an useful extension of the previous procedure which unlike IFk allows only Boolean expressions as factual arguments lj, otherwise returning the unevaluated call. In the rest, the IFk and IFk1 are functionally identical. Thus, similarly to the Maple offer if, the procedures IFk and IFk1 are quite useful at programming of the branching algorithms of the different types. With that said, the above procedures IFk, IFk1 are provided with a quite developed mechanism of testing of the factual arguments transferred at the procedure call whose algorithm is easily seen from the source code (see below).

In[7]:= IFk1[x__] := Module[{a = {x}, b, c = "", d = "If[", e = "]",

h = {}, k = 1}, b = Length[a];

If[For[k, k <= b – 1, k++, AppendTo[h, b >= 2 && ListQ[a[[k]]] && Length[a[[k]]] == 2]]; DeleteDuplicates[h] != {True}, Return[Defer[IFk1[x]]], {h, k} = {{}, 1}];

If[For[k, k <= b – 1, k++, AppendTo[h, a[[k]][[1]]]]; Select[h, ! MemberQ[{True, False}, #] &] != {},

Return[Defer[IFk1[x]]], k = 1]; For[k = 1, k <= b – 1, k++, c = c <> d <> ToString[a[[k]][[1]]] <> "," <> ToString[a[[k]][[2]]] <> ","];

c = c <> ToString[a[[b]]] <> StringMultiple[e, b – 1]; ToExpression[c]]

113

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

In[8]:= IFk1[{False, b}, {False, d}, {False, s}, {True, G}, {False, p}, h]

Out[8]= G

Now, using the described approach fairly easy to program in Math–language an arbitrary Maple construction describing the branching algorithms [27–41].

In a number of cases the simple Iff procedure with number of arguments in quantity 1÷n which generalizes the standard If function is quite useful tool; it is very convenient at number of arguments, starting with 1, what is rather convenient in those cases when calls of the Iff function are generated in a certain procedure automatically, simplifying processing of erroneous and especial situations arising at the call of such procedure at number of arguments from range 2..4. The following fragment represents source code of the Iff procedure with an example. In addition, it must be kept in mind that all its factual arguments, since the second, are coded in string format in order to avoid their premature calculation at the call Iff[x, ...] when the factual arguments are being evaluated/simplified.

In[1114]:= Iff[x_, y__ /; StringQ[y]] := Module[{a = {x, y}, b},

b = Length[a];

If[b == 1 || b >= 5, Defer[Iff[x, y]], If[b == 2, If[x, ToExpression[y]],

If[b == 3, If[x, ToExpression[y], ToExpression[a[[3]]]], If[b == 4, If[x, ToExpression[a[[2]]], ToExpression[a[[3]]],

ToExpression[a[[4]]]], Null]]]]] In[1115]:= a = {}; For[k = 1, k <= 77, k++, Iff[PrimeQ[k],

"AppendTo[a, k]"]]; a

Out[1115]= {2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73}

To a certain degree to the If constructions adjoins the Which function of the following format:

Which[lc1, w1, lc2, w2, lc3, w3, ..., lck, wk]

which returns the result of evaluation of the first wj expression for which Boolean expression lcj (j = 1..k) accepts True value. If some of the evaluated conditions lcj doesn't return {True|False} the function call is returned unevaluated while in a case of False for all lcj conditions (j = 1..k) the function call returns Null, i.e.

114

Mathematica: Functional and procedural programming

nothing. At dynamical generation of a Which object the simple procedure WhichN can be a rather useful that allows any even number of arguments similar to the Which function, otherwise returning result unevaluated. In the rest, the WhichN is similar to the Which function; the following fragment represents source code of the WhichN procedure with a typical example of its use.

In[47]:= WhichN[x__] := Module[{a = {x}, c = "Which[", d, k = 1}, d = Length[a]; If[OddQ[d], Defer[WhichN[x]],

ToExpression[For[k, k <= d, k++, c = c <> ToString[a[[k]]] <> ","]; StringTake[c, {1, –2}] <> "]"]]]

In[48]:= f = 90; WhichN[False, b, f == 90, SV, g, h, r, t]

Out[48]= SV

The above procedures Iff, IFk, IFk1 and Which represent a quite certain interest at programming a number of applications of various purpose, first of all, of the system character (similar tools are considered in our collection [15] and package MathToolBox).

Unconditional transitions. At that, control transfers of the unconditional type are defined in the language, usually, by the Goto constructions by which control is transferred to the Label point of the procedure specified by the corresponding Label. In Mathematica language, can to usage unconditional transitions based on the built-in Goto function encoded in the Goto[Label] format to organize the branching of algorithms along with the presented If clause. Meanwhile, in a number of cases, the use of this tool is very effective, in particular when it is necessary to load in Mathematica software the programs using unconditional transitions based on Goto offer. Particularly, Fortran programs, rather common in scientifical applications, are a fairly typical example. From our experience it should be noted that the use of Goto function greatly simplified immersion in Mathematica of Fortranprograms related to engineering and physical direction which rather widely use Gotoconstructions. Note that, unlike Mathematica, the goto function in Maple only makes sense in the procedure body, allowing from the goto(Label) call point to go to a sentence preceded by the specified Label. Meanwhile, in

115

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

Mathematica the Goto function is permissible also outside of procedures, although that makes little sense. There can be an arbitrary g expression as a Label, as the following rather simple fragment illustrates, namely:

In[1900]:= G = {}; p = 1; Label[agn]; AppendTo[G, p++];

If[p <= 10, Goto[agn], G]

Out[1900]= {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}

The above fragment illustrates a simple example of cyclic computations on a basis of Goto function without using built-in standard loop functions. However, such cyclic constructions (in the temporary relation) substantially concede to the cycles, for example, based on the builtin function Do, for example:

In[5]:= p = 0; Timing[Label[g]; If[p++ < 999999, Goto[g], p]]

Out[5]= {1.63801, 1000000}

In[6]:= p = 0; Timing[Do[p++, 1000000]; p]

Out[6]= {0.202801, 1000000}

Time differences are quite noticeable, starting with a cycle length greater than 4000 steps. However, cyclic constructions based on the unconditional Goto transition are in some cases quite effective at programming of the procedure bodies. Note another point is worth paying attention to, namely. It is known [20-41] that in Maple, an unconditional goto(Label) transition assumes a global variable as a Label, whereas the definition of

Label as a local (that not test as an error in the procedure definition calculation stage) initiates an erroneous situation when calling the procedure. Whereas the Mathematica in an unconditional Goto[Label] transition as a Label allows both global variable and local along with an arbitrary expression, for example:

In[2254]:= Sv[k_Integer] := Module[{p = 0, g}, Label[g]; If[p++ <= k, Goto[g], {g, p}]]

In[2255]:= Sv[10000]

Out[2255]= {g$16657, 10002}

In[2256]:= Sv1[k_Integer, x_] := Module[{p = 0}, Label[x]; If[p++ <= k, Goto[x], {x, p}]]

In[2257]:= Sv1[10000, a*Sin[b] + c*Cos[d]]

Out[2257]= {c*Cos[d] + a*Sin[b], 10002}

116

Mathematica: Functional and procedural programming

Meanwhile, in order to avoid any misunderstandings, the Label is recommended to be defined as a local variable because the global Label calculated outside of a module will be always acceptable for the module, however calculated in the module body quite can distort calculations outside of the module. At that, multiplicity of occurrences of identical Goto functions into a procedure is a quite naturally and is defined by the realized algorithm while with the corresponding tags Label the similar situation, generally speaking, is inadmissible; in addition, it is not recognized at evaluation of a procedure definition and even at a stage of its performance, often substantially distorting the planned task algorithm. In this case only point of the module body that is marked by the first such Label receives the control. Moreover, it must be kept in mind that lack of a Label[j] for the corresponding call Goto[j] in a block or a module at a stage of evaluation of its definitions isn't recognized, however only at the time of performance with the real appeal to such Goto[j]. Interesting examples illustrating the told can be found in [6-15].

In this connection the GotoLabel procedure can represent a quite certain interest whose call GotoLabel[j] allows to analyse a procedure j on the subject of formal correctness of use of Goto functions and the Label tags corresponding to them. So, the call GotoLabel[j] returns the nested 3–element list whose the first element defines the list of all Goto functions used by a module j, the second element defines the list of all tags (excluding their multiplicity), the third element determines the list whose sublists determine Goto functions with the tags corresponding to them

(in addition, as the first elements of these sub-lists the calls of the Goto function appear, whereas multiplicities of functions and tags remain). The fragment below represents source code of the GotoLabel procedure along with typical examples of its application.

In[7]:= GotoLabel[x_ /; BlockModQ[x]] := Module[{b, d, p, k = 1, c = {{}, {}, {}}, j, h, v = {}, t, a = Flatten[{PureDefinition[x]}][[1]]}, b = ExtrVarsOfStr[a, 1];

b = DeleteDuplicates[Select[b, MemberQ[{"Label", "Goto"}, #] &]]; If[b == {}, c, d = StringPosition[a, Map[" " <> # <> "[" &,

117

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

{"Label", "Goto"}]]; t = StringLength[a];

For[k, k <= Length[d], k++, p = d[[k]]; h = ""; j = p[[2]]; While[j <= t, h = h <> StringTake[a, {j, j}];

If[StringCount[h, "["] == StringCount[h, "]"], AppendTo[v, StringTake[a, {p[[1]] + 1, p[[2]] – 1}] <> h];

Break[]]; j++]]; h = DeleteDuplicates[v]; {Select[h, SuffPref[#, "Goto", 1] &],

Select[h, SuffPref[#, "Label", 1] &], Gather[Sort[v], #1 == StringReplace[#2, "Label[" –> "Goto[", 1] &]}]]

In[8]:= ArtKr[x_ /; IntegerQ[x]] := Module[{prime, agn},

If[PrimeQ[x], Goto[9; prime], If[OddQ[x], Goto[agn], Goto[Sin]]]; Label[9; prime]; Print[x^2]; Goto[Sin]; Print[NextPrime[x]]; Goto[Sin]; Label[9; prime]; Null]

In[9]:= GotoLabel[ArtKr]

Out[9]= {{"Goto[9; prime]", "Goto[agn]", "Goto[Sin]"},

{"Label[9; prime]"}, {{"Goto[9; prime]", "Label[9; prime]", "Label[9; prime]"}, {"Goto[agn]"}, {"Goto[Sin]", "Goto[Sin]", "Goto[Sin]"}}}

In[10]:= Map[GotoLabel, {GotoLabel, TestArgsTypes, CallsInMean}]

Out[10]= {{{}, {}, {}}, {{}, {}, {}}, {{}, {}, {}}}

In[11]:= Map[GotoLabel, {SearchDir, StrDelEnds, OP, BootDrive}]

Out[11]= {{{}, {}, {}}, {{}, {}, {}}, {{"Goto[ArtKr]"}, {"Label[ArtKr]"}, {{"Goto[ArtKr]", "Label[ArtKr]"}}}, {{"Goto[avz]"}, {"Label[avz]"},

{{"Goto[avz]", "Goto[avz]", "Goto[avz]", "Label[avz]"}}}

We will note that existence of the nested list with the third sub-list containing Goto–functions without tags corresponding to them, in the result returned by means call GotoLabel[j] not necessarily speaks about existence of the function calls Goto[j] for which not exists any Label[j] tag. It can be, for example, in the case of generation of a value depending on some condition.

In[320]:= Av[x_Integer, y_Integer, p_ /; MemberQ[{1, 2, 3}, p]] := Module[{}, Goto[p]; Label[1]; Return[x + y]; Label[2]; Return[N[x/y]]; Label[3]; Return[x*y]]

In[321]:= Map[Av[590, 90, #] &, {1, 2, 3, 4}]

Out[321]= {680, 6.55556, 53100, Av[590, 90, 4]} In[322]:= GotoLabel[Av]

Out3[22]= {{"Goto[p]"}, {"Label[1]", "Label[2]", "Label[3]"}, {{"Goto[p]"}, {"Label[1]"}, {"Label[2]"}, {"Label[3]"}}}

118

Mathematica: Functional and procedural programming

For example, according to simple example of the previous fragment the call GotoLabel[Av] contains {"Goto[p]"} in the third sub-list what, at first sight, it would be possible to consider as an impropriety of the corresponding call of Goto function. However, all the matter is that a value of actual p argument in the call Av[x,y,p] and defines a tag really existing in definition of the procedure, i.e. Label[p]. Therefore, the GotoLabel module only at the formal level analyzes existence of the Goto functions, "incorrect" from its point of view with "excess" tags. Whereas refinement of the results received on a basis of a procedure call GotoLabel[W] lies on the user, above all, by means of analysis of accordance of source code of the W to the correctness of the required algorithm.

As a Label may be a correct sentence, for example:

In[9]:= a[x_, y_] := Module[{a, b, c}, If[x/y > 7, Goto[a], Goto[b]];

Label[a[t_] := t^2; a]; c = a[x]; Goto[Fin]; Label[b[t_] := t^3; b]; c = b[y]; Label[Fin]; c]

In[10]:= {a[7, 12], a[77, 7]} Out[10]= {1728, 5929}

The structured paradigm of programming doesn't assume application in programs of the Goto constructions allowing to transfer control from bottom to top. At the same time, in some cases the use of Goto function is rather effective, for example, at needing of embedding into Mathematica of programs which use unconditional transitions on a basis of the goto offer. Thus, Fortran programs can be adduced as a typical example that are very widespread in scientific appendices. From our experience follows, that the use of Goto function allowed significantly to simplify embedding into Mathematica of a number of rather large Fortran programs relating to the engineering and physical applications that very widely use the goto constructions. Right there it should be noted that from our standpoint the function Goto of Mathematica is more preferable, than goto function of Maple in respect of efficiency in the light of application in the procedural programming of various appendices, including also appendices of the system character.

119

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

3.2. Cyclic control structures in Mathematica

One of the basic cyclic structures of Mathematica is based on the For function having the following coding format:

For[start of a loop variable, test, increment, loop body]

Starting from a given initial value of loop variable, the loop body that contains language sentences is cyclically computed, with the loop variable cyclically incremented by an increment until the logical condition (test) remains True. The control words

Continue[] (continuation) and {Break[], Return[]} (termination) respectively are used to control the For–cycle, as the following example well illustrates:

In[21]:= a := {7, 6, c, 8, d, f, g}; b := {a1, d, c1, 77, f1, m, n, t}; In[22]:= For[k = 1, k <= 1000, k++, p = a[[k]]; If[PrimeQ[p],

Break[p], If[IntegerQ[p], Return[], Continue[]]]]

Out[22]= 7

In[23]:= For[k = 1, k <= 1000, k++, p = b[[k]]; If[PrimeQ[p],

Break[p], If[IntegerQ[p], Return[p], Continue[]]]]

Out[23]= Return[77]

In[24]:= For[k = 1, k <= 1000, k++, p = b[[k]]; If[PrimeQ[p],

Break[], If[IntegerQ[p], Print[{k, p}]; Return[], Continue[]]]]

{4, 77} Out[24]= Return[]

In[25]:= A[x___] := Module[{}, For[k = 1, k <= Infinity, k++, p = b[[k]]; If[PrimeQ[p], Break[], If[IntegerQ[p],

Return[p], Continue[]]]]]

In[26]:= A[]

Out[26]= 77

In[27]:= G[x_] := (If[x > 10, Return[x]]; x^2) In[28]:= G[90]

Out[28]= 90

Meanwhile, note in mind that Return[] terminates the loop both outside and inside the procedure or function. In addition, in the first case, the loop ends by Return[], while in the second case the termination of the loop by Return allows by means of the call Return[E] additionally to return a certain E expression,

120

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