2017-09-02 66 views
7

我怎样才能在Go表达一个类型,是“(字符串或其他类似的列表)名单”“(字符串或‘其他如切片’)的片”的类型?基本上,好ol'“树表示为无限的列表和作为值的东西(在这个例子中的字符串)的东西”如何表达

我正在寻找一个S表达式的最简单的可能表示(它本身就是最简单的AST),在Python中看起来像这样:

sexp1 = ["+", "x", "y", ["*", "10", "myVal"]] 
sexp2 = ["foo" "bar" "baz"] 
sexp3 = [ [ [["gooo"], "moo"] ], "too", ["yoo", "2"] ] 

这些表达式在Go中有什么类型?显然[][]string不起作用,因为这不工作:

func makeSexp(parserName string, values ...[][]string) [][]string { 
    return append([]string{parserName}, values...) 
} 

(编译错误:1。cannot use values (type [][][]string) as type []string in append,2 cannot use append([]string literal, values...) (type []string) as type [][]string in return argument

...而完全无类型版本的Works(但我不想完全放弃类型安全):

func makeSexp(parserName string, values ...interface{}) interface{} { 
    return append([]interface{}{parserName}, values...) 
} 
+0

创建'ListHolder'和'StringHolder'类型。用两种方法定义接口:'What_type_are_you'和'Give_me_your_value'。 –

回答

5

不幸的是,Go不支持代数数据类型,所以你最好的选择,使其类型安全是创建一个未导出接口,和m它有两种实现方式:

type sExp interface { 
    sExp() 
} 

type s string 

func (s) sExp() {} 

type l []sExp 

func (l) sExp() {} 

// ... 
var sexp1 sExp = l{s("+"), s("1"), s("2"), l{s("*"), s("10"), s("myVal")}} 

这基本上是Protobuf编译器如何处理例如oneof个案。这仍然需要大量的类型切换或类型断言才能使用,但至少可以确保模块外部没有任何东西能够修补它。

游乐场:https://play.golang.org/p/KOvFqJEvxZ

+0

2关于这个问题,虽然:*(1)*为什么会'SEXP()'空方法甚至需要,因为它看起来像这基本上等同于走样'接口{}''来和sExp' *(2)*为什么类型不像func makeSexp(parserName string,values ... sExp)sExp'的函数满足定义为'type ASTreeMaker func(parserName string,values ... interface {})interface {}'的类型,即使我们只是做了'型SEXP接口{}'所以他们现在应该清楚的是一样的......我越来越糊涂:) – NeuronQ

+1

@NeuronQ的方法是有作为标记。以便模块外部没有人可以创建一个满足'Exp'接口的类型。你不能将'[] sExp'分配给'[] interface {}',因为这些类型不是[assignable](https://golang.org/ref/spec#Assignability)。甚至有关于此的[FAQ条目](https://golang.org/doc/faq#convert_slice_with_same_underlying_type)。 –

+0

uhm,ok,*“你不能改变复合类型元素的名称(和方法集合)”*是一个大开眼界,thx指向那个......但我可能会使用'interface { ]'直到我开始工作的设计,然后看看是否有更安全的方法来实现它...我的实际要求是模块的**调用者可以自由地*发明他自己的AST类型(比如SEXP这里),并解析模块本身会从外部 – NeuronQ