HomeMathComputingArtsWordsLiteratureMusictwitter facebook webfeed

A Example of Mathematica's Expressiveness

Advertise Here For Profit

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.

2008-12-10

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;
}
blog comments powered by Disqus