部分减少流程

huangapple go评论75阅读模式
英文:

"Partly" reducing a Stream

问题

以下是您要翻译的内容:

给定以下上下文

public interface IAdditive<T> {
    /** 如果仍然能够进行crunch,则返回True。 */
    boolean canCrunch();
    /** 如果能够与B一起crunch,则返回True。 */
    boolean canCrunch(T other);
    /** 返回一个新的T,它是this和other的和。 */
    T crunch(T other);    
}

class A implements IAdditive<A> {
    ..
    A crunch(A other) {...}
}

class B extends A {
    ...
    B crunch(B other) {...}
}

class C implements IAdditive<C> {
    ...
    C crunch(C other) {...}
}

现在我想要crunch一组实现的流

/** 在可能的情况下对流进行crunch处理 */
public Stream<A> crunchStream(Stream s) {
    return s.map(...);
}

我卡在了我的相当天真的方法上

public Set<A> collect(Stream<A> stream) {
    Set<I> res = new HashSet<>();
    Set<I> set = stream
            .filter(IAdditive::canCrunch)
            .collect(Collectors.toSet());
    set.forEach(setItem -> set.stream()
            .filter(concurrentItem -> concurrentItem.canCrunch(setItem))
            .map(setItem::crunch)
            .forEach(res::add));
    return res;
}

这应该是有缺陷的我展开了流增加了强制性的复杂性如果我想要接口在默认方法中提供这个功能我将不得不使用rawtypes

我相信我需要一些帮助 :-)
英文:

Given the following context:

public interface IAdditive&lt;T&gt; {
/** True if still capable to crunch. */
boolean canCrunch();
/** True if capable to crunch with B. */
boolean canCrunch(T other);
/** Returns a new T which is the sum of this and other */
T crunch(T other);    
}

class A implements IAdditive&lt;A&gt; {
..
A crunch(A other) {...}
}

class B extends A {
...
B crunch(B other) {...}
}

class C implements IAdditive&lt;C&gt; {
...
C crunch(C other) {...}
}

Now I want to "crunch" a Stream of Implementations

/** Chrunches the streams where possible */
public Stream&lt;A&gt; crunchStream(Stream s) {
return s.map(...);
}

I am stuck with my rather naive approach:

public Set&lt;A&gt; collect(Stream&lt;A&gt; stream) {
Set&lt;I&gt; res = new HashSet&lt;&gt;();
Set&lt;I&gt; set = stream
.filter(IAdditive::canCrunch)
.collect(Collectors.toSet());
set.forEach(setItem -&gt; set.stream()
.filter(concurrentItem -&gt; concurrentItem.canCrunch(setItem))
.map(setItem::crunch)
.forEach(res::add));
return res;
}

That should be flawed. I am unfolding the stream, add mandatory complexity, and If I want the interface to offer that in a default method I would have to use rawtypes.

I believe I could use some help 部分减少流程

答案1

得分: 0

根据您的评论,我认为这就是您想要的:

public static interface Additive<T> {
    default public Additive<T> crunch(Additive<T> other) { return null; }
}

public static class A implements Additive<A> {};

public static class B implements Additive<B> {};

public static class C implements Additive<C> {};

/**
 * 获取任意 Additive 流,并返回一个包含每种类型的一个 crunched Additive 的 Set
 * 
 * @param stream 要 crunch 的 additives 流
 * @return crunched 集合
 */
public Set<Additive<?>> crunch(Stream<Additive<?>> stream) {
    return stream
        .collect(Collectors.groupingBy(o -> o.getClass()))
        .values().stream()
        .map(values -> values.stream().reduce(Additive::crunch).get())
        .collect(Collectors.toSet());
}

/**
 * 获取任意 Additive 流,并返回一个包含每种类型的一个 crunched Additive 的 Collection
 * 
 * @param stream 要 crunch 的 additives 流
 * @return crunched 集合
 */
