mirror of
				https://github.com/flynx/walk.js.git
				synced 2025-10-31 03:50:14 +00:00 
			
		
		
		
	added a walk done handler + updated docs...
Signed-off-by: Alex A. Naanou <alex.nanou@gmail.com>
This commit is contained in:
		
							parent
							
								
									7df2a6b4d9
								
							
						
					
					
						commit
						1f6b2a0f69
					
				
							
								
								
									
										86
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										86
									
								
								README.md
									
									
									
									
									
								
							| @ -3,22 +3,36 @@ | ||||
| An extensible tree walk(..) framework... | ||||
| 
 | ||||
| 
 | ||||
| - [walk.js](#walkjs) | ||||
| 	- [Theory and operation](#theory-and-operation) | ||||
| 		- [Constructing the walker and walking ( walk(..) )](#constructing-the-walker-and-walking--walk) | ||||
| 		- [Getting and processing nodes ( getter(..) )](#getting-and-processing-nodes--getter) | ||||
| 		- [Putting it all together](#putting-it-all-together) | ||||
| 	- [Installation and loading](#installation-and-loading) | ||||
| 	- [API](#api) | ||||
| 		- [`getter(..)`](#getter) | ||||
| 		- [`done(..)` (optional)](#done-optional) | ||||
| 	- [Examples](#examples) | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| ## Theory and operation | ||||
| 
 | ||||
| This module generalizes structure traverse (*walking*). This is done via a `walk(..)` function that recieves a user-defined `getter(..)` function and returns a *walker*. | ||||
| 
 | ||||
| 
 | ||||
| ### Constructing the walker and walking | ||||
| ### Constructing the walker and walking ( walk(..) ) | ||||
| 
 | ||||
| `walk(getter)(state, ...nodes) -> state`   | ||||
| `walk(getter, state)(...nodes) -> state`   | ||||
| `walk(getter, state, ...nodes) -> state`   | ||||
| - Recieves a `getter` function a `state` and a list of `nodes`, | ||||
| - Iterates through `nodes` calling the `getter(..)` per node, threading the `state` through each call, | ||||
| `walk(getter[, done])(state, ...nodes) -> state`   | ||||
| `walk(getter[, done], state)(...nodes) -> state`   | ||||
| `walk(getter[, done], state, ...nodes) -> state`   | ||||
| - Recieves a `getter` function, an optional `done` function, a `state` and a list of `nodes`, | ||||
| - Iterates through `nodes`, calling the `getter(..)` per node and threading the `state` through each call, | ||||
| - If `done(..)` is given, it is called passing the `state` after walking is done, the return value is set as the new `state` value, | ||||
| - Returns the `state` when there are no more `nodes`. | ||||
| 
 | ||||
| 
 | ||||
| ### Getting and processing nodes | ||||
| ### Getting and processing nodes ( getter(..) ) | ||||
| 
 | ||||
| `getter(state, node, next, stop) -> state`   | ||||
| - Recieves `state`, `node` and two control functions: `next` and `stop`, | ||||
| @ -84,6 +98,7 @@ And for *flat* lists `.reduce(..)` and friends are simpler and more logical. `wa | ||||
| 	See a more usefull search in [examples](#examples)... | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| ## Installation and loading | ||||
| 
 | ||||
| ```shell | ||||
| @ -97,21 +112,27 @@ var walk = require('generic-walk').walk | ||||
| *Note: This module supports both AMD and node's `require(..)`** | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| ## API | ||||
| 
 | ||||
| `walk(getter(..)) -> walker(state, ...nodes)`   | ||||
| `walk(getter(..), done(..)) -> walker(state, ...nodes)`   | ||||
| Construct a reusable walker. | ||||
| 
 | ||||
| 
 | ||||
| `walk(getter(..), state) -> walker(...nodes)`   | ||||
| `walk(getter(..), done(..), state) -> walker(...nodes)`   | ||||
| Construct a reusable walker with fixed initial state. | ||||
| 
 | ||||
| 
 | ||||
| `walk(getter(..), state, ...nodes) -> result`   | ||||
| `walk(getter(..), done(..), state, ...nodes) -> result`   | ||||
| Walk the nodes. | ||||
| 
 | ||||
| *Note that `state` can not be a function.* | ||||
| 
 | ||||
| ### The getter | ||||
| 
 | ||||
| ### `getter(..)` | ||||
| 
 | ||||
| `getter(state, node, next(..), stop(..)) -> state`   | ||||
| User provided function, called to process a node. | ||||
| @ -132,6 +153,13 @@ Stop walking and return `state`. The passed `state` is directly returned from th | ||||
| *Note that `stop(..)` behaves in a similar manner to `return`, i.e. execution is aborted immidiately.* | ||||
| 
 | ||||
| 
 | ||||
| ### `done(..)` (optional) | ||||
| 
 | ||||
| `done(state) -> state`   | ||||
| User provided function, if given, is called by the *walker* after walking is done (no more nodes to handle). `state` is passed as argument and the return value is returned from the *walker*. This is run in the same context (`this`) as `getter(..)`. | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| ## Examples | ||||
| 
 | ||||
| Sum all the values of a nested array (breadth-first)... | ||||
| @ -158,31 +186,31 @@ sumd([1, [2], 3, [[4, 5]]]) // -> 15 ...walks the nodes: 1, 2, 3, 4, 5 | ||||
| To explicitly see the paths the `sum`/`sumd` take we need to modify them a little: | ||||
| ```javascript | ||||
| var makeSummer = function(mode){ | ||||
| 	return walk(function(res, node, next){ | ||||
| 		this.log(node) | ||||
| 		return node instanceof Array ? | ||||
| 			next(mode == 'breadth-first' ? 'queue' : 'do', res, ...node)  | ||||
| 			: res + node }, 0) } | ||||
| 	var walker = walk( | ||||
| 		function(res, node, next){ | ||||
| 			this.log(node) | ||||
| 			return node instanceof Array ? | ||||
| 				next(mode == 'breadth-first' ? 'queue' : 'do',  | ||||
| 					res,  | ||||
| 					...node)  | ||||
| 				: res + node }, | ||||
| 		// print the path when done... | ||||
| 		function(state){ | ||||
| 			console.log('-->', this.path) | ||||
| 			return state | ||||
| 		},  | ||||
| 		1)  | ||||
| 	// log the nodes... | ||||
| 	walker.prototype.log = function(node){ | ||||
| 		this.path = node instanceof Array ? | ||||
| 			this.path | ||||
| 			: (this.path || []).concat([node]) }  | ||||
| 	return walker | ||||
| } | ||||
| 
 | ||||
| var sum = makeSummer('breadth-first') | ||||
| var sumd = makeSummer('depth-first') | ||||
| 
 | ||||
| // define the path logger... | ||||
| sum.prototype.log =  | ||||
| sumd.prototype.log = | ||||
| function(node){ | ||||
| 	this.path = node instanceof Array ? | ||||
| 		this.path | ||||
| 		: (this.path || []).concat([node]) | ||||
| }  | ||||
| // XXX need a more natural way to catch the end of the walk... | ||||
| sum.prototype.onWalkEnd =  | ||||
| sumd.prototype.onWalkEnd =  | ||||
| function(res){  | ||||
| 	console.log('-->', this.path) | ||||
| 	return res | ||||
| } | ||||
| 
 | ||||
| sum([1, [2], 3, [[4, 5]]]) // -> 15 | ||||
| 
 | ||||
| sumd([1, [2], 3, [[4, 5]]]) // -> 15 | ||||
|  | ||||
| @ -1,6 +1,6 @@ | ||||
| { | ||||
|   "name": "generic-walk", | ||||
|   "version": "1.1.0", | ||||
|   "version": "1.3.0", | ||||
|   "description": "An extensible tree walk(..) framework...", | ||||
|   "main": "walk.js", | ||||
|   "scripts": { | ||||
|  | ||||
							
								
								
									
										70
									
								
								walk.js
									
									
									
									
									
								
							
							
						
						
									
										70
									
								
								walk.js
									
									
									
									
									
								
							| @ -7,38 +7,43 @@ | ||||
| (function(require){ var module={} // make module AMD/node compatible...
 | ||||
| /*********************************************************************/ | ||||
| 
 | ||||
| // XXX might be a good idea to add a way to do something on start/end of 
 | ||||
| // 		walk...
 | ||||
| // 		...we can easily determine if this is the first call to getter(..)
 | ||||
| // 		by checking and then setting an attr on the context...
 | ||||
| // Walk a set of nodes...
 | ||||
| // 
 | ||||
| //	Construct a walker...
 | ||||
| //	walk(getter(..))
 | ||||
| //	walk(getter(..), done(..))
 | ||||
| //		-> walker(state, ...nodes)
 | ||||
| //			-> state
 | ||||
| //
 | ||||
| //	Construct a walker with a curried start state...
 | ||||
| //	walk(getter(..), state)
 | ||||
| //	walk(getter(..), done(..), state)
 | ||||
| //		-> walker(...nodes)
 | ||||
| //			-> state
 | ||||
| //
 | ||||
| //	Walk the set of nodes...
 | ||||
| //	walk(getter(..), state, ...nodes)
 | ||||
| //	walk(getter(..), done(..), state, ...nodes)
 | ||||
| //		-> state
 | ||||
| //
 | ||||
| // NOTE: state can not be a function...
 | ||||
| //
 | ||||
| //
 | ||||
| // XXX this is essentially a version of .reduce(..), I wonder if it is 
 | ||||
| // 		feasible to do a version of .map(..), i.e. a mechanism to modify/clone
 | ||||
| // 		the input...
 | ||||
| // XXX we need a way to handle the walk end event...
 | ||||
| // 		ways this can be done:
 | ||||
| // 			- a type argument to getter...
 | ||||
| // 					getter(action, ...)
 | ||||
| // 					getter('start', state)
 | ||||
| // 					getter('node', state, node, ...)
 | ||||
| // 					getter('stop', state)
 | ||||
| // 				+ uniform and adds no new clutter...
 | ||||
| // 				- this will make the general case getter more complex
 | ||||
| // 				Q: can we implement this or a similar approach in way 
 | ||||
| // 					that would abstract the user from the actions unless 
 | ||||
| // 					they want to handle it???
 | ||||
| // 						...currently, they would need to handle the actions 
 | ||||
| // 						to ignore the non-node actions...
 | ||||
| // 						...one way to go is to simply mark the first/last 
 | ||||
| // 						calls to getter in some way... 
 | ||||
| // 						the problem here is that there is no way to tell 
 | ||||
| // 						if a specific getter is last before the call is made...
 | ||||
| // 			- event handlers (see .onWalkEnd) -- REJECTED
 | ||||
| // 				- non-uniform...
 | ||||
| // 			- a second handler passed to walk(..)
 | ||||
| // 				- will mess up the signature that is already complex...
 | ||||
| // XXX EXPERIMENTAL: done(..) handler is not yet final...
 | ||||
| var walk =  | ||||
| module.walk = | ||||
| function(getter, state, ...nodes){ | ||||
| 	// XXX EXPERIMENTAL...
 | ||||
| 	var done | ||||
| 	// we've got a done handler passed...
 | ||||
| 	if(state instanceof Function){ | ||||
| 		done = state | ||||
| 		state = nodes.shift() | ||||
| 	} | ||||
| 
 | ||||
| 	// holds the constructed walker function, used mainly to link its 
 | ||||
| 	// .prototype to the context of the getter(..)...
 | ||||
| 	var func | ||||
| @ -141,11 +146,10 @@ function(getter, state, ...nodes){ | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		// onWalkEnd handler...
 | ||||
| 		// XXX need a more natural way to do this while providing access 
 | ||||
| 		// 		to the context...
 | ||||
| 		res = context.onWalkEnd ? | ||||
| 			context.onWalkEnd(res) | ||||
| 		// call the done handler...
 | ||||
| 		// XXX EXPERIMENTAL...
 | ||||
| 		res = done ? | ||||
| 			done.call(context, res) | ||||
| 			: res | ||||
| 
 | ||||
| 		return res | ||||
| @ -153,7 +157,7 @@ function(getter, state, ...nodes){ | ||||
| 
 | ||||
| 	return ( | ||||
| 		// reusable walker...
 | ||||
| 		arguments.length == 1 ? | ||||
| 		arguments.length == (done ? 2 : 1) ? | ||||
| 			// NOTE: this wrapper is here so as to isolate and re-order res 
 | ||||
| 			// 		construction and passing it to the next level...
 | ||||
| 			// 		this is needed as when we do:
 | ||||
| @ -167,7 +171,7 @@ function(getter, state, ...nodes){ | ||||
| 				return _walk(nodes, state) }) | ||||
| 		// reusable walker with a curried initial state... 
 | ||||
| 		// NOTE: better keep this immutable or clone this in get(..)
 | ||||
| 		: arguments.length == 2 ? | ||||
| 		: arguments.length == (done ? 3 : 2) ? | ||||
| 			(func = function(...nodes){ | ||||
| 				return _walk(nodes, state) }) | ||||
| 		// walk...
 | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user