Skip to content

Commit 849891c

Browse files
authored
Merge pull request #78 from jacereda/divide-and-conquer-traverse-2
Divide and conquer traverse 2
2 parents d7a3fa2 + fec9bae commit 849891c

File tree

4 files changed

+77
-51
lines changed

4 files changed

+77
-51
lines changed

bower.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
"purescript-assert": "#compiler/0.12",
2626
"purescript-console": "#compiler/0.12",
2727
"purescript-integers": "#compiler/0.12",
28-
"purescript-math": "#compiler/0.12"
28+
"purescript-math": "#compiler/0.12",
29+
"purescript-unsafe-coerce": "#compiler/0.12"
2930
}
3031
}

src/Data/Traversable.js

Lines changed: 29 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -3,59 +3,49 @@
33
// jshint maxparams: 3
44

55
exports.traverseArrayImpl = function () {
6-
function Cont(fn) {
7-
this.fn = fn;
6+
function array1(a) {
7+
return [a];
88
}
99

10-
var emptyList = {};
11-
12-
var ConsCell = function (head, tail) {
13-
this.head = head;
14-
this.tail = tail;
15-
};
10+
function array2(a) {
11+
return function (b) {
12+
return [a, b];
13+
};
14+
}
1615

17-
function consList(x) {
18-
return function (xs) {
19-
return new ConsCell(x, xs);
16+
function array3(a) {
17+
return function (b) {
18+
return function (c) {
19+
return [a, b, c];
20+
};
2021
};
2122
}
2223

23-
function listToArray(list) {
24-
var arr = [];
25-
var xs = list;
26-
while (xs !== emptyList) {
27-
arr.push(xs.head);
28-
xs = xs.tail;
29-
}
30-
return arr;
24+
function concat2(xs) {
25+
return function (ys) {
26+
return xs.concat(ys);
27+
};
3128
}
3229

3330
return function (apply) {
3431
return function (map) {
3532
return function (pure) {
3633
return function (f) {
37-
var buildFrom = function (x, ys) {
38-
return apply(map(consList)(f(x)))(ys);
39-
};
40-
41-
var go = function (acc, currentLen, xs) {
42-
if (currentLen === 0) {
43-
return acc;
44-
} else {
45-
var last = xs[currentLen - 1];
46-
return new Cont(function () {
47-
return go(buildFrom(last, acc), currentLen - 1, xs);
48-
});
49-
}
50-
};
51-
5234
return function (array) {
53-
var result = go(pure(emptyList), array.length, array);
54-
while (result instanceof Cont) {
55-
result = result.fn();
35+
function go(bot, top) {
36+
switch (top - bot) {
37+
case 0: return pure([]);
38+
case 1: return map(array1)(f(array[bot]));
39+
case 2: return apply(map(array2)(f(array[bot])))(f(array[bot + 1]));
40+
case 3: return apply(apply(map(array3)(f(array[bot])))(f(array[bot + 1])))(f(array[bot + 2]));
41+
default:
42+
// This slightly tricky pivot selection aims to produce two
43+
// even-length partitions where possible.
44+
var pivot = bot + Math.floor((top - bot) / 4) * 2;
45+
return apply(map(concat2)(go(bot, pivot)))(go(pivot, top));
46+
}
5647
}
57-
58-
return map(listToArray)(result);
48+
return go(0, array.length);
5949
};
6050
};
6151
};

test/Main.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,13 @@ exports.arrayFrom1UpTo = function (n) {
77
}
88
return result;
99
};
10+
11+
exports.arrayReplicate = function (n) {
12+
return function (x) {
13+
var result = [];
14+
for (var i = 1; i <= n; i++) {
15+
result.push(x);
16+
}
17+
return result;
18+
};
19+
};

test/Main.purs

Lines changed: 36 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,24 +2,34 @@ module Test.Main where
22

33
import Prelude
44

5-
import Effect (Effect)
6-
import Effect.Console (log)
75
import Data.Bifoldable (class Bifoldable, bifoldl, bifoldr, bifoldMap, bifoldrDefault, bifoldlDefault, bifoldMapDefaultR, bifoldMapDefaultL)
86
import Data.Bifunctor (class Bifunctor, bimap)
97
import Data.Bitraversable (class Bitraversable, bisequenceDefault, bitraverse, bisequence, bitraverseDefault)
108
import Data.Foldable (class Foldable, find, findMap, fold, indexl, indexr, foldMap, foldMapDefaultL, foldMapDefaultR, foldl, foldlDefault, foldr, foldrDefault, length, maximum, maximumBy, minimum, minimumBy, null, surroundMap)
119
import Data.FoldableWithIndex (class FoldableWithIndex, findWithIndex, foldMapWithIndex, foldMapWithIndexDefaultL, foldMapWithIndexDefaultR, foldlWithIndex, foldlWithIndexDefault, foldrWithIndex, foldrWithIndexDefault, surroundMapWithIndex)
1210
import Data.Function (on)
1311
import Data.FunctorWithIndex (class FunctorWithIndex, mapWithIndex)
14-
import Data.Int (toNumber)
12+
import Data.Int (toNumber, pow)
1513
import Data.Maybe (Maybe(..))
1614
import Data.Monoid.Additive (Additive(..))
15+
import Data.Newtype (unwrap)
1716
import Data.Traversable (class Traversable, sequenceDefault, traverse, sequence, traverseDefault)
1817
import Data.TraversableWithIndex (class TraversableWithIndex, traverseWithIndex)
18+
import Effect (Effect, foreachE)
19+
import Effect.Console (log)
1920
import Math (abs)
20-
import Test.Assert (assert)
21+
import Test.Assert (assert, assert')
22+
import Unsafe.Coerce (unsafeCoerce)
2123

2224
foreign import arrayFrom1UpTo :: Int -> Array Int
25+
foreign import arrayReplicate :: forall a. Int -> a -> Array a
26+
27+
foldableLength :: forall f a. Foldable f => f a -> Int
28+
foldableLength = unwrap <<< foldMap (const (Additive 1))
29+
30+
-- Ensure that a value is evaluated 'lazily' by treating it as an Eff action.
31+
deferEff :: forall a. (Unit -> a) -> Effect a
32+
deferEff = unsafeCoerce
2333

2434
main :: Effect Unit
2535
main = do
@@ -43,8 +53,9 @@ main = do
4353
log "Test foldrDefault"
4454
testFoldableFoldrDefault 20
4555

46-
log "Test traversableArray instance"
47-
testTraversableArrayWith 20
56+
foreachE [1,2,3,4,5,10,20] \i -> do
57+
log $ "Test traversableArray instance with an array of size: " <> show i
58+
testTraversableArrayWith i
4859

4960
log "Test traversableArray instance is stack safe"
5061
testTraversableArrayWith 20000
@@ -234,19 +245,33 @@ testFoldableWithIndexLawsOn c f g = do
234245
assert $ foldMapWithIndex f c == foldrWithIndexDefault (\i x y -> f i x <> y) mempty c
235246

236247
testTraversableFWith
237-
:: forall f
248+
:: forall f
238249
. Traversable f
239250
=> Eq (f Int)
240251
=> (Int -> f Int)
241252
-> Int
242253
-> Effect Unit
243254
testTraversableFWith f n = do
244255
let dat = f n
256+
let len = foldableLength dat
257+
258+
_ <- traverse pure dat
259+
260+
assert' "traverse Just == Just" $ traverse Just dat == Just dat
261+
assert' "traverse pure == pure (Array)" $ traverse pure dat == [dat]
262+
263+
when (len <= 10) do
264+
result <- deferEff \_ -> traverse (\x -> [x,x]) dat == arrayReplicate (pow 2 len) dat
265+
assert' "traverse with Array as underlying applicative" result
266+
267+
assert' "traverse (const Nothing) == const Nothing" $
268+
traverse (const Nothing :: Int -> Maybe Int) dat == Nothing
269+
270+
assert' "sequence <<< map f == traverse f" $
271+
sequence (map Just dat) == traverse Just dat
245272

246-
assert $ traverse Just dat == Just dat
247-
assert $ traverse pure dat == [dat]
248-
assert $ traverse (\x -> if x < 10 then Just x else Nothing) dat == Nothing
249-
assert $ sequence (map Just dat) == traverse Just dat
273+
assert' "underlying applicative" $
274+
(traverse pure dat :: Unit -> f Int) unit == dat
250275

251276
testTraversableArrayWith :: Int -> Effect Unit
252277
testTraversableArrayWith = testTraversableFWith arrayFrom1UpTo

0 commit comments

Comments
 (0)