mirror of
https://github.com/flynx/walk.js.git
synced 2025-10-29 19:10:11 +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
68
README.md
68
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){
|
||||||
this.log(node)
|
return walk(function(res, node, next){
|
||||||
return node instanceof Array ?
|
this.log(node)
|
||||||
// compensate for that next(..) returns undefined...
|
return node instanceof Array ?
|
||||||
next(...node)
|
next(mode == 'breadth-first' ? 'queue' : 'do', res, ...node)
|
||||||
|| res
|
: res + node }, 0) }
|
||||||
: res + node }, 0)
|
|
||||||
var sumd = walk(function(res, node, next, down, stop){
|
var sum = makeSummer('breadth-first')
|
||||||
this.log(node)
|
var sumd = makeSummer('depth-first')
|
||||||
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']
|
||||||
|
|||||||
50
walk.js
50
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 = []
|
||||||
|
|
||||||
|
// stop walking...
|
||||||
|
// stop()
|
||||||
|
// stop(value)
|
||||||
|
//
|
||||||
|
// NOTE: 'throw' is used to stop all handling, including
|
||||||
|
// the rest of the current level...
|
||||||
|
var stop = function(r){
|
||||||
|
stop_res = r
|
||||||
|
WalkStopException = new Error('WALK_STOP_EVENT')
|
||||||
|
throw WalkStopException
|
||||||
|
}
|
||||||
|
|
||||||
res = getter.call(context,
|
res = getter.call(context,
|
||||||
res,
|
res,
|
||||||
node,
|
node,
|
||||||
// breadth first step...
|
function(action, state, ...nodes){
|
||||||
// next(...nodes) -> undefined
|
// queue nodes (breadth-first)...
|
||||||
function(...nodes){ next = nodes },
|
if(action == 'queue'){
|
||||||
// depth first step...
|
next = nodes
|
||||||
// down(state, ..nodes) -> res
|
|
||||||
function(res, ...nodes){
|
// process nodes (depth-first)...
|
||||||
return _step(context, nodes, res) },
|
} else if(action == 'do'){
|
||||||
// stop walking...
|
state = _step(context, nodes, state)
|
||||||
// stop()
|
|
||||||
// stop(value)
|
// stop processing...
|
||||||
//
|
} else if(action == 'stop'){
|
||||||
// NOTE: 'throw' is used to stop all handling, including
|
stop(state)
|
||||||
// the rest of the current level...
|
}
|
||||||
function(r){
|
return state
|
||||||
stop_res = r
|
},
|
||||||
WalkStopException = new Error('WALK_STOP_EVENT')
|
stop)
|
||||||
throw WalkStopException
|
|
||||||
})
|
|
||||||
return next
|
return next
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user