Structural Operations

The Wolfram System contains some powerful primitives for making structural changes to expressions. You can use these primitives both to implement mathematical properties such as associativity and distributivity, and to provide the basis for some succinct and efficient programs.

Here we describe various operations that you can explicitly perform on expressions. "Attributes" describes how some of these operations can be performed automatically on all expressions with a particular head by assigning appropriate attributes to that head.

You can use the Wolfram System function (RefLink[Sort, paclet:ref/Sort])[expr] to sort elements not only of lists, but of expressions with any head. In this way, you can implement the mathematical properties of commutativity or symmetry for arbitrary functions.

You can use (RefLink[Sort, paclet:ref/Sort]) to put the arguments of any function into a standard order:

Sort[f[c,a,b]]

Sorting into order.

The second argument to (RefLink[Sort, paclet:ref/Sort]) is a function used to determine whether pairs are in order. This sorts numbers into descending order:

Sort[{5,1,8,2},(#2<#1)&]

This sorting criterion puts elements that do not depend on x before those that do:

Sort[{x^2,y,x+y,y-2},FreeQ[#1,x]&]

Flattening out expressions.

(RefLink[Flatten, paclet:ref/Flatten]) removes nested occurrences of a function:

Flatten[f[a,f[b,c],f[f[d]]]]

You can use (RefLink[Flatten, paclet:ref/Flatten]) to "splice" sequences of elements into lists or other expressions:

Flatten[{a,f[b,c],f[a,b,d]},1,f]

You can use (RefLink[Flatten, paclet:ref/Flatten]) to implement the mathematical property of associativity. The function (RefLink[Distribute, paclet:ref/Distribute]) allows you to implement properties such as distributivity and linearity.

Applying distributive laws.

This "distributes" f over a+b:

Distribute[f[a+b]]

Here is a more complicated example:

Distribute[f[a+b,c+d]]

In general, if f is distributive over (RefLink[Plus, paclet:ref/Plus]), then an expression like f[a+b] can be "expanded" to give f[a]+f[b]. The function (RefLink[Expand, paclet:ref/Expand]) does this kind of expansion for standard algebraic operators such as (RefLink[Times, paclet:ref/Times]). (RefLink[Distribute, paclet:ref/Distribute]) allows you to perform the same kind of expansion for arbitrary operators.

(RefLink[Expand, paclet:ref/Expand]) uses the distributivity of (RefLink[Times, paclet:ref/Times]) over (RefLink[Plus, paclet:ref/Plus]) to perform algebraic expansions:

Expand[(a+b) (c+d)]

This applies distributivity over lists, rather than sums. The result contains all possible pairs of arguments:

Distribute[f[{a,b},{c,d}],List]

This distributes over lists, but does so only if the head of the whole expression is f:

Distribute[f[{a,b},{c,d}],List,f]

This distributes over lists, making sure that the head of the whole expression is f. In the result, it uses gp in place of (RefLink[List, paclet:ref/List]), and fp in place of f:

Distribute[f[{a,b},{c,d}],List,f,gp,fp]

Related to (RefLink[Distribute, paclet:ref/Distribute]) is the function (RefLink[Thread, paclet:ref/Thread]). What (RefLink[Thread, paclet:ref/Thread]) effectively does is to apply a function in parallel to all the elements of a list or other expression.

Functions for threading expressions.

Here is a function whose arguments are lists:

f[{a1,a2},{b1,b2}]

(RefLink[Thread, paclet:ref/Thread]) applies the function "in parallel" to each element of the lists:

Thread[%]

Arguments that are not lists get repeated:

Thread[f[{a1,a2},{b1,b2},c,d]]

As mentioned in "Collecting Objects Together", and discussed in more detail in "Attributes", many built-in Wolfram System functions have the property of being "listable", so that they are automatically threaded over any lists that appear as arguments.

Built-in mathematical functions such as (RefLink[Log, paclet:ref/Log]) are listable, so that they are automatically threaded over lists:

Log[{a,b,c}]

(RefLink[Log, paclet:ref/Log]) is, however, not automatically threaded over equations:

Log[x==y]

You can use (RefLink[Thread, paclet:ref/Thread]) to get functions applied to both sides of an equation:

Thread[%,Equal]

Generalized outer and inner products.

(RefLink[Outer, paclet:ref/Outer])[f,(Subscript[list, 1]),(Subscript[list, 2])] takes all possible combinations of elements from (Subscript[list, 1]) and (Subscript[list, 2]), and combines them with f. (RefLink[Outer, paclet:ref/Outer]) can be viewed as a generalization of a Cartesian product for tensors, as discussed in "Tensors".

(RefLink[Outer, paclet:ref/Outer]) forms all possible combinations of elements, and applies f to them:

Outer[f,{a,b},{1,2,3}]

Here (RefLink[Outer, paclet:ref/Outer]) produces a lower-triangular Boolean matrix:

Outer[Greater,{1,2,3},{1,2,3}]

You can use (RefLink[Outer, paclet:ref/Outer]) on any sequence of expressions with the same head:

Outer[g,f[a,b],f[c,d]]

(RefLink[Outer, paclet:ref/Outer]), like (RefLink[Distribute, paclet:ref/Distribute]), constructs all possible combinations of elements. On the other hand, (RefLink[Inner, paclet:ref/Inner]), like (RefLink[Thread, paclet:ref/Thread]), constructs only combinations of elements that have corresponding positions in the expressions it acts on.

Here is a structure built by (RefLink[Inner, paclet:ref/Inner]):

Inner[f,{a,b},{c,d},g]

(RefLink[Inner, paclet:ref/Inner]) is a generalization of (RefLink[Dot, paclet:ref/Dot]):

Inner[Times,{a,b},{c,d},Plus]