mirror of
https://github.com/flynx/walk.js.git
synced 2025-10-29 19:10:11 +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
82
README.md
82
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){
|
||||
var walker = walk(
|
||||
function(res, node, next){
|
||||
this.log(node)
|
||||
return node instanceof Array ?
|
||||
next(mode == 'breadth-first' ? 'queue' : 'do', res, ...node)
|
||||
: res + node }, 0) }
|
||||
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