Xah Lee, 2008-12-05
Here's a explanation of some high powered construct of Mathematica.
Let's say for example, we want to write a function that takes a vector (of linear algebra), and return a vector in the same direction but with length 1. In linear algebar terminology, the new vector is called the “normalized” vector of the original.
Let's say we are working with 3-dimensional vectors. This means, we want a function whose input is a list of 3 elements say {x,y,z}, and output is also a list of 3 elements, say {a,b,c}, with the condition that:
a = x/Sqrt[x^2+y^2+z^2] b = y/Sqrt[x^2+y^2+z^2] c = z/Sqrt[x^2+y^2+z^2]
In Mathematica, this can be defined like this:
normalize=Function[#/Sqrt@(Plus@@(#^2))]
The beauty of this line is that it works for vectors of any dimention, i.e. list of any length.
Here i explain how it is so succinct.
Mathematica's syntax support what's called FullForm, which is basically a fully nested notation like lisp's. In fact, the Mathematica compiler works with FullForm. The FullForm is not something internal. A programer can type his code that way if he so pleases.
In FullForm, the above expression is this:
Set[ normalize, Function[ Times[Slot[1], Power[ Sqrt[ Apply[ Plus, Power[ Slot[1], 2 ] ] ], -1 ] ] ]
Now, in normalize=Function[#/Sqrt@(Plus@@(#^2))], the “Function” is your lisper's “lambda”. The # is the formal parameter. We assign a pure function to the variable “normalize”. Note that the formal parameter # does not just represent a number, but can be any expression, including a list of the form 「{x,y,z}」. So, we see here that math operations are applied to list entities directly. For example, in Mathematica, {3,4,5}/2 returns {3/2,2,5/2} and {3,4,5}^2 returns
{9,16,25}.
In typical lang such as python, including lisp, you would have to map the operation into each lisp elements instead.
The Sqrt@… is a syntax shortcut for Sqrt[…], and the Plus@@… is a syntax shortcut for Apply[Plus, …], which is lisp's “funcall”. So, taking the above all together, the code for “normalize” given above is _syntactically equivalent_ to this:
normalize=Function[ #/Sqrt[ Apply[Plus, #^2] ]]
the above means: square the vector, add them together, take the square root, then have the original vector divide it.
The # is in fact a syntax shortcut for Slot[1], meaning the first
formal parameter. The = is in fact a syntax shortcut for Set[].
The ^ is a shortcut for Power[], and the / in a/b is a shortcut for
Times[a,Power[b, -1]]. Putting all these together, you can see how the code is
syntactically equivalent to the above nested FullForm.
In lisp, python, perl, etc, you'll have 5 or so lines. In C or Java, 20.
Note: Normalize function is now a built-in function in Mathematica v6, released sometimes in 2007. See: http://reference.wolfram.com/mathematica/ref/Normalize.html.
Thanks to “comp.lang.*” newsgroups, various people have given code for different languages.
-- Haskell. By the.brown.dragon…@gmail.com let normalize vec = map (/ (sqrt $ sum (map (^2) vec))) vec in normalize [3,4]
# Ruby 1.8.7. By w_a_x…@yahoo.com def normalize vec s = Math.sqrt(vec.map{|x|x*x}.inject{|x,y|x+y}) vec.map{|x| x/s} end p normalize([3,4,5,6]) # [0.323498319610315, 0.431331092813754, 0.539163866017192, 0.64699663922063]
# Python 2.6.5 By Roberto Bonvallet def normalize(v): return (lambda norm: [x/norm for x in v])(sum(x**2 for x in v) **0.5) print normalize([3,4,5,6]) # [0.32349831961031522, 0.43133109281375365, 0.53916386601719202, 0.64699663922063044]
# Perl v5.10.1. By Jim Gibson sub normalize { my $sum = 0; $sum += $_ ** 2 for @_; my $length = sqrt($sum); return map { $_ / $length } @_; } print normalize(3,4,5,6); # 0.3234983196103150.4313310928137540.5391638660171920.64699663922063
;; Scheme Lisp. (scsh 0.6.7 (R6RS)) By Bakul Shah (define (normalize vec) (let ((d (sqrt (apply + (map (lambda (x) (* x x)) vec))))) (map (lambda (x) (/ x d)) vec))) (normalize '(3 4 5 6)) ;; '(0.323498 0.431331 0.539164 0.646997)
// Javascript. By Gerard Flanagan var map = function(fn, v) { var b = new Array(v.length); for (i = 0; i < v.length; i++) { b[i] = fn(v[i]); } return b }; var reduce = function(fn, v, init) { var s = init; for (i = 0; i < v.length; i++) { s = fn(s, v[i]); } return s }; var sum = function(v) { return reduce(function(x, y) { return x + y }, v, 0.0) }; var vectorLength = function(v) { var pow = Math.pow; return Math.sqrt(sum(map(function(x) { return pow(x, 2) }, v))) }; var normalize = function(vec) { var N = vectorLength(vec); return map(function(x) { return x/N }, vec) } print(normalize([3,4]));
// Java. By John W Kennedy. Class wrapper and printing code added by Xah Lee. class Test { static float[] normalize(final float[] vec) { float sum = 0.0f; for (int i = 0; i < vec.length; ++i) sum += vec[i] * vec[i]; final float divisor = (float) Math.sqrt(sum); float[] a = new float[vec.length]; for (int i = 0; i < vec.length; ++i) a[i] = vec[i]/divisor; return a; } public static void main(String[] args) { float[] inputVector = {3,4}; float[] resultVector = normalize(inputVector); for (int i = 0; i < resultVector.length; i++) { System.out.print(resultVector[i] + " "); } } }
The following javascript program is not qualified. It is syntax error in SpiderMonkey engine “JavaScript-C 1.7.0 2007-10-03”.
// Javascript. By William James function normalize( vec ) { var div=Math.sqrt(vec.map(function(x) x*x).reduce(function(a,b) a+b)) return vec.map(function(x) x/div) }
The following C code is not qualified. It needs to be a full stand alone program that takes vector of any dimention and prints the result. Due to C's low-level nature, the program perhaps needs include code to parse command line input of arbitrary vector such as a list of numbers separated by space, and print the result in output.
// C. By John W Kennedy #include <stdlib.h> #include <math.h> void normalize(int dim, float* x, float* a) { float sum = 0.0f; int i; float divisor; for (i = 0; i < dim; ++i) sum += x[i] * x[i]; divisor = sqrt(sum); for (i = 0; i < dim; ++i) a[i] = x[i]/divisor; }