Ale*_*ard 8 java algorithm lambda java-8 java-stream
我有一个简单的分支定界算法,适用于旅行商问题的变体,我认为尝试将其转换为使用Java 8 Stream API会很有趣.然而,我很难在不依赖副作用的情况下弄清楚如何做到这一点.
初始代码
int bound = Integer.MAX_VALUE;
List<Location> bestPath = null;
while(!queue.isEmpty()) {
Node curr = queue.poll();
//bound exceeds best, bail
if (curr.getBound() >= bound) {
return bestPath;
}
//have a complete path, save it
if(curr.getPath().size() == locations.size()) {
bestPath = curr.getPath();
bound = curr.getBound();
continue;
}
//incomplete path - add all possible next steps
Set<Location> unvisited = new HashSet<>(locations);
unvisited.removeAll(curr.getPath());
for (Location l : unvisited) {
List<Location> newPath = new ArrayList<>(curr.getPath());
newPath.add(l);
Node newNode = new Node(newPath, getBoundForPath(newPath));
if (newNode.getBound() <= bound){
queue.add(newNode);
}
}
}
Run Code Online (Sandbox Code Playgroud)
我第一次尝试将其转换为Stream API,并提出以下建议:
Java 8版
Consumer<Node> nodeConsumer = node -> {
if(node.getPath().size() == locations.size() ) {
bestPath = node.getPath();
bound = node.getBound();
} else {
locations.stream()
.filter(l -> !node.getPath().contains(l))
.map(l -> {
List<Location> newPath = new ArrayList<>(node.getPath());
newPath.add(s);
return new Node(newPath, getBoundForPath(newPath));
})
.filter(newNode -> newNode.getBound() <= bound)
.forEach(queue::add);
}
};
Stream.generate(() -> queue.poll())
.peek(nodeConsumer)
.filter(s -> s.getBound() > bound)
.findFirst();
return bestPath;
Run Code Online (Sandbox Code Playgroud)
主要问题是nodeConsumer必须引用bestPath和bound,它们不是最终变量.我可以让他们最终的AtomicReference变量解决这个问题,但我觉得这样违反了流API的精神.任何人都可以帮助我将初始算法提炼成更惯用的实现吗?
我想知道使用是否reduce
是解决此问题的方法,因为它允许您在不需要外部变量的情况下跟踪值。
类似于以下内容(我不得不猜测上面代码的一些细节,但希望我走在正确的轨道上)。
\n\n final BiFunction<Entry<Integer, List<Location>>, Node, Entry<Integer, List<Location>>> accumulator\n = (identity, node) -> {\n if (node.getPath().size() == locations.size() ) {\n return new SimpleEntry<>(node.getBound(), node.getPath());\n } else {\n locations.stream()\n .filter(l -> !node.getPath().contains(l))\n .map(l -> {\n List<Location> newPath = new ArrayList<>(node.getPath());\n newPath.add(l);\n return new Node(newPath, getBoundForPath(newPath));\n })\n .filter(newNode -> newNode.getBound() <= identity.getKey())\n .forEach(queue::add);\n return identity;\n }\n };\n\n final BinaryOperator<Entry<Integer, List<Location>>> combiner\n = (left, right) -> left.getKey() < right.getKey() ? left : right;\n\n final Entry<Integer, List<Location>> identity\n = new SimpleEntry<>(Integer.MAX_VALUE, null);\n\n final List<Location> bestValue = Stream.generate(queue::poll)\n .reduce(identity, accumulator, combiner)\n .getValue();\n
Run Code Online (Sandbox Code Playgroud)\n\n或者,您可以查看 using Seq
in jOO\xce\xbb(Streams 的顺序扩展),并foldLeft
改为使用。