我有一些Java经验,我正在学习Ruby。我遇到了一个ruby程序如下:
class Tree
attr_accessor :children, :node_name
def initialize(name, children=[])
@children = children
@node_name = name
end
def visit_all(&block)
visit &block
children.each {|c| c.visit_all &block}
end
def visit(&block)
block.call self
end
end
ruby_tree = Tree.new( "Ruby" ,
[Tree.new("Reia" ),
Tree.new("MacRuby" )] )
puts "Visiting a node"
ruby_tree.visit {|node| puts node.node_name}
puts
puts "visiting entire tree"
ruby_tree.visit_all {|node| puts node.node_name}
当我看到ruby语言的强大功能时,我想用Java编写类似的代码,如下所示:
public class Tree {
private String name;
private Tree[] children;
Tree(String name, Tree[] children) {
this.name = name;
this.children = children;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Tree[] getChildren() {
return children;
}
public void setChildren(Tree[] children) {
this.children = children;
}
public static void main(String[] args) {
Tree myTree = new Tree("Ruby", new Tree[] {
new Tree("Reia", new Tree[] {}),
new Tree("MacRuby", new Tree[] {}) });
myTree.visit();
myTree.visit_all();
}
public void visit() {
System.out.println(getName());
}
public void visit_all() {
visit();
for (Tree tree : children) {
tree.visit();
}
}
}
问题: 我知道这里的java版本并不像Ruby那样灵活。我在Java中有类似的东西可以实现像Ruby一样提供的灵活性水平吗?
答案 0 :(得分:1)
首先,请注意:该代码绝对可怕。它几乎不提供封装,它左右泄漏实现细节,Tree
对象无法维护自己的不变量或状态。其次,它没有将完全与Ruby的集合框架集成。
因此,我的Java翻译也同样可怕, 也没有与Java的集合框架集成。
您的Java代码与Ruby相比的两个最大缺点是
String
,而在Ruby版本中,它可以是任何对象,甚至是同一对象的混合树,和第一个问题在Java中无法轻易解决。您可以使集合具有通用性,以便它可以保存任何类型的元素,但使其成为异构(即能够在同一集合中保存不同类型的元素)将会很多工作。所以,我坚持使用部分解决方案:使Tree
通用。
第二个问题可以通过让迭代器获取包含代码的对象来解决。毕竟,第一类子程序与只有一种方法的对象基本相同。 (Java 8将带走一些痛苦,我在代码中包含了一些例子。)
import java.util.Collection;
import java.util.ArrayList;
interface Consumer<T> {
void accept(T e);
}
// In Java 8, this interface is already part of the JRE.
// Just replace the 3 lines above with this import:
//import java.util.function.Consumer;
class Tree<T> {
private String nodeName;
private Collection<Tree<T>> children = new ArrayList<>();
Tree(String name, Collection<Tree<T>> children) {
nodeName = name;
this.children = children;
}
Tree(String name) {
nodeName = name;
}
public String getNodeName() { return nodeName; }
public void setNodeName(String name) { nodeName = name; }
public Collection<Tree<T>> getChildren() { return children; }
public void setChildren(Collection<Tree<T>> children) { this.children = children; }
void visitAll(Consumer<Tree<T>> block) {
visit(block);
for (Tree<T> tree : children) tree.visitAll(block);
}
void visit(Consumer<Tree<T>> block) {
block.accept(this);
}
public static void main(String... args) {
ArrayList<Tree<String>> children = new ArrayList<>();
children.add(new Tree<String>("Reia"));
children.add(new Tree<String>("MacRuby"));
Tree<String> rubyTree = new Tree<>("Ruby", children);
System.out.println("Visiting a node");
rubyTree.visit(new Consumer<Tree<String>>() {
public void accept(Tree<String> node) {
System.out.println(node.getNodeName());
}
});
// In Java 8, you can use a lambda.
// Just replace the 5 lines above with this line:
//rubyTree.visit(node -> System.out.println(node.getNodeName()));
System.out.println();
System.out.println("Visiting entire tree");
rubyTree.visitAll(new Consumer<Tree<String>>() {
public void accept(Tree<String> node) {
System.out.println(node.getNodeName());
}
});
// In Java 8, you can use a lambda.
// Just replace the 5 lines above with this line:
//rubyTree.visitAll(node -> System.out.println(node.getNodeName()));
}
}
答案 1 :(得分:0)
def visit(&block)
block.call self
end
更好地写成
def visit
yield self
end
此外,visit_all
和visit
会更加惯用地编写为符合Enumerable
模块:
class Tree
include Enumerable
# ...
def each(&cb)
cb.call(@element)
children.each end |child|
child.each(&cb) if child.respond_to?(:each)
end
end
end
通过这种方式,您可以免费获得各种其他内容,例如: max
...而且,每个人都知道each
会对所有元素应用一个块,而他们必须深入挖掘您的API文档或代码才能看到该函数被调用visit_all
编辑:删除了一块,因为我显然是个白痴。感谢steenslag让我做对了。