public Collection<Additive<Object>> crunchMap(Stream<Additive<?>> stream) {
    return stream
        .collect(Collectors.toMap(k -> k.getClass(), v -> (Additive<Object>) v, (a, b) -> a.crunch(b))).values();
}

这两个方法都应该产生所需的输出。它们接受一个包含任意 Additive 的流,按实际类型对它们进行分组,然后将相同类型的对象 crunched 成一个对象,并最终返回一个仅包含每种类型的一个对象的 SetCollection。我已经提供了两种不同的方法。第一种方法首先将对象分组到一个映射中,然后对所有相似类型进行 crunch。这可能是更易理解的方法。第二种方法使用映射收集器,将每个 additive 映射到其类作为键,并对值执行 no-op 操作,然后在键冲突时,将新值与旧值 crunched 并放入映射中。它涉及的内容较多,阅读起来较困难,并且需要更多的泛型技巧才能使其正常工作。请注意,将流作为参数传递没有任何好处 - 只需传递集合即可。实际上,不建议传递流,因为您无法确定哪些人已经在流上操作过,哪些没有,在流已关闭时会导致异常。请注意,我没有使用您的 canCrunch 方法,而是依赖于每种类型只能对自己进行 crunched 的事实,但不能少于这个数量。这比处理无法被压缩的相同类型的对象要容易得多。如果您希望实现这一点,您需要某种形式的区分它们的方式,并相应地更改分类器。

英文:

Based on your comments, I think this is what you want:

public static interface Additive&lt;T&gt; {
default public Additive&lt;T&gt; crunch(Additive&lt;T&gt; other) { return null; }
}
public static class A implements Additive&lt;A&gt; {};
public static class B implements Additive&lt;B&gt; {};
public static class C implements Additive&lt;C&gt; {};
/**
* Takes a stream of arbitrary Additives and returns a Set containing
* only one crunched Additive for each type
* 
* @param stream additives to crunch
* @return crunched set
*/
public Set&lt;Additive&lt;?&gt;&gt; crunch(Stream&lt;Additive&lt;?&gt;&gt; stream) {
return stream
.collect(Collectors.groupingBy(o -&gt; o.getClass()))
.values().stream()
.map(values -&gt; values.stream().reduce(Additive::crunch).get())
.collect(Collectors.toSet());
}
/**
* Takes a stream of arbitrary Additives and returns a Set containing
* only one crunched Additive for each type
* 
* @param stream additives to crunch
* @return crunched set
*/
public Collection&lt;Additive&lt;Object&gt;&gt; crunchMap(Stream&lt;Additive&lt;?&gt;&gt; stream) {
return stream
.collect(Collectors.toMap(k -&gt; k.getClass(), v -&gt; (Additive&lt;Object&gt;) v, (a, b) -&gt; a.crunch(b))).values();
}

Both method should produce the desired output. They take a Stream containing arbitrary Additives, group them by actual type, then crunch the ones of the same type into only one object, and finally return a Set or Collection containing only one object for each type of Additive.

I have laid out two different approaches. The first approach first groups into a map, and then crunches all similar types. this is perhaps the easier to understand method.

the second approach uses a mapping collector, mapping each additive to its class as key, and doing a no-op for the value, then, on a key collision, crunches the new value with the old values and puts that into the map. its a bit more involved, a bit harder to read and needs a bit more generic-fu to actually get to work.

Note that passing a Stream as parameter doesn't have any benefit - just pass the collection. In fact, passing streams is discouraged as you can never be sure who has already operated on the stream and who hasn't, leading to exceptions when the stream is already closed.

Note that I do not use your canCrunch method, but rely on the fact that each type can crunch itself and only itself, but no less. This is far easier to enforce and deal with than objects of same types not being able to be crushed. if you want that, you need some form to distinguish them and to change the classificator accordingly.

huangapple
  • 本文由 发表于 2020年7月25日 07:19:01
  • 转载请务必保留本文链接:https://go.coder-hub.com/63082621.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定