Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added some MultiSet functionality #13

Merged
merged 4 commits into from
Apr 9, 2020

Conversation

7h3kk1d
Copy link
Member

@7h3kk1d 7h3kk1d commented Apr 9, 2020

Default Methods:

  • MultiSet#contains plus overload for amount
  • MultiSet#union
  • MultiSet#difference
  • MultiSet#inclusion
  • MultiSet#intersection
  • MultiSet#symmetricDifference
  • MultiSet#merge
  • MultiSet.EquivalenceRelations#sameElements

Abstract Methods:

  • MultiSet#unique

I feel pretty gross about some of the current Javadocs but I thought it would be easier to discuss over PR.

Comment on lines +166 to +168
* Determine if another {@link MultiSet} is a subset of this {@link MultiSet}. A {@link MultiSet} <code>x</code> is
* a subset of <code>y</code> if for every <code>a^i</code> in <code>x</code> there exists <code>a^j</code> in
* <code>y</code> where ^ denotes the number of occurrences in the multiset.
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Futile attempt to use a notation for occurrences.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The notation is a challenge. I went with (a * k) in the toString. It's annoying. I don't think I like the xor/exponentiation notation but it won't prevent a merge, so don't sweat it.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I shamelessly stole it from wikipedia and a couple papers.

default MultiSet<A> merge(Semigroup<Natural> semigroup, MultiSet<A> other) {
return foldLeft((acc, a) -> semigroup
.apply(get(a), other.get(a)).match(constantly(acc), result -> acc.add(a, result)),
this.difference(this), // Empty but the correct impl.
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't @ me.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do you need to start from empty? Why not just fold other, doing a get on this for the other a, applying the values via the semigroup? What am I missing here?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You need to go over all the keys in both to make sure you still call the semigroup with zero. I could still use this but I'd have to set(via removeall + add) the value with result instead of just add.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

return foldLeft(curried(ms -> into((a, k_) -> {
                    Natural k = ms.get(a);
                    return semigroup.apply(k, k_)
                            .match(__ -> ms.removeAll(a),
                                   nzk -> eq(nzk, k)
                                          ? ms
                                          : lt(nzk, k)
                                            ? k.minus(nzk).flatMap(CoProduct2::projectB)
                                                    .match(constantly(ms), diff -> ms.add(a, diff))
                                            : nzk.minus(k).flatMap(CoProduct2::projectB)
                                                    .match(constantly(ms), diff -> ms.remove(a, diff)));
                })),
                this,
                other);

It's painful, but I don't like the idea of the starting difference, that seems like a hack.

I'm currently working on making MultiSet subtype Map at which point you'll get put(a, NonZero k) which will make this implementation trivial, so I won't sweat this too much right now.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll add this in. Just for my own clarity, is it significantly cheaper to put by adding/removing vs removeAll + add.

Default Methods:
- MultiSet#contains plus overload for amount
- MultiSet#union
- MultiSet#difference
- MultiSet#inclusion
- MultiSet#intersection
- MultiSet#symmetricDifference
- MultiSet#merge
- MultiSet.EquivalenceRelations#sameElements

Abstract Methods:
- MultiSet#unique
* @see MultiSet#merge
*/
default MultiSet<A> union(MultiSet<A> other) {
return merge(max(), other);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good

* @see MultiSet#merge
*/
default MultiSet<A> intersection(MultiSet<A> other) {
return merge(min(), other);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also good.

* @see MultiSet#merge
*/
default MultiSet<A> symmetricDifference(MultiSet<A> other) {
return difference(other).union(other.difference(this));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This stands out to me as another case where the union definition for MultiSet being the supremum makes sense, and the symmetry with Set is intuitive. Nice.

Copy link
Member

@jnape jnape left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm going to finish my spike of making MultiSet subtype Map: if it ends up happening, we'll want to remove unique from MultiSet as it would just be keys and make my proposed implementation of merge much simpler. If it doesn't end up panning out, I'll merge this PR in as-is.

Thanks for the wonderful work.

@jnape
Copy link
Member

jnape commented Apr 9, 2020

@7h3kk1d do me a favor and comment on #17 so I can assign it to you

@jnape jnape linked an issue Apr 9, 2020 that may be closed by this pull request
- Fix Javadoc typos
- Stop using empty hack
- Fix MultiSet#merge
- Rename MultiSet.EquivalenceRelations.sameElements
@jnape jnape merged commit f1096a0 into palatable:master Apr 9, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

MultiSet setops
2 participants