jsbean / Collections

Collection data structures
MIT License
2 stars 0 forks source link

Implement fold (or reduce) for Tree #112

Open jsbean opened 7 years ago

jsbean commented 7 years ago

There are now several methods that traverse over a Tree while holding over some accumulator.

It could be worthwhile to implement:

func reduce <U> (_ initial: U, _ nextPartial: (U, T) -> U) -> U
mossheim commented 6 years ago

Confused about how to do this correctly.. what is the type of T above? Tree? Then it seems hard to do without hitting infinite recursion:

    public func reduce <A> (_ initial: A, _ nextPartial: (A, Tree) -> A) -> A {
        switch self {
        case .leaf(let value):
            return nextPartial(initial, self)
        case .branch(let value, let trees):
            ???
        }
    }

This also seems overly complicated as it forces you to write nextPartial with a switch every time.

is it Either<Leaf, Branch>? Couldn't get this to compile.

Furthermore, should it be inorder, preorder, postorder...?

mossheim commented 6 years ago

Aha!

    public func reduce <A> (_ initial: A, _ nextPartial: (A, Tree) -> A) -> A {
        switch self {
        case .leaf(_):
            return nextPartial(initial, self)
        case .branch(_, let trees):
            return nextPartial(trees.reduce(initial) { $1.reduce($0, nextPartial) }, self)
        }
    }
mossheim commented 6 years ago

That's a postorder version. The others are not too hard to write. I guess it doesn't really matter anyway.

mossheim commented 6 years ago

Still, I feel like it should be a goal to specialize extension Tree where Branch == Leaf

mossheim commented 6 years ago
extension Tree where Branch == Leaf {
    public func reduceValues <A> (_ initial: A, _ nextPartial: (A, Leaf) -> A) -> A {
        switch self {
        case .leaf(let value):
            return nextPartial(initial, value)
        case .branch(let value, let trees):
            return nextPartial(trees.reduce(initial) { $1.reduce($0, nextPartial) }, value)
        }
    }
}
mossheim commented 6 years ago
    func testReduceBranchEqLeaf() {
        let a = Tree.branch(5, [
            .leaf(1),
            .branch(2, [
                .leaf(2),
                .leaf(3)
            ]),
            .leaf(4)
        ])

        let expected = 17

        XCTAssertEqual(a.reduceValues(0, +), expected)
    }

without changing the function name, swiftc couldn't infer types. This is pretty damn cool. How I've missed FP...