mirror of
				https://github.com/flynx/walk.js.git
				synced 2025-10-31 03:50:14 +00:00 
			
		
		
		
	a major refactoring, not all issues resolved yet...
Signed-off-by: Alex A. Naanou <alex.nanou@gmail.com>
This commit is contained in:
		
							parent
							
								
									a2c8bb5d32
								
							
						
					
					
						commit
						95b089c317
					
				
							
								
								
									
										64
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										64
									
								
								README.md
									
									
									
									
									
								
							| @ -18,14 +18,14 @@ This module generalizes structure traverse (*walking*). This is done via a `walk | |||||||
| - Returns the `state` when there are no more `nodes`. | - Returns the `state` when there are no more `nodes`. | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| ### The getter | ### Getting and processing nodes | ||||||
| 
 | 
 | ||||||
| `getter(state, node, next, down, stop) -> state`   | `getter(state, node, next, stop) -> state`   | ||||||
| - Recieves `state`, `node` and three control functions: `next`, `down` and `stop`, | - Recieves `state`, `node` and two control functions: `next` and `stop`, | ||||||
| - Called in a context (`this`), persistent within one `walk(..)` call, inherited from *walker*`.prototype` and usable to store data between `getter(..)` calls, | - Called in a context (`this`), persistent within one `walk(..)` call, inherited from *walker*`.prototype` and usable to store data between `getter(..)` calls, | ||||||
| - Can process `node` and `state`, | - Can process `node` and `state`, | ||||||
| - Can queue nodes for walking via `next(...nodes)` | - Can queue nodes for walking via `next('queue', state, ...nodes)` | ||||||
| - Can walk nodes directly via `down(state, ...nodes) -> state` | - Can walk nodes directly via `next('do', state, ...nodes) -> state` | ||||||
| - Can abbort *walking* and return a state via `stop()` or `stop(state)` | - Can abbort *walking* and return a state via `stop()` or `stop(state)` | ||||||
| - Returns `state`, | - Returns `state`, | ||||||
| 
 | 
 | ||||||
| @ -47,14 +47,14 @@ And for *flat* lists `.reduce(..)` and friends are simpler and more logical. `wa | |||||||
| - The input is not *flat*: | - The input is not *flat*: | ||||||
| 	```javascript | 	```javascript | ||||||
| 	// sum the items in a *deep* array (depth-first)... | 	// sum the items in a *deep* array (depth-first)... | ||||||
| 	var sum = walk(function(r, n){ | 	var sum = walk(function(r, n, next){ | ||||||
| 		return n instanceof Array ? | 		return n instanceof Array ? | ||||||
| 			down(r, ...n) | 			next('queue', r, ...n) | ||||||
| 			: r + n }, 0)  | 			: r + n }, 0)  | ||||||
| 			 | 			 | ||||||
| 	sum( [1, [2, 3], 4, [[5], 6]] ) // -> 21 | 	sum( [1, [2, 3], 4, [[5], 6]] ) // -> 21 | ||||||
| 	``` | 	``` | ||||||
| 	For reference here is a *recursive* `.reduce(..)` example: | 	For reference here is a *recursive* `.reduce(..)` example, already a bit more complex: | ||||||
| 	```javascript | 	```javascript | ||||||
| 	function sumr(l){ | 	function sumr(l){ | ||||||
| 		return l.reduce(function(r, e){ | 		return l.reduce(function(r, e){ | ||||||
| @ -68,16 +68,14 @@ And for *flat* lists `.reduce(..)` and friends are simpler and more logical. `wa | |||||||
| - Need to abort the recursion prematurelly: | - Need to abort the recursion prematurelly: | ||||||
| 	```javascript | 	```javascript | ||||||
| 	// check if structure contains 0... | 	// check if structure contains 0... | ||||||
| 	var containsZero = walk(function(r, e, next, down, stop){ | 	var containsZero = walk(function(r, e, next, stop){ | ||||||
| 		// NOTE: we'll only count leaf nodes... | 		// NOTE: we'll only count leaf nodes... | ||||||
| 		this.nodes_visited = (this.nodes_visited || 0) | 		this.nodes_visited = (this.nodes_visited || 0) | ||||||
| 		return e === 0 ?  | 		return e === 0 ?  | ||||||
| 				// target found... | 				// abort search, report number of nodes visited... | ||||||
| 				//...abort search, report number of nodes visited... |  | ||||||
| 				stop(this.nodes_visited+1) | 				stop(this.nodes_visited+1) | ||||||
| 			: e instanceof Array ? | 			: e instanceof Array ? | ||||||
| 				// breadth-first walk... | 				next('queue', ...e) | ||||||
| 				!!next(...e) |  | ||||||
| 			: (this.nodes_visited++, r) }, false) | 			: (this.nodes_visited++, r) }, false) | ||||||
| 
 | 
 | ||||||
| 	containsZero( [1, [2, 0], 4, [[5], 6]] ) // -> 3 | 	containsZero( [1, [2, 0], 4, [[5], 6]] ) // -> 3 | ||||||
| @ -115,15 +113,15 @@ Walk the nodes. | |||||||
| 
 | 
 | ||||||
| ### The getter | ### The getter | ||||||
| 
 | 
 | ||||||
| `getter(state, node, next(..), down(..), stop(..)) -> state`   | `getter(state, node, next(..), stop(..)) -> state`   | ||||||
| User provided function, called to process a node. | User provided function, called to process a node. | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| `next(...nodes)`   | `next('queue', state, ...nodes) -> state`   | ||||||
| Queue `nodes` for walking. The queued nodes will get *walked* after this level of nodes is done (i.e. the `getter(..)` is called for each node on level). | Queue `nodes` for walking. The queued nodes will get *walked* after this level of nodes is done (i.e. the `getter(..)` is called for each node on level). `state` is returned as-is. This is done to make `next('queue', ..)` and `next('do', ..)` signature compatible and this simpler to use. | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| `down(state, ...nodes) -> state`   | `next('do', state, ...nodes) -> state`   | ||||||
| Walk `nodes` and return `state`. The nodes will get *walked* immidiately. | Walk `nodes` and return `state`. The nodes will get *walked* immidiately. | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| @ -140,9 +138,7 @@ Sum all the values of a nested array (breadth-first)... | |||||||
| ```javascript | ```javascript | ||||||
| var sum = walk(function(res, node, next){ | var sum = walk(function(res, node, next){ | ||||||
| 	return node instanceof Array ? | 	return node instanceof Array ? | ||||||
| 		// compensate for that next(..) returns undefined... | 		next('queue', res, ...node)  | ||||||
| 		next(...node)  |  | ||||||
| 			|| res |  | ||||||
| 		: res + node }, 0) | 		: res + node }, 0) | ||||||
| 
 | 
 | ||||||
| sum([1, [2], 3, [[4, 5]]]) // -> 15 ...walks the nodes: 1, 3, 2, 4, 5 | sum([1, [2], 3, [[4, 5]]]) // -> 15 ...walks the nodes: 1, 3, 2, 4, 5 | ||||||
| @ -150,9 +146,10 @@ sum([1, [2], 3, [[4, 5]]]) // -> 15 ...walks the nodes: 1, 3, 2, 4, 5 | |||||||
| 
 | 
 | ||||||
| Sum all the values of a nested array (depth-first)... | Sum all the values of a nested array (depth-first)... | ||||||
| ```javascript | ```javascript | ||||||
| var sumd = walk(function(res, node, next, down, stop){ | var sumd = walk(function(res, node, next){ | ||||||
| 	return node instanceof Array ? | 	return node instanceof Array ? | ||||||
| 		down(res, ...node) | 		// yes, this is the only difference... | ||||||
|  | 		next('do', res, ...node) | ||||||
| 		: res + node }, 0) | 		: res + node }, 0) | ||||||
| 
 | 
 | ||||||
| sumd([1, [2], 3, [[4, 5]]]) // -> 15 ...walks the nodes: 1, 2, 3, 4, 5 | sumd([1, [2], 3, [[4, 5]]]) // -> 15 ...walks the nodes: 1, 2, 3, 4, 5 | ||||||
| @ -160,18 +157,15 @@ 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: | To explicitly see the paths the `sum`/`sumd` take we need to modify them a little: | ||||||
| ```javascript | ```javascript | ||||||
| var sum = walk(function(res, node, next){ | var makeSummer = function(mode){ | ||||||
|  | 	return walk(function(res, node, next){ | ||||||
| 		this.log(node) | 		this.log(node) | ||||||
| 		return node instanceof Array ? | 		return node instanceof Array ? | ||||||
| 		// compensate for that next(..) returns undefined... | 			next(mode == 'breadth-first' ? 'queue' : 'do', res, ...node)  | ||||||
| 		next(...node)  | 			: res + node }, 0) } | ||||||
| 			|| res | 
 | ||||||
| 		: res + node }, 0) | var sum = makeSummer('breadth-first') | ||||||
| var sumd = walk(function(res, node, next, down, stop){ | var sumd = makeSummer('depth-first') | ||||||
| 	this.log(node) |  | ||||||
| 	return node instanceof Array ? |  | ||||||
| 		down(res, ...node) |  | ||||||
| 		: res + node }, 0) |  | ||||||
| 
 | 
 | ||||||
| // define the path logger... | // define the path logger... | ||||||
| sum.prototype.log =  | sum.prototype.log =  | ||||||
| @ -199,7 +193,7 @@ FInd first zero in tree and return it's path... | |||||||
| // NOTE: the only reason this is wrapped into a function is that we need | // NOTE: the only reason this is wrapped into a function is that we need | ||||||
| //  	to restrict the number of items (L) this is passed to 1... | //  	to restrict the number of items (L) this is passed to 1... | ||||||
| var firstZero = function(L){ | var firstZero = function(L){ | ||||||
| 	return walk(function(res, node, next, down, stop){ | 	return walk(function(res, node, next, stop){ | ||||||
| 		// setup... | 		// setup... | ||||||
| 		if(this.path == null){ | 		if(this.path == null){ | ||||||
| 			this.path = [] | 			this.path = [] | ||||||
| @ -214,9 +208,7 @@ var firstZero = function(L){ | |||||||
| 				stop(path.slice(1).concat([k])) | 				stop(path.slice(1).concat([k])) | ||||||
| 			: v instanceof Object? | 			: v instanceof Object? | ||||||
| 				(path.push(k),  | 				(path.push(k),  | ||||||
| 				down( | 				next('do', res, ...Object.entries(v))) | ||||||
| 					res,  |  | ||||||
| 					...Object.entries(v))) |  | ||||||
| 			: res}, false, L) } | 			: res}, false, L) } | ||||||
| 
 | 
 | ||||||
| firstZero([10, 5, [{x: 1, y: 0}, 4]]) // -> ['2', '0', 'y'] | firstZero([10, 5, [{x: 1, y: 0}, 4]]) // -> ['2', '0', 'y'] | ||||||
|  | |||||||
							
								
								
									
										38
									
								
								walk.js
									
									
									
									
									
								
							
							
						
						
									
										38
									
								
								walk.js
									
									
									
									
									
								
							| @ -32,9 +32,10 @@ | |||||||
| // 						calls to getter in some way... 
 | // 						calls to getter in some way... 
 | ||||||
| // 						the problem here is that there is no way to tell 
 | // 						the problem here is that there is no way to tell 
 | ||||||
| // 						if a specific getter is last before the call is made...
 | // 						if a specific getter is last before the call is made...
 | ||||||
| // 			- event handlers (see .onWalkEnd)
 | // 			- event handlers (see .onWalkEnd) -- REJECTED
 | ||||||
| // 				- non-uniform...
 | // 				- non-uniform...
 | ||||||
| // 			- a second handler passed to walk(..)
 | // 			- a second handler passed to walk(..)
 | ||||||
|  | // 				- will mess up the signature that is already complex...
 | ||||||
| var walk =  | var walk =  | ||||||
| module.walk = | module.walk = | ||||||
| function(getter, state, ...nodes){ | function(getter, state, ...nodes){ | ||||||
| @ -55,27 +56,38 @@ function(getter, state, ...nodes){ | |||||||
| 		// results...
 | 		// results...
 | ||||||
| 		var _get = function(node){ | 		var _get = function(node){ | ||||||
| 			var next = [] | 			var next = [] | ||||||
| 			res = getter.call(context, | 
 | ||||||
| 				res,  |  | ||||||
| 				node,  |  | ||||||
| 				// breadth first step...
 |  | ||||||
| 				// 	next(...nodes) -> undefined
 |  | ||||||
| 				function(...nodes){ next = nodes }, |  | ||||||
| 				// depth first step...
 |  | ||||||
| 				// 	down(state, ..nodes) -> res
 |  | ||||||
| 				function(res, ...nodes){  |  | ||||||
| 					return _step(context, nodes, res) }, |  | ||||||
| 			// stop walking...
 | 			// stop walking...
 | ||||||
| 			// 	stop()
 | 			// 	stop()
 | ||||||
| 			// 	stop(value)
 | 			// 	stop(value)
 | ||||||
| 			//
 | 			//
 | ||||||
| 			// NOTE: 'throw' is used to stop all handling, including 
 | 			// NOTE: 'throw' is used to stop all handling, including 
 | ||||||
| 			// 		the rest of the current level...
 | 			// 		the rest of the current level...
 | ||||||
| 				function(r){ | 			var stop = function(r){ | ||||||
| 				stop_res = r | 				stop_res = r | ||||||
| 				WalkStopException = new Error('WALK_STOP_EVENT') | 				WalkStopException = new Error('WALK_STOP_EVENT') | ||||||
| 				throw WalkStopException  | 				throw WalkStopException  | ||||||
| 				}) | 			} | ||||||
|  | 
 | ||||||
|  | 			res = getter.call(context, | ||||||
|  | 				res,  | ||||||
|  | 				node,  | ||||||
|  | 				function(action, state, ...nodes){ | ||||||
|  | 					// queue nodes (breadth-first)...
 | ||||||
|  | 					if(action == 'queue'){ | ||||||
|  | 						next = nodes | ||||||
|  | 
 | ||||||
|  | 					// process nodes (depth-first)...
 | ||||||
|  | 					} else if(action == 'do'){ | ||||||
|  | 						state = _step(context, nodes, state) | ||||||
|  | 
 | ||||||
|  | 					// stop processing...
 | ||||||
|  | 					} else if(action == 'stop'){ | ||||||
|  | 						stop(state) | ||||||
|  | 					} | ||||||
|  | 					return state | ||||||
|  | 				}, | ||||||
|  | 				stop) | ||||||
| 			return next | 			return next | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user