Skip to content

Instantly share code, notes, and snippets.

@ChillyBwoy
Last active March 30, 2017 14:55
Show Gist options
  • Save ChillyBwoy/fa8ac636396c78c36dd2 to your computer and use it in GitHub Desktop.
Save ChillyBwoy/fa8ac636396c78c36dd2 to your computer and use it in GitHub Desktop.
Transducers CoffeeScript
class Reduced
@isReduced = (obj) -> obj instanceof Reduced
constructor: (@wraped) ->
unwrap: -> @wraped
reduce = (collection, fn, seed) ->
result = seed;
for item in collection
result = fn result, item
if Reduced.isReduced result then return result.unwrap()
return result
append = (result, item) ->
return [] if not result? and not item?
return result if result? and not item?
return result.concat [item] if result? and item?
transduce = (collection, transducer, append) ->
step = transducer append
seed = step()
result = reduce collection, step, seed
return step result
# constructors
map = (fn) -> (step) -> (result, item) ->
return step() if not result? and not item?
return step(result) if result? and not item?
return step(result, fn(item)) if result? and item?
filter = (predicate) -> (step) -> (result, item) ->
return (if predicate item then step result, item else result) if result? and item?
return step(result) if result? and not item?
return step() if not result? and not item?
take = (n) -> (step) ->
count = 0
(result, item) ->
return (if count++ < n then step result, item else new Reduced result) if result? and item?
return step(result) if result? and not item?
return step() if not result? and not item?
flatten = -> (step) -> (result, item) ->
if result? and item?
for x in item
result = step result, x
return result
return step(result) if result? and not item?
return step() if not result? and not item?
partition = (n) ->
if n < 1 then throw new Error "n > 1"
(step) ->
current = []
(result, item) ->
if result? and item?
current.push item
if current.length is n
result = step result, current
current = []
return result
if result? and not item?
if current.length > 0
result = step result, current
return result
if not result? and not item?
return step()
# samples
addOneT = map (x) -> x + 1
mulTwoT = map (x) -> x * 2
lessTnan4T = filter (x) -> x < 4
flattenT = flatten()
first5T = take 5
by3ItemsT = partition 3
addOneMulTwoT = (step) -> addOneT mulTwoT(step)
console.log(transduce [1..20], addOneT, append)
console.log(transduce [1..20], mulTwoT, append)
console.log(transduce [1..20], addOneMulTwoT, append)
console.log(transduce [[1, 2], [], [3], [4, 5]], flattenT, append)
console.log(transduce [1..20], lessTnan4T, append)
console.log(transduce [1..20], first5T, append)
console.log(transduce [1..20], by3ItemsT, append)
"use strict";
function Reduced(wrapped) {
this._wrapped = wrapped;
}
Reduced.isReduced = function(obj) {
return (obj instanceof Reduced);
};
Reduced.prototype.unwrap = function() {
return this._wrapped;
};
function curryArgs() {
return function(first) {
return function(all) {
return function(last) {
return function() {
var args = Array.prototype.slice.call(arguments, 0);
if (arguments.length === 0) {
return first.apply(null, args);
}
if (arguments.length === 1) {
return last.apply(null, args);
}
if (arguments.length === 2) {
return all.apply(null, args);
}
};
};
};
};
}
function prepareArgs(first, all, last) {
return function() {
var args = Array.prototype.slice.call(arguments, 0);
if (arguments.length === 0) {
return first.apply(this, args);
}
if (arguments.length === 1) {
return last.apply(this, args);
}
if (arguments.length === 2) {
return all.apply(this, args);
}
};
}
function reduce(collection, fn, seed) {
var result = seed;
for (var i = 0; i < collection.length; i++) {
result = fn(result, collection[i]);
if (Reduced.isReduced(result)) {
return result.unwrap();
}
}
return result;
}
function append(result, item) {
if (arguments.length === 2) {
return result.concat([item]);
}
if (arguments.length === 1) {
return result;
}
if (arguments.length === 0) {
return [];
}
}
function transduce(collection, transducer, append) {
var step = transducer(append),
seed = step(),
result = reduce(collection, step, seed);
return step(result);
}
// transducers constructors:
function take(n) {
return function(step) {
var count = 0,
onFirst = function() { return step(); },
onLast = function(result) { return step(result); },
onAll = function(result, item) {
if (count++ < n) {
return step(result, item);
} else {
return new Reduced(result);
}
};
return prepareArgs(onFirst, onAll, onLast);
};
}
function map(fn) {
return function(step) {
return function(result, item) {
if (arguments.length === 2) {
return step(result, fn(item));
}
if (arguments.length === 1) {
return step(result);
}
if (arguments.length === 0) {
return step();
}
};
};
}
function filter(predicate) {
return function(step) {
var onFirst = function() { return step(); },
onLast = function(result) { return step(result); },
onAll = function(result, item) {
if (predicate(item)) {
return step(result, item);
} else {
return result;
}
};
return prepareArgs(onFirst, onAll, onLast);
};
}
function flatten() {
return function(step) {
var onFirst = function() { return step(); },
onLast = function(result) { return step(result); },
onAll = function(result, item) {
for (var i = 0; i < item.length; i++) {
result = step(result, item[i]);
}
return result;
};
return prepareArgs(onFirst, onAll, onLast);
};
}
function partition(n) {
if (n < 1) {
throw new Error("n > 1");
}
return function(step) {
var current = [],
onFirst = function() { return step(); },
onLast = function(result) {
if (current.length > 0) {
result = step(result, current);
return step(result);
}
},
onAll = function(result, item) {
current.push(item);
if (current.length === n) {
result = step(result, current);
current = [];
}
return result;
};
return prepareArgs(onFirst, onAll, onLast);
};
}
// sample
var initial = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
11, 12, 13, 14, 15, 16, 17, 18, 19, 20];
var addOneT = map(function(x) { return x + 1; }),
lessTnan4T = filter(function(x) { return x < 4; }),
flattenT = flatten(),
first5T = take(5),
by3ItemsT = partition(3);
var addOne_lessTnan4 = function(step) {
return addOneT(lessTnan4T(step));
};
console.log(transduce(initial, addOneT, append));
console.log(transduce(initial, lessTnan4T, append));
console.log(transduce(initial, addOne_lessTnan4, append));
console.log(transduce([[1, 2], [], [3]], flattenT, append));
console.log(transduce(initial, first5T, append));
console.log(transduce(initial, by3ItemsT, append));
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment