The SelectingElements Package
By Ted Ersek
      ersektr@navair.navy.mil

Installation of the package can be achieved by making an "Enhancements" folder under  
…\AddOns\ExtraPackages. The source code for the package is in the file (SelectingElements.m) which should be copied to the folder …\AddOns\ExtraPackages\Enhancements.  Once that file is copied to the right folder the package can be loaded by evaluating the next cell.

<< Enhancements`SelectingElements`

The next cell defines expressions which are needed in many examples that follow.

ClearAll[h];
expr1 = 
        h[4.5, 2, 2.5, 8.5, 3, 2.3, 4, 2, 7, 4.7, 9.5, 3, 6, 4.2, 2.3, 2.2, 
          10.5, 7, 4.9, 11.5, 12.5, 4.7];
expr2 = 
        1 + a + b + c + 
          d + (((Sqrt[a + c]) + a + b + c + d)/(1 + a + b + c + d));
expr3 = 
        HoldForm[
          2/3 + 12/4 + 
            77/11 + ((x + 12/4 + 77/11 + (Sqrt[y + 12/4 + 77/11]))/(12/4 + 77/11))];

Cases

After the package is loaded Cases can still do anything it normally does. The next line returns a list of all Integers in (expr1) whether the package was loaded or not.

Cases[expr1, _Integer]

Examples of Cases that follow only work after the package is loaded.  The next line returns the last 4 Integers in (expr1).

Cases[expr1, _Integer, 1, -4]

When the fourth argument in Cases is {5} a list containing the fifth element matching the pattern is returned.

Cases[expr1, _Integer, 1, {5}]

When the fourth argument is {3,6} the (3^rd) through the (6^th) elements matching the pattern are returned.

Cases[expr1, _Integer, 1, {3, 6}]

Since (expr1) only has 8 integers, the (5^th) through the (8^th) elements matching the pattern are returned when the fourth argument is {5,100}.

Cases[expr1, _Integer, 1, {5, 100}]

The enhancements above also work with any valid pattern or level specification, or with (pattern->rhs) for a second argument.  I don't demonstrate this point, but when the fourth argument of Cases is a list of integers, the list can include negative integers.  The same thing applies to the use of negative integers in a list under Select, Position or StringPosition.

Select

The built in version of Select gives the same result in the next example.  Here we get all arguments with a magnitude greater than 5.

