Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
147 views
in Technique[技术] by (71.8m points)

javascript - Fulfilling all function arguments in order before invoking

I'm trying to understand how the following zip function (esp. the invoke function) can be made more functional. The issue I've got is that the invoke method has to wait for both the left and right side to be filled before it can dispatch the values. The values have to be called in order so that the correct values are zipped, otherwise I would consider a curry/partial function to fulfill this.

Is there anything that I could use that could remove this hinderance.

function zip(state, a, b) {
    var left = [];
    var right = [];

    function invoke() {
        if (left.length > 0 && right.length > 0) {
            state([left.shift(), right.shift()]);
        }
    }

    a.foreach(function(v) {
        left.push(v);
        invoke();
    });

    b.foreach(function(v) {
        right.push(v);
        invoke();
    });
}

Bellow is a simple example of what satisfies the zip function.

function Stream() {
    var env = this;
    env.subs = [];
    env.id = setInterval(function() {
        env.subs.forEach(function(f) {
            f(Math.random()); 
        });
    }, ((Math.random() * 100) + 500) | 0);
}
Stream.prototype.foreach = function(f) {
    this.subs.push(f);
}
zip(function(v) {
    console.log(v);
}, new Stream(), new Stream());

Bonus: Removing mutable array.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

A more functional approach would be possible if the Stream had some kind of iterator interface, that divides the list in the first element and its successors (like Haskell lists are built, you seem to know them).

I know this code is more complex (and at least longer) at first, but using the structures gets more convenient:

function Promise(resolver) {
    // you know better promise libs of course
    // this one is not even monadic
    var subs = [],
        res = null;
    resolver(function resolve() {
        res = arguments;
        while (subs.length) subs.shift().apply(null, res);
    });
    this.onData = function(f) {
        if (res)
            f.apply(null, res);
        else
            subs.push(f);
        return this;
    };
}
Promise.all = function() {
    var ps = Array.prototype.concat.apply([], arguments);
    return new Promise(function(resolve) {
        var res = [],
            l = ps.length;
        ps.forEach(function(p, i) {
            p.onData(function() {
                while(res.length < arguments.length) res.push([]);
                for (var j=0; j<arguments.length; j++)
                    res[j][i] = arguments[j];
                if (--l == 0)
                    resolve.apply(null, res);
            });
        });
    });
};
function Stream() {
    // an asynchronous (random) list
    var that = this,
        interval = (Math.random() * 100 + 500) | 0;
    this.first = new Promise(function create(resolve) {
        that.id = setTimeout(function() {
            resolve(Math.random(), new Promise(create));
        }, interval);
    });
}
// this is how to consume a stream:
Stream.prototype.forEach = function(f) {
    this.first.onData(function fire(res, next) {
        f(res);
        next.onData(fire);
    });
    return this;
};
Stream.prototype.end = function() { clearTimeout(this.id); return this; };

But zipping them is easy now:

function zip() {
    var res = Object.create(Stream.prototype); // inherit the Stream interface
    res.first = (function create(firsts) {
        return new Promise(function(resolve) {
            Promise.all(firsts).onData(function(results, nexts) {
                resolve(results, create(nexts));
            });
        });
    })(Array.prototype.map.call(arguments, function(stream) {
        return stream.first;
    }));
    return res;
}
zip(new Stream, new Stream).forEach(console.log.bind(console));

Basically I've generalized your waiting for the first items into the Promise pattern, where Promise.all features parallel waiting, and your mutable arrays of results into nested lists of promises. And I've avoided code duplication (for left and right) by making all functions work with any number of arguments.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...