From 8bc7328997c5fadec672a063185c4e6058c7673a Mon Sep 17 00:00:00 2001 From: dsc Date: Sat, 30 Oct 2010 22:23:56 -0700 Subject: [PATCH] Added new functional tools. --- src/Y/modules/y.op.js | 1 + src/Y/y-collection.js | 10 +-- src/Y/y-function.js | 234 +++++++++++++++++++++++++++++++------------------ src/Y/y-number.js | 45 +++++++--- src/Y/y-op.js | 19 +++- src/portal/shape.js | 64 +++++++++----- 6 files changed, 236 insertions(+), 137 deletions(-) diff --git a/src/Y/modules/y.op.js b/src/Y/modules/y.op.js index 4eed2b2..d8260a4 100644 --- a/src/Y/modules/y.op.js +++ b/src/Y/modules/y.op.js @@ -97,4 +97,5 @@ Y.op = { } }; + })(this.Y); diff --git a/src/Y/y-collection.js b/src/Y/y-collection.js index 4de7996..ee317ee 100644 --- a/src/Y/y-collection.js +++ b/src/Y/y-collection.js @@ -15,11 +15,6 @@ YBase.subclass('YCollection', { 'init' : function(o){ if (!o) return; this._o = o; - // var proto = (o.constructor || {}).prototype || {}; - // for (var k in proto) { - // if (isFunction(proto[k])) - // this[k] = bind.call(proto[k], o); - // } }, 'attr' : function(k, v, def){ @@ -105,9 +100,10 @@ YBase.subclass('YCollection', { }, 'zip' : function(){ - var sequences = new Y(arguments).map(Y).unshift(this); + var sequences = new Y(arguments).map( Y.limit(1) ).unshift(this); return this.map(function(_, k){ - return sequences.invoke('attr', k); + var r = sequences.map( Y.op.get(k) ); + return r; }); }, diff --git a/src/Y/y-function.js b/src/Y/y-function.js index 91447cf..d73707d 100644 --- a/src/Y/y-function.js +++ b/src/Y/y-function.js @@ -1,27 +1,34 @@ +var WR_P = "__wraps__"; + +var _ = globals._ = YFunction._ = {}; + function YFunction(fn){ - fn._o = fn; + if (!fn) + fn = function(){}; + if (fn.__y__) + return fn; + + // fn._o = fn; fn.__y__ = true; return install(fn); } -// YFunction = YCollection.subclass({ -// init : YFunction, -// reduce : reduce, -// attr : attr, -// extend : extend, -// end : function(){ return this; } -// }) - Y.YFunction = YFunction; -YFunction.prototype.end = function(){ return this; }; +Y.extend(YFunction.prototype, { + init : YFunction, + reduce : methodize(Y.reduce), + extend : methodize(Y.extend), + end : function(){ return this; } +}); YFunction.prototype.attr = methodize(Y.attr); -YFunction.prototype.extend = methodize(Y.extend); -YFunction.prototype.reduce = methodize(Y.reduce); +// YFunction.prototype.extend = methodize(Y.extend); +// YFunction.prototype.reduce = methodize(Y.reduce); YFunction.install = install; function install(target){ target = target || Function.prototype; var proto = YFunction.prototype; + for (var k in proto) { if ( isFunction(proto[k]) && (k == 'bind' || !target[k]) ) target[k] = proto[k]; @@ -31,6 +38,51 @@ function install(target){ +function unwrap(fn){ + return ( fn && isFunction(fn) ) ? unwrap(fn[WR_P]) || fn : fn; +} + +Y.curry = curry; +YFunction.prototype.curry = methodize(curry); +function curry(fn){ + if (fn.__curried__) + return fn.apply(this, Y(arguments,1)); + + var args = Y(arguments, 1) + , L = unwrap(fn).length; + + if ( args.length >= L ) + return fn.apply(this, args); + + function curried(){ + var _args = args.concat( Y(arguments) ); + if ( _args.length >= L ) + return fn.apply(this, _args); + else + return curry.apply(this, [fn].concat(_args)); + } + + curried.__wraps__ = fn; + curried.__curried__ = args; + return curried; +} + + +Y.methodize = methodize; +YFunction.prototype.methodize = methodize(methodize); +function methodize(fn) { + if ( fn.__methodized__ ) + return fn.__methodized__; + + var m = fn.__methodized__ = + function(){ + return fn.apply(this, [this].concat( Y(arguments) )); + }; + m[WR_P] = fn; + return m; +} + + Y.compose = compose; YFunction.prototype.compose = methodize(compose); function _composer(x,fn){ return fn.call(this, x); } @@ -60,56 +112,6 @@ function chain(f,g){ return wrapper; } -function splat(fn, x){ - return fn[ isArray(x) ? "apply" : "call"](this, x); -} - -function unwrap(fn){ - return (fn && isFunction(fn)) ? unwrap(fn.__wraps__) || fn : fn; -} - -YFunction.prototype.bind = function(context, args){ - var bound = Y( _Function.prototype.bind.apply(this, arguments) ); - bound.__wraps__ = this; - return bound; -}; - - -// Remembers arguments but obeys current context -YFunction.prototype.partial = partial; -function partial(){ - var fn = this - , args = Y(arguments) - , partially = Y(function(){ - return fn.apply( this, args.concat(arguments) ); - }); - partially.__wraps__ = fn; - return partially; -} - -// Collects arguments but ignores context: fn.curry( arg1, arg2, ... ) -YFunction.prototype.curry = methodize(curry); -function curry(fn){ - if (fn.__curried__) - return fn.apply(this, arguments); - - var args = Y(arguments, 1) - , L = unwrap(fn).length; - - function curried(){ - var self = arguments.callee - , _args = self.__curried__ = args.concat(arguments); - self.__wraps__ = fn; - - if ( _args.length >= L ) - return fn.apply(this, _args); - else - return curried; - } - - return curried(); -} - // YFunction.prototype.lazy = methodize(lazy); // function lazy(fn){ // var args = Y(arguments, 1) @@ -137,41 +139,96 @@ function curry(fn){ // } -Y.methodize = methodize; -YFunction.prototype.methodize = methodize(methodize); // heh -function methodize( fn ) { - if (fn.__methodized) - return fn.__methodized; - - var m = fn.__methodized = - Y(function(){ - var target = (this instanceof YBase) ? this._o : this - , result = fn.apply(target, [target].concat(Y(arguments))); - if (result === target) - return this; - else - return result; - // return fn.apply(this, [this].concat(Y(arguments))); - }); - m.__wraps__ = fn; - return m; +function splat(fn, x){ + return fn[ isArray(x) ? "apply" : "call"](this, x); } +var _bind = _Function.prototype.bind; +YFunction.prototype.bind = function(context, args){ + var bound = _bind.apply(this, arguments); + bound.__wraps__ = this; + return Y(bound); +}; + + +// Remembers arguments but obeys current context +YFunction.prototype.partial = partial; +function partial(){ + var fn = this + , args = Y(arguments) + , partially = function(){ + return fn.apply( this, args.concat(Y(arguments)) ); + }; + partially.__wraps__ = fn; + return Y(partially); +} + + Y.genericize = genericize; YFunction.prototype.genericize = methodize(genericize); // heh function genericize( fn ) { - if (fn.__genericize) - return fn.__genericize; + if (fn.__genericized__) + return fn.__genericized__; - var g = fn.__genericize = - Y(function(){ + var g = fn.__genericized__ = + function(){ var args = Y(arguments), self = args.shift(); return fn.apply(self, args); - }); + }; g.__wraps__ = fn; - return g; + return Y(g); +} + +// Only works for arguments whose toString is unique and stateless (for example, primitives, but not closures). +// XXX: hashCode() +Y.memoize = memoize; +YFunction.prototype.memoize = methodize(memoize); +function memoize(fn){ + var cache = {}; + function memorizer(){ + // I'd like to use toJSON here, but that won't work cross-browser. + var key = Y(arguments).join('\0'); + if ( !(key in cache) ) + cache[key] = fn.apply(this, arguments); + return cache[key]; + } + + memorizer.__wraps__ = fn; + memorizer.__cache__ = cache; + return memorizer; } +// Memorized to reduce eval costs +var +n = 4, +_ofArityWrapper = +YFunction._ofArityWrapper = + memoize(function(n, limit){ + return eval('(function(fn){ '+ + 'return function('+Y.range(n).map( Y.op.add('$') ).join(',')+'){ '+ + 'return fn.apply(this,' + (limit ? 'Y(arguments).slice(0,'+n+')' : 'arguments')+ + '); }; })'); + }); + +YFunction.prototype.aritize = function(n){ + return _ofArityWrapper(n, false)(this); +}; + +YFunction.prototype.limit = function(n){ + return _ofArityWrapper(n, true)(this); +}; + + +/** + * Filter the arguments passed to the wrapper function + */ +YFunction.prototype.mask = mask; +function mask(){ + +} + + + /** Returns the declared name of a function. */ YFunction.prototype.getname = Y.getname = getname; function getname( fn ){ @@ -183,7 +240,6 @@ function getname( fn ){ return fn.className || fn.name || (fn+'').match( /function\s*([^\(]*)\(/ )[1] || ''; } - Y.mixinNames = mixinNames; function mixinNames(o, Donor, names, override, yWrap){ var target = ( isFunction(o) ? o.prototype : o) @@ -202,6 +258,10 @@ function mixinNames(o, Donor, names, override, yWrap){ } +YFunction(Y); Y(Y.reduce); Y(Y.attr); Y(Y.extend); +Y.reduce(YFunction.prototype, function(_,fn,name){ + YFunction(fn); +}); diff --git a/src/Y/y-number.js b/src/Y/y-number.js index 724a8e6..c390bb5 100644 --- a/src/Y/y-number.js +++ b/src/Y/y-number.js @@ -21,20 +21,37 @@ YCollection.subclass('YNumber', { Y.range = range; -function range(start, end){ - // range() --> [] - if ( arguments.length === 0 ) return []; - // range(3) --> range(0,3) - else if ( arguments.length === 1 ) { end = start; start = 0; } - // range(5,2) --> range(1,6).reverse() - if (start > end) { - var r = range(end-1, start+1); - r.reverse(); - return r; - } else { - var i = end - start - 1, r = new Array(i); - while ( i >= 0 ) r[i] = start + i--; - return r; +function range(start, end, step){ + switch (arguments.length) { + // range() -> [] + case 0 : return []; + + // range(3) -> range( 0,3) + // range(-3) -> range(-3,0) + case 1 : + if (start > 0) { + end = start; + start = 0; + } else + end = 0; } + + if (start === end) return []; + + // range(2,5) -> range(2,5, 1) -> [2,3,4] + // range(5,2) -> range(5,2,-1) -> [5,4,3] + if (start < end) + var v = start, L = end, s = 1; + else + var v = end, L = start, s = -1; + + step = step || s; + + var r = []; + while ( v < L ) { + r.push(v); + v += step; + } + return r; } diff --git a/src/Y/y-op.js b/src/Y/y-op.js index 38d05b5..36394e3 100644 --- a/src/Y/y-op.js +++ b/src/Y/y-op.js @@ -48,12 +48,21 @@ Y.op = { nop: function(){}, // accessors - isset: function(o,def){ return o !== undefined ? o : def; }, - has: function(x,y){ return y in x; }, - getkey: function(k,o){ return o[k] }, - getdef: function(o,k,def){ return (k in o ? o[k] : def); }, - set: function(o,k,v){ if (o) o[k] = v; return o; } + isset: function(def,o){ return o !== undefined ? o : def; }, + has: function(k,o){ return k in o; }, + get: function(k,o){ return o[k] }, + getdef: function(def,k,o){ return (k in o ? o[k] : def); }, + set: function(o,k,v){ if (o) o[k] = v; return o; }, + method: function(name){ + var args = Y(arguments,1); + return function(obj){ return obj[name].apply(obj, args); }; + } }; +// Curry all operators +Y.op = Y.reduce(Y.op, function(op, fn, k){ + op[k] = Y(fn).curry(); + return op; +}, {}); diff --git a/src/portal/shape.js b/src/portal/shape.js index e0e3a63..a7b4c95 100644 --- a/src/portal/shape.js +++ b/src/portal/shape.js @@ -57,41 +57,40 @@ Rect = new Y.Class('Rect', Shape, { }); -Triangle = new Y.Class('Triangle', Shape, { - x1: 0, y1: 0, - x2: 0, y2: 0, +Polygon = new Y.Class('Polygon', Shape, { + x0: 0, y0:0, _offsetX : 0, // We need our triangle to be in the first quadrant _offsetY : 0, // so we'll have to offset any negative shapes at the position call - init : function(x1,y1, x2,y2){ + /** + * Expects two arrays of coordinate-halfs, which could be zipped + * together to make the numbered coordinates. + * x0 and y0 will always be 0. + */ + init : function(xs, ys){ Layer.init.call(this); - var minX = Math.min(0, x1, x2); - if (minX < 0) { - var offX = this._offsetX = -minX; - x1 += offX; x2 += offX; - } + var xs = this._calcOffset('x', xs) + , ys = this._calcOffset('y', ys) ; - var minY = Math.min(0, y1, y2); - if (minY < 0) { - var offY = this._offsetY = -minY; - Y1 += offY; y2 += offY; - } + this.points = Y(xs).zip(ys); - this.x1 = x1; this.y1 = y1; - this.x2 = x2; this.y2 = y2; - this.width( Math.max(0, x1, x2) ); - this.height( Math.max(0, y1, y2) ); + this.width( Math.max.apply(Math, xs) ); + this.height( Math.max.apply(Math, ys) ); }, _calcOffset : function(which, values){ - var min = Math.min(0, Math.min.apply(Math, values)); + values.unshift(0); + var self = this + , off = -1 * Math.min.apply(Math, values); - if (min < 0) { - var off = this['_offset'+which] = -min; - x1 += off; x2 += off; - } - return + self['_offset'+which.toUpperCase()] = off; + self[which+'s'] = values = values.map(function(v, i){ + v += (i === 0 ? 0 : off); + self[which+i] = v + return v; + }); + return values; }, position : function(left, top){ @@ -110,6 +109,23 @@ Triangle = new Y.Class('Triangle', Shape, { return this; }, + drawShape : function(ctx){ + var self = this; + + } +}); + +Triangle = new Y.Class('Triangle', Polygon, { + x1: 0, y1: 0, + x2: 0, y2: 0, + + init : function (x1,y1, x2,y2){ + Polygon.init.call(this, [x1,x2], [y1,y2]); + }, + + drawShape : function(ctx){ + + } }); Ellipse = new Y.Class('Ellipse', Shape, { -- 1.7.0.4