mirror of
https://github.com/flynx/diff.js.git
synced 2025-10-28 18:40:09 +00:00
added stop(..) to walk(..) + examples...
Signed-off-by: Alex A. Naanou <alex.nanou@gmail.com>
This commit is contained in:
parent
3aa6577eb9
commit
61b57bca2a
205
diff.js
205
diff.js
@ -263,17 +263,23 @@ var proxy = function(path, func){
|
||||
}
|
||||
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
//
|
||||
// Construct a reusable walker function...
|
||||
// walk(get(..))
|
||||
// -> func(start, ...nodes)
|
||||
// -> func(state, ...nodes)
|
||||
//
|
||||
// Construct a reusable walker function with set initial state...
|
||||
// walk(get(..), state)
|
||||
// -> func(...nodes)
|
||||
//
|
||||
// Walk the nodes...
|
||||
// walk(get(..), start, ...nodes)
|
||||
// walk(get(..), state, ...nodes)
|
||||
// -> res
|
||||
//
|
||||
// The get(..) that walk(..) expects has the following signature:
|
||||
// get(res, node, next(..), down(..))
|
||||
// get(res, node, next(..), down(..), stop(..))
|
||||
// -> res
|
||||
//
|
||||
// Queue next nodes to walk...
|
||||
@ -286,41 +292,90 @@ var proxy = function(path, func){
|
||||
// state.
|
||||
//
|
||||
// Walk the next set of nodes...
|
||||
// down(start, ...nodes)
|
||||
// down(state, ...nodes)
|
||||
// -> res
|
||||
// NOTE: this will process all the nodes and then return the
|
||||
// result.
|
||||
//
|
||||
// Stop the walk...
|
||||
// stop()
|
||||
// stop(result)
|
||||
// -> undefined
|
||||
// NOTE: this will break current function execution in a similar
|
||||
// effect to 'return', i.e. no code in function will be
|
||||
// executed after stop(..) call...
|
||||
// NOTE: this is done by throwing a special error, this error
|
||||
// should either not be caught or must be re-thrown, as
|
||||
// shadowing this exception may have unexpected results...
|
||||
//
|
||||
//
|
||||
// Example:
|
||||
// // sum all the values of a nested array...
|
||||
// var sum = walk(function(res, node, next){
|
||||
// // go into arrays...
|
||||
// if(node instanceof Array){
|
||||
// next(...node)
|
||||
//
|
||||
// // sum the values...
|
||||
// } else {
|
||||
// console.log(node)
|
||||
// res += node
|
||||
// }
|
||||
// return res
|
||||
// })
|
||||
// return node instanceof Array ?
|
||||
// // compensate for that next(..) returns undefined...
|
||||
// next(...node)
|
||||
// || res
|
||||
// : res + node }, 0)
|
||||
//
|
||||
// // depth first walker...
|
||||
// var sumd = walk(function(res, node, next, down){
|
||||
// if(node instanceof Array){
|
||||
// res = down(res, ...node)
|
||||
// var sumd = walk(function(res, node, next, down, stop){
|
||||
// return node instanceof Array ?
|
||||
// down(res, ...node)
|
||||
// : res + node }, 0)
|
||||
//
|
||||
// } else {
|
||||
// console.log(node)
|
||||
// res += node
|
||||
// }
|
||||
// return res
|
||||
// })
|
||||
// sum([1, [2], 3, [[4, 5]]]) // -> 15 ...walks the nodes: 1, 3, 2, 4, 5
|
||||
// sumd([1, [2], 3, [[4, 5]]]) // -> 15 ...walks the nodes: 1, 2, 3, 4, 5
|
||||
//
|
||||
//
|
||||
// Example:
|
||||
// // XXX res/path threading seem unnatural here...
|
||||
// var __firstZero = walk(function(res, node, next, down, stop){
|
||||
// var k = node[0]
|
||||
// var v = node[1]
|
||||
// var path = res[0]
|
||||
// return v === 0 ?
|
||||
// // NOTE: we .slice(1) here to remove the initial null
|
||||
// // we added in firstZero(..)...
|
||||
// stop([ path.slice(1).concat([k]) ])
|
||||
// : v instanceof Object?
|
||||
// down(
|
||||
// [path.concat([k]), null],
|
||||
// ...Object.entries(v))
|
||||
// : res }, [[], null])
|
||||
// var firstZero = function(value){
|
||||
// // NOTE: we are wrapping the input here to make it the same
|
||||
// // format as that of Object.entries(..) items...
|
||||
// return __firstZero([null, value]).pop() }
|
||||
//
|
||||
//
|
||||
// // same as the above but illustrates a different strategy, a bit
|
||||
// // cleaner but creates a walker every time it's called...
|
||||
// var firstZero = function(value){
|
||||
// return walk(
|
||||
// function(res, node, next, down, stop){
|
||||
// var k = node[0]
|
||||
// var v = node[1]
|
||||
// var path = res[0]
|
||||
// return v === 0 ?
|
||||
// // NOTE: we .slice(1) here to remove the initial null
|
||||
// // we added in firstZero(..)...
|
||||
// stop([ path.slice(1).concat([k]) ])
|
||||
// : v instanceof Object?
|
||||
// down(
|
||||
// [path.concat([k]), null],
|
||||
// ...Object.entries(v))
|
||||
// : res },
|
||||
// [[], null],
|
||||
// // wrap the input to make it compatible with Object.entries(..)
|
||||
// // items...
|
||||
// [null, value])
|
||||
// // separate the result from path...
|
||||
// .pop() }
|
||||
//
|
||||
//
|
||||
// firstZero([10, 5, [{x: 1, y: 0}, 4]]) // -> ['2', '0', 'y']
|
||||
//
|
||||
// sum([1, [2], 3, [[4, 5]]]) // -> 15 and log 1, 3, 2, 4, 5
|
||||
// sumd([1, [2], 3, [[4, 5]]]) // -> 15 and log 1, 2, 3, 4, 5
|
||||
//
|
||||
//
|
||||
// NOTE: a walker is returned iff walk(..) is passed a single argument.
|
||||
@ -329,60 +384,92 @@ var proxy = function(path, func){
|
||||
// walk(get)(start, ...nodes)
|
||||
// NOTE: walk goes breadth first...
|
||||
//
|
||||
var walk = function(get, state, ...args){
|
||||
var _step = function(args, res){
|
||||
// XXX might be a good idea to move this into it's own module...
|
||||
// generic-walk might be a good name...
|
||||
var walk = function(get, state, ...nodes){
|
||||
// this is used to break out of the recursion...
|
||||
// NOTE: this can leak out but we only care about it's identity thus
|
||||
// no damage can be done...
|
||||
var WalkStopException
|
||||
var err_res
|
||||
|
||||
var _step = function(nodes, res){
|
||||
// construct a comfortable env for the user and handle the
|
||||
// results...
|
||||
var _get = function(node){
|
||||
var next = []
|
||||
res = get(res, node,
|
||||
res = get(
|
||||
res,
|
||||
node,
|
||||
// breadth first step...
|
||||
// next(...nodes)
|
||||
// -> undefined
|
||||
// NOTE: this is different to down(..) in that this will
|
||||
// return immediately and no result is known at time
|
||||
// of call thus undefined is returned...
|
||||
// NOTE: this is different to down(..) in that the user
|
||||
// indirectly gives this the state by returning it
|
||||
// from the last get(..) called per level.
|
||||
// next(...nodes) -> undefined
|
||||
function(...nodes){ next = nodes },
|
||||
// depth first step...
|
||||
// down(state, ..nodes)
|
||||
// -> res
|
||||
// NOTE: this is different to next as the user has control
|
||||
// over what value to start with and will get the
|
||||
// result they can handle...
|
||||
// down(state, ..nodes) -> res
|
||||
function(res, ...nodes){
|
||||
return _step(nodes, res) })
|
||||
return _step(nodes, res) },
|
||||
// stop...
|
||||
// stop()
|
||||
// stop(value)
|
||||
//
|
||||
// NOTE: 'throw' is used to stop all handling, including
|
||||
// the rest of the current level...
|
||||
function(r){
|
||||
err_res = r
|
||||
WalkStopException = new Error('WALK_STOP_EVENT')
|
||||
throw WalkStopException
|
||||
})
|
||||
return next
|
||||
}
|
||||
return args.length == 0 ?
|
||||
|
||||
return nodes.length == 0 ?
|
||||
// no new nodes to walk...
|
||||
res
|
||||
// do the next level...
|
||||
// NOTE: see note below... ( ;) )
|
||||
: _step(args
|
||||
: _step(nodes
|
||||
.map(_get)
|
||||
.reduce(function(next, e){
|
||||
return e instanceof Array ?
|
||||
next.concat(e)
|
||||
: next.push(e) }, []), res) }
|
||||
: next.push(e) }, []), res)
|
||||
}
|
||||
// call _step(..) and handle WalkStopException...
|
||||
var _walk = function(nodes, res){
|
||||
try{
|
||||
return _step(nodes, res)
|
||||
|
||||
return arguments.length == 1 ?
|
||||
} catch(e){
|
||||
// handle the abort condition...
|
||||
if(e === WalkStopException){
|
||||
return err_res
|
||||
}
|
||||
// something broke...
|
||||
throw e
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
// return a reusable walker...
|
||||
// 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:
|
||||
// step(res, ...args.map(_get).reduce(...))
|
||||
// res (if immutable) will first get passed by value and
|
||||
// then will get re-assigned in _get(..), thus step(..) will
|
||||
// not see the updated version...
|
||||
// This approach simply pushes the pass-by-value of res till
|
||||
// after it gets updated by _get(..).
|
||||
function(res, ...args){
|
||||
return _step(args, res) }
|
||||
arguments.length == 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:
|
||||
// step(res, ...nodes.map(_get).reduce(...))
|
||||
// res (if immutable) will first get passed by value and
|
||||
// then will get re-assigned in _get(..), thus step(..) will
|
||||
// not see the updated version...
|
||||
// This approach simply pushes the pass-by-value of res till
|
||||
// after it gets updated by _get(..).
|
||||
function(res, ...nodes){
|
||||
return _walk(nodes, res) }
|
||||
// reusable walker with a curried initial state...
|
||||
// NOTE: better keep this immutable or clone this in get(..)
|
||||
: arguments.length == 2 ?
|
||||
function(...nodes){
|
||||
return _walk(nodes, state) }
|
||||
// walk...
|
||||
: _step(args, res)
|
||||
: _walk(nodes, state))
|
||||
}
|
||||
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user