Removed concatenation of nested arrays

This commit is contained in:
josdejong 2013-09-25 21:17:28 +02:00
parent 625fe4a847
commit 0dfdf3b2a2
10 changed files with 128 additions and 130 deletions

View File

@ -2,8 +2,14 @@
https://github.com/josdejong/mathjs
## not yet released, version 0.13.1
## not yet released, version 0.14.0
- Removed concatenation of nested arrays in the expression parser.
You can now input nested arrays like in JavaScript. Matrices can be
concatenated using the function `concat`.
- The matrix syntax `[...]` in the expression parser now creates 1 dimensional
matrices by default. `math.eval('[1,2,3,4]')` returns a matrix with size `[4]`,
`math.eval('[1,2;3,4]')` returns a matrix with size `[2,2]`.
- Fixed non working operator `mod` (modulus operator).

View File

@ -24,7 +24,7 @@ Powerful and easy to use.
Math.js can be installed using npm or bower, or by [downloading](http://mathjs.org/#install_or_download) the library.
The library can be used in both node.js and in the browser.
See the [Getting Start](https://github.com/josdejong/mathjs/blob/master/docs/getting_started.md) for a more detailed tutorial. To install math.js using npm:
See the [Getting Started](https://github.com/josdejong/mathjs/blob/master/docs/getting_started.md) for a more detailed tutorial. To install math.js using npm:
npm install mathjs
@ -62,9 +62,9 @@ math.select(3)
## Documentation
[Getting Started](https://github.com/josdejong/mathjs/blob/master/docs/getting_started.md) •
[Examples](https://github.com/josdejong/mathjs/tree/master/examples/) •
[Documentation](https://github.com/josdejong/mathjs/blob/master/docs/index.md)
- [Getting Started](https://github.com/josdejong/mathjs/blob/master/docs/getting_started.md)
- [Examples](https://github.com/josdejong/mathjs/tree/master/examples/)
- [Documentation](https://github.com/josdejong/mathjs/blob/master/docs/index.md)
## Build

View File

@ -1,6 +1,6 @@
{
"name": "mathjs",
"version": "0.13.1-SNAPSHOT",
"version": "0.14.0-SNAPSHOT",
"main": "./dist/math.js",
"ignore": [
"coverage",

View File

@ -1,6 +1,4 @@
# Overview
The documentation of math.js contains the following pages:
# Math.js Documentation
- [Getting Started](getting_started.md)
- [Constants](constants.md)

View File

@ -41,8 +41,8 @@ parser = math.parser();
parser.eval('a = [1, 2; 3, 4]'); // Matrix, [[1, 2], [3, 4]]
parser.eval('b = zeros(2, 2)'); // Matrix, [[0, 0], [0, 0]]
parser.eval('b(1, 1:2) = [5, 6]'); // Matrix, [[5, 6], [0, 0]]
parser.eval('b(2, :) = [7, 8]'); // Matrix, [[5, 6], [7, 8]]
parser.eval('b(1, 1:2) = [[5, 6]]'); // Matrix, [[5, 6], [0, 0]]
parser.eval('b(2, :) = [[7, 8]]'); // Matrix, [[5, 6], [7, 8]]
parser.eval('c = a * b'); // Matrix, [[19, 22], [43, 50]]
parser.eval('d = c(2, 1)'); // 43
parser.eval('e = c(2, 1:end)'); // Matrix, [[43, 50]]

View File

@ -135,8 +135,8 @@ print(parser.eval('f(2, 3)')); // 8
console.log('\nmanipulate matrices');
print(parser.eval('k = [1, 2; 3, 4]')); // [[1, 2], [3, 4]]
print(parser.eval('l = zeros(2, 2)')); // [[0, 0], [0, 0]]
print(parser.eval('l(1, 1:2) = [5, 6]')); // [[5, 6], [0, 0]]
print(parser.eval('l(2, :) = [7, 8]')); // [[5, 6], [7, 8]]
print(parser.eval('l(1, 1:2) = [[5, 6]]')); // [[5, 6], [0, 0]]
print(parser.eval('l(2, :) = [[7, 8]]')); // [[5, 6], [7, 8]]
print(parser.eval('m = k * l')); // [[19, 22], [43, 50]]
print(parser.eval('n = m(2, 1)')); // 43
print(parser.eval('n = m(:, 1)')); // [[19], [43]]

View File

@ -6,8 +6,8 @@ var Node = require('./Node.js'),
/**
* @constructor MatrixNode
* Holds an 2-dimensional array with nodes
* @param {Array[]} nodes 2 dimensional array with nodes
* Holds an 1-dimensional array with nodes
* @param {Array} nodes 1 dimensional array with nodes
* @extends {Node}
*/
function MatrixNode(nodes) {
@ -22,26 +22,14 @@ MatrixNode.prototype = new Node();
* @override
*/
MatrixNode.prototype.eval = function() {
// evaluate all nodes in the 2d array, and merge the results into a matrix
// evaluate all nodes in the array, and merge the results into a matrix
var nodes = this.nodes,
results = [],
mergeNeeded = false;
results = [];
for (var r = 0, rows = nodes.length; r < rows; r++) {
var nodes_r = nodes[r];
var results_r = [];
for (var c = 0, cols = nodes_r.length; c < cols; c++) {
var results_rc = nodes_r[c].eval();
if (collection.isCollection(results_rc)) {
mergeNeeded = true;
}
results_r[c] = results_rc;
}
results[r] = results_r;
}
if (mergeNeeded) {
results = merge(results);
for (var i = 0, ii = nodes.length; i < ii; i++) {
var node = nodes[i];
var result = node.eval();
results[i] = (result instanceof Matrix) ? result.valueOf() : result;
}
return new Matrix(results);
@ -77,6 +65,7 @@ MatrixNode.prototype.find = function (filter) {
* @param {Array} array Two-dimensional array containing Matrices
* @return {Array} merged The merged array (two-dimensional)
*/
// TODO: cleanup merge function
function merge (array) {
var merged = [];
var rows = array.length;

View File

@ -1046,7 +1046,7 @@ module.exports = function (math) {
* @private
*/
function parseMatrix (scope) {
var array, params, r, c, rows, cols;
var array, params, rows, cols;
if (token == '[') {
// matrix [...]
@ -1057,92 +1057,58 @@ module.exports = function (math) {
getToken();
}
// check if this is an empty matrix "[ ]"
if (token != ']') {
// this is a non-empty matrix
params = [];
r = 0;
c = 0;
var row = parseRow(scope);
params[0] = [parseAssignment(scope)];
if (token == ';') {
// 2 dimensional array
rows = 1;
params = [row];
// the columns in the matrix are separated by commas, and the rows by dot-comma's
while (token == ',' || token == ';') {
if (token == ',') {
c++;
}
else {
r++;
c = 0;
params[r] = [];
// the rows of the matrix are separated by dot-comma's
while (token == ';') {
getToken();
// skip newlines
while (token == '\n') {
getToken();
}
params[rows] = parseRow(scope);
rows++;
// skip newlines
while (token == '\n') {
getToken();
}
}
// skip newlines
if (token != ']') {
throw createSyntaxError('End of matrix ] expected');
}
getToken();
while (token == '\n') {
getToken();
// check if the number of columns matches in all rows
cols = (params.length > 0) ? params[0].length : 0;
for (var r = 1; r < rows; r++) {
if (params[r].length != cols) {
throw createError('Number of columns must match ' +
'(' + params[r].length + ' != ' + cols + ')');
}
}
params[r][c] = parseAssignment(scope);
// skip newlines
while (token == '\n') {
getToken();
array = new MatrixNode(params);
}
else {
// 1 dimensional vector
if (token != ']') {
throw createSyntaxError('End of matrix ] expected');
}
getToken();
array = row;
}
// TODO: spaces as separator for matrix columns
/*
// the columns in the matrix are separated by commas or spaces,
// and the rows by dot-comma's
while (token && token != ']') {
if (token == ';') {
r++;
c = 0;
params[r] = [];
getToken();
}
else if (token == ',') {
c++;
getToken();
}
else {
c++;
}
// skip newlines
while (token == '\n') {
getToken();
}
//TODO: math.eval('[1 -2 3]') is evaluated as '[(1-2) 3]' instead of '[(1) (-2) (3)]'
//TODO: '[(1) (-2) (3)]' doesn't work
params[r][c] = parseAssignment(scope);
// skip newlines
while (token == '\n') {
getToken();
}
}
*/
rows = params.length;
cols = (params.length > 0) ? params[0].length : 0;
// check if the number of columns matches in all rows
for (r = 1; r < rows; r++) {
if (params[r].length != cols) {
throw createError('Number of columns must match ' +
'(' + params[r].length + ' != ' + cols + ')');
}
}
if (token != ']') {
throw createSyntaxError('End of matrix ] expected');
}
getToken();
array = new MatrixNode(params);
}
else {
// this is an empty matrix "[ ]"
@ -1159,6 +1125,36 @@ module.exports = function (math) {
return parseNumber(scope);
}
/**
* Parse a single comma-separated row from a matrix, like 'a, b, c'
* @param {Scope} scope
* @return {MatrixNode} node
*/
function parseRow (scope) {
var params = [parseAssignment(scope)];
var len = 1;
while (token == ',') {
getToken();
// skip newlines
while (token == '\n') {
getToken();
}
// parse expression
params[len] = parseAssignment(scope);
len++;
// skip newlines
while (token == '\n') {
getToken();
}
}
return new MatrixNode(params);
}
/**
* parse a number
* @param {Scope} scope

View File

@ -1,6 +1,6 @@
{
"name": "mathjs",
"version": "0.13.1-SNAPSHOT",
"version": "0.14.0-SNAPSHOT",
"description": "Math.js is an extensive math library for JavaScript and Node.js. It features real and complex numbers, units, matrices, a large set of mathematical functions, and a flexible expression parser.",
"author": "Jos de Jong <wjosdejong@gmail.com>",
"contributors": [

View File

@ -155,7 +155,12 @@ describe('parse', function() {
assert.deepEqual(b.size(), [2,2]);
assert.deepEqual(b, new Matrix([[5,6],[1,1]]));
// from 1 to n dimensions
assert.deepEqual(parseAndEval('[ ]'), new Matrix([]));
assert.deepEqual(parseAndEval('[1,2,3]'), new Matrix([1,2,3]));
assert.deepEqual(parseAndEval('[1;2;3]'), new Matrix([[1],[2],[3]]));
assert.deepEqual(parseAndEval('[[1,2],[3,4]]'), new Matrix([[1,2],[3,4]]));
assert.deepEqual(parseAndEval('[[[1],[2]],[[3],[4]]]'), new Matrix([[[1],[2]],[[3],[4]]]));
});
@ -189,15 +194,15 @@ describe('parse', function() {
assert.deepEqual(parseAndEval('a = []', scope), new Matrix([]));
assert.deepEqual(parseAndEval('a(1,3) = 3', scope), new Matrix([[0,0,3]]));
assert.deepEqual(parseAndEval('a(2,:) = [4,5,6]', scope), new Matrix([[0,0,3],[4,5,6]]));
assert.deepEqual(parseAndEval('a(2,:) = [[4,5,6]]', scope), new Matrix([[0,0,3],[4,5,6]]));
assert.deepEqual(parseAndEval('a = []', scope), new Matrix([]));
assert.deepEqual(parseAndEval('a(3,1) = 3', scope), new Matrix([[0],[0],[3]]));
assert.deepEqual(parseAndEval('a(:,2) = [4;5;6]', scope), new Matrix([[0,4],[0,5],[3,6]]));
assert.deepEqual(parseAndEval('a = []', scope), new Matrix([]));
assert.deepEqual(parseAndEval('a(1,1:3) = [1,2,3]', scope), new Matrix([[1,2,3]]));
assert.deepEqual(parseAndEval('a(2,:) = [4,5,6]', scope), new Matrix([[1,2,3],[4,5,6]]));
assert.deepEqual(parseAndEval('a(1,1:3) = [[1,2,3]]', scope), new Matrix([[1,2,3]]));
assert.deepEqual(parseAndEval('a(2,:) = [[4,5,6]]', scope), new Matrix([[1,2,3],[4,5,6]]));
});
it('should get/set the matrix correctly', function() {
@ -260,29 +265,33 @@ describe('parse', function() {
assert.deepEqual(parseAndEval('a(2:end-1, 2:end-1)', scope), new Matrix([[2,0],[9,9]]));
});
it('should merge nested matrices', function() {
var scope = {};
parseAndEval('a=[1,2;3,4]', scope);
});
it('should parse matrix concatenations', function() {
var scope = {};
parseAndEval('a=[1,2;3,4]', scope);
parseAndEval('b=[5,6;7,8]', scope);
assert.deepEqual(parseAndEval('c=[a,b]', scope), new Matrix([[1,2,5,6],[3,4,7,8]]));
assert.deepEqual(parseAndEval('c=[a;b]', scope), new Matrix([[1,2],[3,4],[5,6],[7,8]]));
assert.deepEqual(parseAndEval('c=[a,b;b,a]', scope), new Matrix([[1,2,5,6],[3,4,7,8],[5,6,1,2],[7,8,3,4]]));
assert.deepEqual(parseAndEval('c=[[1,2]; [3,4]]', scope), new Matrix([[1,2],[3,4]]));
assert.deepEqual(parseAndEval('c=[1; [2;3]]', scope), new Matrix([[1],[2],[3]]));
assert.deepEqual(parseAndEval('c=concat(a,b)', scope), new Matrix([[1,2,5,6],[3,4,7,8]]));
assert.deepEqual(parseAndEval('c=concat(a,b,0)', scope), new Matrix([[1,2],[3,4],[5,6],[7,8]]));
assert.deepEqual(parseAndEval('c=concat(concat(a,b), concat(b,a), 0)', scope), new Matrix([[1,2,5,6],[3,4,7,8],[5,6,1,2],[7,8,3,4]]));
assert.deepEqual(parseAndEval('c=concat([[1,2]], [[3,4]], 0)', scope), new Matrix([[1,2],[3,4]]));
assert.deepEqual(parseAndEval('c=concat([[1]], [2;3], 0)', scope), new Matrix([[1],[2],[3]]));
assert.deepEqual(parseAndEval('d=1:3', scope), [1,2,3]);
assert.deepEqual(parseAndEval('[d,d]', scope), new Matrix([[1,2,3,1,2,3]]));
assert.deepEqual(parseAndEval('[d;d]', scope), new Matrix([[1,2,3],[1,2,3]]));
assert.deepEqual(parseAndEval('concat(d,d)', scope), [1,2,3,1,2,3]);
assert.deepEqual(parseAndEval('e=1+d', scope), [2,3,4]); // e is an Array
assert.deepEqual(parseAndEval('size(e)', scope), [3]);
assert.deepEqual(parseAndEval('[e,e]', scope), new Matrix([[2,3,4,2,3,4]]));
assert.deepEqual(parseAndEval('[e;e]', scope), new Matrix([[2,3,4],[2,3,4]]));
assert.deepEqual(parseAndEval('[[],[]]', scope), new Matrix([[]]));
assert.deepEqual(parseAndEval('[[],[]]', scope).size(), [1, 0]);
assert.deepEqual(parseAndEval('concat(e,e)', scope), [2,3,4,2,3,4]);
assert.deepEqual(parseAndEval('[[],[]]', scope), new Matrix([[],[]]));
assert.deepEqual(parseAndEval('[[],[]]', scope).size(), [2, 0]);
});
it('should throw an error for invalid matrix concatenations', function() {
var scope = {};
assert.throws(function () {parseAndEval('c=[a; [1,2,3] ]', scope)});
assert.throws(function () {parseAndEval('c=concat(a, [1,2,3])', scope)});
});
});
@ -332,7 +341,7 @@ describe('parse', function() {
assert.equal(scope.c, 4.5);
assert.equal(scope.d, 4.5);
assert.equal(scope.e, 4.5);
assert.deepEqual(parseAndEval('a = [1,2,f=3]', scope), new Matrix([[1,2,3]]));
assert.deepEqual(parseAndEval('a = [1,2,f=3]', scope), new Matrix([1,2,3]));
assert.equal(scope.f, 3);
assert.equal(parseAndEval('2 + (g = 3 + 4)', scope), 9);
assert.equal(scope.g, 7);
@ -425,7 +434,7 @@ describe('parse', function() {
assert.equal(parseAndEval('4 ./ 2'), 2);
assert.equal(parseAndEval('8 ./ 2 / 2'), 2);
assert.deepEqual(parseAndEval('[1,2,3] ./ [1,2,3]'), new Matrix([[1,1,1]]));
assert.deepEqual(parseAndEval('[1,2,3] ./ [1,2,3]'), new Matrix([1,1,1]));
});
it('should parse .*', function() {
@ -436,7 +445,7 @@ describe('parse', function() {
approx.deepEqual(parseAndEval('8 .* 2 .* 2'), 32);
assert.deepEqual(parseAndEval('a=3; a.*4'), [12]);
assert.deepEqual(parseAndEval('[1,2,3] .* [1,2,3]'), new Matrix([[1,4,9]]));
assert.deepEqual(parseAndEval('[1,2,3] .* [1,2,3]'), new Matrix([1,4,9]));
});
it('should parse .^', function() {
@ -446,7 +455,7 @@ describe('parse', function() {
approx.deepEqual(parseAndEval('-2.^2'), -4); // -(2^2)
approx.deepEqual(parseAndEval('2.^3.^4'), 2.41785163922926e+24); // 2^(3^4)
assert.deepEqual(parseAndEval('[2,3] .^ [2,3]'), new Matrix([[4,27]]));
assert.deepEqual(parseAndEval('[2,3] .^ [2,3]'), new Matrix([4,27]));
});
it('should parse ==', function() {
@ -640,7 +649,7 @@ describe('parse', function() {
it('should correctly stringify a node tree', function() {
assert.equal(math.parse('0').toString(), 'ans = 0');
assert.equal(math.parse('"hello"').toString(), 'ans = "hello"');
assert.equal(math.parse('[1, 2 + 3i, 4]').toString(), 'ans = [[1, 2 + 3i, 4]]');
assert.equal(math.parse('[1, 2 + 3i, 4]').toString(), 'ans = [1, 2 + 3i, 4]');
});
it('should support custom node handlers', function() {