4

我定义了一个treeNode创建节点的方法,它可以有子节点。简化的代码是:

def treeNode(text:String) (children: => Any) {
    val b = new TreeNode(text)
    children
}

当我使用这种方法时,我必须写:

treeNode("aaa") {
    treeNode("bbb") {}
    treeNode("ccc") {}
}

您可以看到它们没有子节点的叶节点,但它们必须有一个空块{}

有没有办法给参数children: => Any一个默认的无操作值,我可以把代码写成:

treeNode("aaa") {
    treeNode("bbb")
    treeNode("ccc")
}

帮助~

4

2 回答 2

9

问题在于你不能给它一个什么都不做的(默认)值;问题是即使你这样做了,具有多个参数块的函数至少必须为每个块有括号或大括号。

有一个例外:根本不需要引用隐式参数块。不幸的是,您不允许使用按名称调用的隐式参数,即使您是,您的签名也将允许任何随机隐式在该位置工作!

现在,有一种方法可以解决这个问题,为了完整起见,我将展示它,但我建议(假设你不只是想要另一个名字,比如leafNode)你把尾随{}留在那里。

如果您执行以下操作,您可以获得所需的语法。首先,您需要一个隐式参数,但您将其设为包装类(可以使用Function0已经存在的,但下一步可能会产生意想不到的后果):

trait AnyByName { def eval: Any }
def treeNode(text: String)(implicit children: AnyByName) = (text,children.eval)

现在你需要两件事——你需要能够将一个别名Any转换为你的新特性,并且你需要一个隐式的无操作可用。所以我们

implicit val nameForDoingNothing = new AnyByName { def eval = () }
implicit def wrap_any_by_name(a: => Any) = new AnyByName { def eval = a }

现在我们恢复了您所追求的行为:

scala> treeNode("Hi")
res1: (String, Any) = (Hi,())

scala> treeNode("Hi") { treeNode("there") }
res2: (String, Any) = (Hi,(there,()))

(在您的示例中,您不返回任何内容;我这样做是为了证明它有效。)

但是,为了避免某些 s,需要使用很多工具{},这就是为什么我建议仅在您预计这是一个非常频繁使用的 DSL并且这两个名称是不可接受的情况下才这样做的原因。(此外,如果您希望它被大量使用,那么它treeNode作为一个名字可能会很长;我建议只是node。)

于 2012-01-08T11:24:05.767 回答
6

AFAICT 默认情况下,您将无法传递一种空块。

treeNode顺便说一句,您真的可以通过创建另一个任务来根据您的功能简单地划分任务def leaf(text:String) = treeNode(t) {}

于 2012-01-08T10:49:00.750 回答