为什么这个未使用的流会影响结果?

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

Why does this unused stream have an impact on the result?

问题

以下是翻译好的代码部分:

private List<Var> getAllDefinedVars(List<Var> allVars) {
    List<Var> collect = allVars.stream().filter(v -> false).collect(Collectors.toList());
    return collect;
}

请注意,此行代码实际上没有对结果产生影响,因为它只是使用流过滤了一个始终为假的条件,从而创建了一个空的列表。删除这行代码不应该对最终结果产生影响。

英文:

Why does the following effectivly unused line (in the method: getAllDefinedVars) have an impact on the end result:
List<Var> collect = allVars.stream().filter(v -> false).collect(Collectors.toList());

If I remove the whole method and the one line of code, which is calling this method (first line in generateOneSetOfBools), I get an other result in the end.
I would expect such a behavior if...

  1. the mentioned line had an impact on the List allVars or any other variable
  2. the result of the stream would be used

As far as I can see, none of this happens. Therefore a removal of this whole method should have no impact on the result.

To convince yourself you can run the main the first time with the method containing the stream and the second time without this method and then compare the output.

public class PairwiseMain {
	
	private static PairwisePermutator pp = new PairwisePermutator();

	public static void main(String[] args) {
		for(int i = 0; i &lt; 20; i++) {
			pp.permutate(i);
			System.out.println(&quot;----------------&quot;);
		}
	}

}


