a major refactoring, not all issues resolved yet...

Signed-off-by: Alex A. Naanou <alex.nanou@gmail.com>
This commit is contained in:
Alex A. Naanou 2018-09-30 00:16:09 +03:00
parent a2c8bb5d32
commit 95b089c317
2 changed files with 61 additions and 57 deletions

View File

@ -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
View File

@ -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
} }