Select[expr1, # > 5 &]

The other examples of Select will only work after loading the package.  Since the third argument in the next example is -3, Select returns the last three arguments meeting the criteria ( #  > 5 &).

Select[expr1, # > 5 &, -3]

When the third argument of Select is {4} we only get the fourth argument meeting the criteria ( #  > 5 &).

Select[expr1, # > 5 &, {4}]

When the third argument of Select is {3,5} we get the (3^rd), (4^th), (5^th) arguments meeting the criteria ( # > 5 &).

Select[expr1, # > 5 &, {3, 5}]

When the third argument of Select is {3,100} we get the (3^rd) through (100^th)arguments meeting the criteria ( # > 5 &). In this case we have fewer than 100 arguments meeting the criteria so we get all elements starting with the (5^th) meeting the criteria.

Select[expr1, # > 5 &, {5, 100}]

Position

The built in version of Position gives the same result for the next example.  Here we get the position of all Integers in expr1.

Position[expr1, _Integer]

The other examples of Position will only work after loading the package.  In the next example we get the positions of the last 4 integers at level 1 in expr1.

Position[expr1, _Integer, 1, -4]

In the next example we get the position of the (5^th) Integer at level 1.

Position[expr1, _Integer, 1, {5}]

In the next example we get the positions of the (3^rd), (4^th), (5^th)  Integers at level 1 in expr1.

Position[expr1, _Integer, 1, {3, 5}]

In the last example of Position we would get the positions of the (5^th) through (100^th) Integers at level 1 in expr1.  However, expr1 has fewer than 100 Integers so we get the position of all Integers starting with the (5^th).

Position[expr1, _Integer, 1, {5, 100}]

Extended Positions

Dave Park noted that an extension to the usual notion of position is needed and Dave suggested the notation eP[list1,list2] for an 'extended position' and eP is given the usage message in the next cell.

?eP

Position is enhanced to check for extended positions when it is used with two or three arguments provided negative numbers are not used in the level specification. This is demonstrated in the next example where the position of all occurrences of (a+c) are returned. We say (a+c) is at position eP[{6,2},{1,3}] and eP[{},{2,4}] to indicate that (a+c) is returned by evaluating  Part[Extract[expr2,{6,2}],{1,3}], and  Part[expr2,{2,4}].

expr2
Position[expr2, a + c]

Position can give an extended position of an expression that must not evaluate as in the next cell.  Getting this to work required very careful programming.

expr3
posn = Position[expr3, Unevaluated[12/4 + 77/11]]

In the next example the level specification (2) is used and Position doesn't look as deep as it did in the last example.

expr3
Position[expr3, Unevaluated[12/4 + 77/11], 2]

In the next example I use Block to ensure $ExtendPosition  is not True and Position only looks for regular positions.

?$ExtendPosition

expr3
\(Block[{$ExtendPosition}, 

      Position[expr3, 
        Unevaluated[12/4 + 77/11]]
]\)

StringPosition

The next cell defines a character string named string1. This string is needed for the examples of StringPosition.

string1 = "aa aaadsaa aaadsdsa aaa adsadsaa aadsaa adsaa adsaa \
aadsaa aadsaa";

The built-in version of StringPosition will give the same result in the next example.  Here we get the string position of each occurrence of "ds" in string1.

StringPosition[string1, "ds"]

The remaining examples of StringPosition only work after the package is loaded.  The next line returns the string position of the last three occurrences of "ds".

StringPosition[string1, "ds", -3]

Next we get the string position of only the third occurrence of "ds".

StringPosition[string1, "ds", {3}]

The next line gives the string positions of the (4^th) through (8^th) occurrences of "ds".

StringPosition[string1, "ds", {4, 8}]

MapAt

In the next example MapAt, maps a function to two positions where one of the positions is an extended position.

expr3
MapAt[f1, expr3, {{1, 4, 2, 1}, eP[{1, 4, 1}, {2, 3}]}]

ReplacePart

In the next example ReplacePart, replaces expressions at two positions where one of the positions is an extended position.

expr3
ReplacePart[expr3, new, {eP[{1}, {2, 3}], {1, 4, 1, 3}}]

Extract

Extract is enhanced so it can extract a subexpression at an extended position.

expr3
Extract[expr3, eP[{1}, {2, 3}], HoldForm]

PositionMin, PositionMax

PositionMin and PositionMax are new functions defined in the package. The usage message of PositionMin is shown below, and PositionMax is similar to PositionMin.

?PositionMin

The next cell creates a list of complex numbers which can be used to demonstrate PositionMin and PositionMax.

SeedRandom[5];
IntList = {5, 6, 7, 5, 7, 2, 2, 7, 2, 6, 7, 2, 3, 2, 2, 
          7} ;
ImagList = 
        Table[Exp[I Random[Real, {-π, π}]], {16}];
expr4 = IntList ImagList

In the cell below PositionMin returns the positions of elements of expr4 which have the smallest magnitude.  Extract is then used to get those elements of expr4.  Next we see each of the elements with smallest magnitude has a magnitude of 2.

PositionMin and PositionMax are very versatile since any function that evaluates to a numeric value with no imaginary part can be used to determine which elements are "smallest" or "largest".  In addition the list of positions returned can  be used directly in Extract, ReplacePart, MapAt, Insert, Delete, FlattenAt.

Extract[expr4, PositionMin[expr4, Abs]]

Abs /@ %

Below PositionMax and Extract are used to get a list of elements of expr4 which have the largest magnitude.  Then we see each of these elements has an absolute value of 7.

Extract[expr4, PositionMax[expr4, Abs]]

Abs /@ %

EvaluateAt

EvaluateAt is a new function defined in the package and it's usage message is shown below.  The core of this function was written by Allan Hayes.  If a very long list of positions is provided it takes a long time to evaluate, since it is O[n^2].  A more efficient definition with O[n] complexity can be written, but the more efficient definition would not evaluate the subexpressions in the proper order.

?EvaluateAt

The held expression (expr5) is defined in the next cell and will be used to demonstrate the EvaluateAt function.

expr5 =
      HoldForm[(Erf[Infinity] + 5)*Sin[Pi/(1 + 3)]*
          Sqrt[5 + 4]/(Sqrt[36] - Sqrt[16])]

In the next line we evaluate expr5 at positions {1,3,1,1}, {1,3,2,1}. In this case the subexpressions at those positions are (Sqrt[36]) - (Sqrt[16]), and (5 + 4) respectively.

expr5
EvaluateAt[expr5, {{1, 3, 1, 1}, {1, 3, 2, 1}}]

The next line evaluates expr5 at positions {1,3,1,1}, {1,3,2,1} and adds x to each result.

expr5
EvaluateAt[expr5, {{1, 3, 1, 1}, {1, 3, 2, 1}}, # + x &]

In the next example two subexpressions are evaluated and one of them is an extended position.

expr5
EvaluateAt[
      expr3, {eP[{1, 4, 1}, {2, 3}], {1, 4, 1, 4, 1, 3}}]

EvaluateAtPattern

EvaluateAt is another new function defined in the package and it's usage message is shown below.  The core of this function was also written by Allan Hayes.  Similar to EvaluateAt this function takes a long time when it has to evaluate many subexpressions.  In fact EvaluateAtPattern has O[n^2] complexity.  A more efficient definition with O[n] complexity can be written, but the more efficient definition would not evaluate the subexpressions in the proper order.

?EvaluateAtPattern

The expression expr5, defined below, is the same example used to demonstrate EvaluateAt.

expr5 = 
      HoldForm[(Erf[Infinity] + 5)*Sin[Pi/(1 + 3)]*
          Sqrt[5 + 4]/(Sqrt[36] - Sqrt[16])]

In the first example all subexpressions of expr5 with the head Plus are evaluated.

expr5
EvaluateAtPattern[expr5, _Plus]

In the next example all subexpressions with the head Plus are evaluated and x is added to the result of that evaluation.

expr5
EvaluateAtPattern[expr5, _Plus, # + x &]

EvaluateAtPattern also allows for fourth and fifth arguments which is seldom needed.  The fourth argument is for a level specification. This is expr1nstated in the next line where all subexpressions at level (-2) are evaluated.  Since the second argument is _ the subexpressions don't have to match a certain pattern.  Since the third argument is Identity nothing happens to the subexpressions after evaluation.

expr5
EvaluateAtPattern[expr5, _, Identity, {-2}]

The fifth argument in EvaluateAtPattern specifies which occurrences of a pattern match at the specified level should be evaluated.  A fifth argument of n indicates the first n occurrences should evaluate, and -n indicates the last n occurrences should evaluate.  A fifth argument of {n} or {-n} indicates only the (n^th) or  (n^th) from the last occurrence should evaluate.  Finally when the fifth argument is {m, n} occurrences m through n are evaluated.

In the next line only the second subexpression with the head Plus is evaluated.  That subexpression is 1+3.

expr5
EvaluateAtPattern[expr5, _Plus, Identity, ∞, {2}]

Other Possibilities

Built-in functions Dimensions, MapThread, Operate, StringReplacePart could be enhanced so the second argument can be a negative integer or list of one or two integers. When the second argument is a list of one or two integers, one or both could be negative.