import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.Stream;
public class PairwisePermutator {
private int n;
public Stream&lt;boolean[]&gt; permutate(int n) {
this.n = n;
List&lt;Var&gt; vars = generateRequiredVars(n);
List&lt;TupleSet&gt; table = generateTable(vars);
generateCombinations(table, vars);
return null;
}
private void generateCombinations(List&lt;TupleSet&gt; table, List&lt;Var&gt; vars) {
TupleSet start = findStartTupleSet(table);
if(start == null) {
return; 
}else {
Tuple t = start.tuples.get(0);
start.setVars(t);
generateOneSetOfBools(table, vars, new HashSet&lt;&gt;(Arrays.asList(start)));
}
System.out.println(Arrays.toString(vars.toArray()));
resetAllVars(vars);
generateCombinations(table, vars);
}
private void resetAllVars(List&lt;Var&gt; vars) {
vars.stream().forEach(v -&gt; v.value = null);
}
private void generateOneSetOfBools(List&lt;TupleSet&gt; table, List&lt;Var&gt; allVars, Set&lt;TupleSet&gt; start) {
List&lt;Var&gt; alreadyDefinedVars = getAllDefinedVars(allVars); //REMOVAL OF THIS LINE SHOULD HAVE NO IMPACT
Map&lt;TupleSet, Integer&gt; relevant = findRelevantTuplesInOtherTupleSets(table, start);
boolean changes = false;
boolean existsMultipleOptions = false;
TupleSet minimalMultipleOptions = null;
int minimalMultipleOptionsNumber = Integer.MAX_VALUE;
for(Map.Entry&lt;TupleSet, Integer&gt; entry : relevant.entrySet()) {
if(entry.getValue() == -1) {
removeTuple(entry.getKey());
start.add(entry.getKey());
changes = true;
}else if(entry.getValue() == 1) {	
changes = true;
removeTuple(entry.getKey(), table, allVars);
start.add(entry.getKey());
}else if(entry.getValue() &gt;= 2) {	 
existsMultipleOptions = true;
if(entry.getValue() &lt; minimalMultipleOptionsNumber) {
minimalMultipleOptionsNumber = entry.getValue();
minimalMultipleOptions = entry.getKey();
}
}
}
if(!changes &amp;&amp; existsMultipleOptions) {
removeRandomTuple(minimalMultipleOptions, start);
}else if(!changes) { 
setVars(table);
}
if(areAllVarsDefined(allVars)) {
removeMatchingTuples(table);
return;
}
generateOneSetOfBools(table, allVars, start);
}
private List&lt;Var&gt; getAllDefinedVars(List&lt;Var&gt; allVars) {
List&lt;Var&gt; collect = allVars.stream().filter(v -&gt; false).collect(Collectors.toList());
return collect;
}
private void removeMatchingTuples(List&lt;TupleSet&gt; table) {
for(TupleSet ts : table) {
boolean v1 = ts.x.value;
boolean v2 = ts.y.value;
Tuple toRemove = null;
for(Tuple t : ts.tuples) {
if(t.a == v1 &amp;&amp; t.b == v2) {
toRemove = t;
}
}
if(toRemove != null) {
ts.setVars(toRemove);
}
}
}
private boolean areAllVarsDefined(List&lt;Var&gt; allVars) {
return allVars.stream().filter(v -&gt; v.value != null).count() == n;
}
private void removeRandomTuple(TupleSet minimalMultipleOptions, Set&lt;TupleSet&gt; start) {
Tuple toRemove = minimalMultipleOptions.tuples.get(0);
minimalMultipleOptions.setVars(toRemove);
start.add(minimalMultipleOptions);
}
private void setVars(List&lt;TupleSet&gt; table) {
boolean foundOne = false;
for(TupleSet ts : table) {
if(ts.x.value == null &amp;&amp; ts.y.value == null) {
foundOne = setVars(ts);
}
}
if(!foundOne) {
for(TupleSet ts : table) {
if(ts.x.value == null || ts.y.value == null) {
if(ts.tuples.isEmpty()){
ts.x.value = true;
ts.y.value = false;
}else {
ts.setVars(ts.tuples.get(0));
}
}
}
}
}
private boolean setVars(TupleSet ts) {
boolean foundOne;
if(ts.tuples.isEmpty()){
ts.x.value = true;
ts.y.value = false;
foundOne = true;
}else {
ts.setVars(ts.tuples.get(0));
foundOne = true;
}
return foundOne;
}
private void removeTuple(TupleSet ts, List&lt;TupleSet&gt; table, List&lt;Var&gt; vars) {
Tuple toRemove = null;
if(ts.x.value != null) {
for(Tuple t : ts.tuples) {
if(t.a == ts.x.value) {
toRemove = t;
}
}
}else if(ts.y.value != null) {
for(Tuple t : ts.tuples) {
if(t.b == ts.y.value) {
toRemove = t;
}
}
}
if(toRemove != null)
ts.setVars(toRemove);
}
private void removeTuple(TupleSet ts) {
boolean v1 = ts.x.value;
boolean v2 = ts.y.value;
Tuple toRemove = null;
for(Tuple t : ts.tuples) {
if(t.a == v1 &amp;&amp; t.b == v2) {
toRemove = t;
}
}
if(toRemove != null) {
ts.setVars(toRemove);
}
}
private Map&lt;TupleSet, Integer&gt; findRelevantTuplesInOtherTupleSets(List&lt;TupleSet&gt; table, Set&lt;TupleSet&gt; alreadyVisited) {
Map&lt;TupleSet, Integer&gt; freedomMap = new HashMap&lt;&gt;();
for(TupleSet ts : table) {
if(!alreadyVisited.contains(ts)) {
int degreeOfFreedom = calculateDegreeOfFreedom(ts);
freedomMap.put(ts, degreeOfFreedom);			}
}
return freedomMap;
}
private int calculateDegreeOfFreedom(TupleSet ts) {
List&lt;Var&gt; alreadyDefinedVars = new ArrayList&lt;&gt;();
if(ts.x.value != null) {
alreadyDefinedVars.add(ts.x);
}
if(ts.y.value != null) {
alreadyDefinedVars.add(ts.y);
}
int degreeOfFreedom = 0;
if(areBothDefinied(alreadyDefinedVars)) {
degreeOfFreedom = -1; 
}else if(isExactlyOneDefinied(alreadyDefinedVars)) {
Var defined = getDefinedVar(alreadyDefinedVars);
if(defined == ts.x) {
degreeOfFreedom = (int) ts.tuples.stream().filter(t -&gt; t.a == ts.x.value).count();
}else if(defined == ts.y) {
degreeOfFreedom = (int) ts.tuples.stream().filter(t -&gt; t.b == ts.y.value).count();
}
}else if(alreadyDefinedVars.isEmpty()) {
degreeOfFreedom = ts.tuples.size(); 
}
return degreeOfFreedom;
}
private Var getDefinedVar(List&lt;Var&gt; alreadyDefinedVars) {
return alreadyDefinedVars.get(0);
}
private boolean isExactlyOneDefinied(List&lt;Var&gt; alreadyDefinedVars) {
return alreadyDefinedVars.size() == 1;
}
private boolean areBothDefinied(List&lt;Var&gt; alreadyDefinedVars) {
return alreadyDefinedVars.size() == 2;
}
private TupleSet findStartTupleSet(List&lt;TupleSet&gt; table) {
TupleSet startingTupleSet = null;
for(TupleSet ts : table) {
if(!ts.tuples.isEmpty()) {
startingTupleSet = ts;
}
}
return startingTupleSet;
}
private List&lt;Var&gt; generateRequiredVars(int n) {
return IntStream.range(0, n).mapToObj(number -&gt; new Var()).collect(Collectors.toList());
}
private List&lt;TupleSet&gt; generateTable(List&lt;Var&gt; vars) {
List&lt;TupleSet&gt; tupleSets = new ArrayList&lt;&gt;();
int kStart = 1;
for(int i = 0; i &lt; vars.size(); i++) {
for(int k = kStart; k &lt; vars.size(); k++) {
tupleSets.add(getInitSet(vars.get(i), vars.get(k)));
}
kStart++;
}
return tupleSets;
}
private TupleSet getInitSet(Var x, Var y){
return new TupleSet(x, y, (new ArrayList&lt;&gt;(Arrays.asList( new Tuple(true, true),
new Tuple(true, false),
new Tuple(false, true),
new Tuple(false, false)))));
}
private static class TupleSet{
Var x;
Var y;
ArrayList&lt;Tuple&gt; tuples;
TupleSet(Var x, Var y, ArrayList&lt;Tuple&gt; tuples){
this.x = x;
this.y = y;
this.tuples = tuples;
}
public void setVars(Tuple t) {
x.value = t.a;
y.value = t.b;
tuples.remove(t);
}
@Override
public String toString() {
return &quot;[&quot;+x+&quot;,&quot;+y+&quot;]&quot;+tuples;
}
}
private static class Var{
public Boolean value;
@Override
public String toString() {
return value == null ? &quot;null&quot; : value.toString();
}
}
private static class Tuple{
public Boolean a;
public Boolean b;
public Tuple(Boolean a, Boolean b) {
this.a = a;
this.b = b;
}
@Override
public String toString() {
return &quot;(&quot;+a+&quot;,&quot;+b+&quot;)&quot;;
}
}
}

