Notes to the Core Language of Mathematica II (List)
Lisp
As we have noted before, Mathematica is similar to Lisp. So let us talk about some characters of Lisp before we discuss the Mathematica.
Actually, Lisp is the abbreviation for list processing. List is an object take the form of:
1 | (item1,item2,item3) |
The list is also named as symbolic expression. The target of the
interpreter of Lisp is to get the value of each symbol expression passed
to it. The compiler takes item1
as the name of the function
and item2
and item3
as variables. The method
was invented by Jan Łukasiewicz from Poland, so it is named as Polish
notation. Despite its difficulties for human to understand, it is
convenient for the computer to cope with. When we compile a program, we
need to convert the source code to abstract syntax tree. So the way we
write Lisp is just writing a syntax tree.
In Mathematica, the function Treeform
can get a
expression's syntax tree.
1 | TreeForm[(a + b^n)/z == x] |
Have a try, and some surprises are waiting for you. If we want to write the expression above in Lisp, then it would take the form of
1 | (==,(*,(+,a,(expt,b,n)),(expt,z,-1)),x) |
Actually, the kernel of Mathematica understand the expression in the same way.
1 | In:FullForm[(a + b^n)/z == x] |
The the similarity is very conspicuous.
Everything is An Expression
==Everything is An Expression== is a philosophy that both Mathematica and Lisp follow (actually, it is more proper to say Mathematica get the idea from Lisp). Now let's review the definition of expression:
- An atom is an expression;
- If
are expressions, is a expression.
Then we go on to illustrate why we say everything is an expression.
1 | In: Map[FullForm,{a+b,a-b,a*b,a/b,a^b,a==b,a!=b,a<b,a<=b,a>b,a>=b,a&&b,a||b} ] |
Though, expressions like a+b
,a-b
and
a!=b
is not take the form of Map[f,expr]
is equivalent to f\@ expr
.
Computation is Rewriting
We could find that the first principle only care about the method we use to denote the object to be computed. It doesn't take the computation itself into consideration. ==Lisp is not good at computation==, and that's why Wolfram unsatisfied with Lisp. Mathematica got its way of computation from other functional programming languages like Haskell and OCaml.
In order to understand that, let's recall how we human perform
computation. Just take a integrate as an example,
Function Matters
There is no rewriting system in Lisp, and Haskell and OCaml don't take everything as list, but we still classify them into one category which is so-called functional language. Why we do that?
In Lisp, we could add a '
to prevent a list get its'
value, and the we take the list as some kind of data which is
manipulative. When we combine some data to a List, we could use
(eval L)
to force the list get its value. Then the data L
be converted to executable code again. We call Code-as-Data as the
philosophy of Lisp.
In Haskell, we take every category as a set. Functions between
categories are just the map between sets. It's easy to combine .
.
What's more, all the map from
The character is so important that we call it Mathematica's zeroth principle: Function Matters, not variables.
Expression and List
Brief Structure of An Expression or List
A expression is defined as an atom or a function like
Given a expression
1 | In:Head /@ {1,1/2,True,"Number",a+b,a-b,a*b,a/b,(f+g)[x1,x2,x3]} |
Note:f\@expr
is Map[f,expr]
We could find that for atom, a symbol's head is always
Symbol
, a number's head depends on its type and could be
Integer
, Rational
, Real
,
Complex
, a string's head is always String
,
etc. We could decide whether a expression is an atom in this manner.
1 | myAtomQ = |
However, sometimes we need to deal with the parameters of a
expression. Then we take them out, but what we get turns out to be only
a sequence compose of several expressions, and there is no head with
them. But all of expressions of Mathematica must have a head. In order
to deal with a expression without head, Mathematica introduce list and
all of expressions don't have a head have a head of List. The case
bellow shows that when we apply List
to a expression, it
become a list
.
1 | In: ex = f[x1, x2, x3]; |
List itself is also an inner function of Mathematica, and it could turn a sequence of expressions into as list. eg
1 | In:List[1,2,3] |
@@
, whose full name Apply
, is also a
functional operation and it change a expression's head. eg
1 | In:Apply[g,h[x1,x2,x3]] |
There is also another type of list which is so-called
Sequence
. We could take sequence as a list without
{}
in both sides. Or we could say, list is turn a sequence
into an object while sequence is something more basic.
1 | In: ex = h[1, 2, 3] |
And when we want to convey the parameters of one expression to
another expression, we can't get what we want because of the
{}
. Therefore we need to change the head of that expression
to Sequence
.
1 | In: f[seq] |
Operations on Lists
Apart from Head
and Apply
, we could use
the built-in function Part
to visit expression inside
compound expressions. It takes the form of
Part[expr,number]
or expr[[number]]
and 0 is
the head.
1 | In: ex = f[x1, x2, x3]; |
As we have discussed before, the type checking of Mathematica is not
so strict and as a result of there is no ex[[4]]
,
Mathematica return us a result of f[x1, x2, x3][[4]]
. It's
often the case that when Mathematica doesn't know what to do with our
command, it would return what it get to us.
And for nested expressions, we can take Part
more than
one time.
1 | In: ex = f[a, g[b, c], h[d, k[e, i], j]]; |
The operation could also write as ex[[3, 2, 2]]
. There
are many other ways to use Part
, and we show this by four
examples
1 | In: ex[[-1, -2, -1]] |
There are also some built-in functions for some Parts which are often used.
1 | In: Function[op, op[f[x1, x2, x3, x4]]] /@ {First, Last, Rest, Most} |
For a given expression, there are two values matter. i.e. depth and length.
1 | In: Length[f[g[x1, h[x2, x3]], x4]] |
Construct a List
Like many other language, Range could be used to build a simple list.
1 | Range[begin,end,step] |
Only end
is a must to this function. And
Table
, Array
, Turples
and
Outer
can be used to build a list too.
1 | In: Table[i^2 + i + 1, {i, 10}] |
Tuples[List,n]
means use members of the list to
construct lists of n numbers and combine all possible answer to one
list.
1 | In: Tuples[Range[3], 3] |
Outer[f,list1,list2]
group all the possible combinations
of members of list1
and list2
and use them as
the independent variables of f
.
1 | In: Outer[f, Range[3], Range[2]] |
Search and Sort
MemberQ[expr,i]
is used to judge whether i
is a member of expr
, while FreeQ[expr,i]
is
used to judge whether expr
has a sub-expression which
matches i
. Both of them will return False
,
when i
is the head of the expr
.
1 | In: ex = f[x1, x2, x3, x4]; |
And we could appoint the depth of searching for them.
Count[expr,i]
is used to calculate how many times
i
appear in expr
, appoint depth is also
powered.
1 | In: euler = (a + b^n)/n == x; |
Use n
means searching from 0th layer to nth layer, while
use {n}
means only search nth layer.
1 | In: Position[euler, n] |
And we could use Select
to choose members by
self-defined rules.
1 | In: Select[Prime /@ Range[10], OddQ] |