An example of an impact would be running pp.permutate(5) with the following output:
With the stream: run the code as it was posted

[true, true, true, true, true]  
[false, false, false, true, false]  
[false, false, false, false, true]  
[true, true, true, false, false]  
[true, false, false, true, false]  
[false, false, true, true, false]  
[true, true, true, false, false] 

Without the stream: only delete the first line in generateOneSetOfBool()

[true, true, true, true, true]  
[false, false, false, true, false]  
[false, false, false, false, true]  
[true, true, true, false, false]  
[false, true, true, true, false]  
[true, false, true, true, false]  
[true, true, false, false, false]  

Both outputs differ, for example the last line

答案1

得分: 2

你得到的结果差异与这行代码 List collect = allVars.stream().filter(v -&gt; false).collect(Collectors.toList()); 无关。问题在于你的算法是非确定性的。我已经拿到了你的代码并为相同的输入多次运行它:

for(int i = 0; i &lt; 20; i++) {
    pp.permutate(5);
    System.out.println(&quot;----------------&quot;);
}

无论是否有你提到的那行代码,我都得到了你提到的两种输出(只出现这两种变化):

[true, true, true, true, true]
[false, false, false, true, false]
[false, false, false, false, true]
[true, true, true, false, false]
[true, false, false, true, false]
[false, false, true, true, false]
[true, true, true, false, false]
----------------
[true, true, true, true, true]
[false, false, false, true, false]
[false, false, false, false, true]
[true, true, true, false, false]
[false, true, true, true, false]
[true, false, true, true, false]
[true, true, false, false, false]

我没有逐行查看你的代码,所以我不确定,但我猜想你的一些集合不保证元素的顺序。

然而,有趣的是,当我多次运行 main 时,不同版本的输出出现的顺序总是相同的(或者至少在我尝试了5次中如此)。更有趣的是,当删除你提到的那行代码时,那个顺序会发生变化,但在调用 main 之间保持不变。考虑到在不同的机器上表现不同,我的结论可能是与程序在内存中的放置方式有关。

英文:

The difference in results you're getting has nothing to do with the line List collect = allVars.stream().filter(v -&gt; false).collect(Collectors.toList());. The problem is that your algorithm is non-deterministic. I've taken your code and run it multiple time for the same input:

for(int i = 0; i &lt; 20; i++) {
pp.permutate(5);
System.out.println(&quot;----------------&quot;);
}

Doesn't matter if there was the line you're talking about or not - I was getting both outputs you're mentioning (only those two variations appear tho):

[true, true, true, true, true]
[false, false, false, true, false]
[false, false, false, false, true]
[true, true, true, false, false]
[true, false, false, true, false]
[false, false, true, true, false]
[true, true, true, false, false]
----------------
[true, true, true, true, true]
[false, false, false, true, false]
[false, false, false, false, true]
[true, true, true, false, false]
[false, true, true, true, false]
[true, false, true, true, false]
[true, true, false, false, false]

I didn't go line by line through your code so I'm not sure but I'd guess that some of your collections doesn't guarantee anything about the order of elements.

However, what's interesting is that when I run main multiple time the order in which different versions of the output appear will always be the same (or at least in 5 times I tried). What is more, when that one line you mentioned is removed then that order changes - but stays the same between calls of main. When we add to that the fact that on different machine it behaves differently my conclusion would be that it might have something to with how the program is placed in memory.

huangapple
  • 本文由 发表于 2020年8月6日 21:49:10
  • 转载请务必保留本文链接:https://go.coder-hub.com/63285034.html
匿名

发表评论

匿名网友

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

确定