Ceylon对于可能被视为某种阵列的事物有几个不同的概念:List
,Tuple
,Sequence
,Sequential
,Iterable
, Array
,Collection
,Category
等。这些类型的不同之处是什么?我应该何时使用它们?
答案 0 :(得分:8)
在基本级别开始了解这些事情的最佳地点是Ceylon tour。深入了解这些事情的地方是module API。查看source files for these。
也很有帮助与所有优秀的现代编程语言一样,前几个接口都是超级抽象的。它们围绕一个正式成员构建,并通过一组默认和实际成员提供其功能。 (在Java 8之前创建的编程语言中,您可能听说过这些称为“traits”的区域,以区别于只有正式成员且没有功能的传统接口。)
让我们首先谈谈界面Category
。它表示您可以询问的类型“此集合是否包含此对象”,但您可能不一定能够从集合中获取任何成员。它的正式成员是:
shared formal Boolean contains(Element element)
一个例子可能是大数的所有因子的集合 - 你可以有效地测试任何整数是否是一个因素,但不能有效地得到所有因子。
Category
的子类型是接口Iterable
。它表示一种类型,您可以从中一次获取一个元素,但不一定索引元素。元素可能没有明确定义的顺序。这些元素甚至可能尚不存在,但是在运行中生成。该系列甚至可能无限长!它的正式成员是:
shared formal Iterator<Element> iterator()
一个例子是标准输出的字符流。另一个例子是提供给for循环的一系列整数,为了一次生成一个数字,它的内存效率更高。
这是Ceylon中的一种特殊类型,可以缩写为{Element*}
或{Element+}
,具体取决于iterable是否为空或绝对不为空。
Iterable
的一个子类型是接口Collection
。它有一个正式成员:
shared formal Collection<Element> clone()
但这并不重要。定义Collection
的重要一点是文档中的这一行:
所有
Collection
都需要支持明确定义的价值观 平等,但平等的定义取决于那种 集合。
基本上,Collection
是一个集合,其结构定义足以使彼此相等并且可克隆。这个对定义良好的结构的要求意味着这是超级抽象接口的最后一个,其余的看起来就像更熟悉的集合。
Collection
的一个子类型是接口List
。它表示一个集合,其元素我们可以通过索引(myList[42]
)获得。当你的函数需要一个数组来解决问题时使用这种类型,但不关心它是可变的还是不可变的。它有一些正式的方法,但重要的方法来自其他超类型Correspondence
:
shared formal Item? get(Integer key)
最重要的List
子类型是接口Sequential
。它代表一个不可变的List
。锡兰喜欢这种类型并围绕它构建了很多语法。它被称为[Element*]
和Element[]
。它有两个子类型:
Empty
(又名[]
),代表空集合Sequence
(又名[Element+]
),代表非空集合。因为集合是不可变的,所以你可以用它们做很多事情,而不能用可变集合做。例如,许多操作可能会在null
和first
空列表中Sequence
失败,但如果您先reduce
,则可以保证这些操作始终成功,因为集合之后不能变空(毕竟它们是不可变的)。
Sequence
的一个非常特殊的子类型是test that the type is Sequence
,这是此处列出的第一个真正的类。与Element
不同,其中所有元素都被约束为一种类型Tuple
,[String, Integer, String]
具有每个元素的类型。它在Ceylon中得到了特殊的语法,其中List
是一个完全由三个元素组成的不可变列表,正好按照这个顺序排列。
{{1}}的另一个子类型是Tuple
,也是一个真正的类。这是熟悉的Java数组,一个可变的固定大小的元素列表。
答案 1 :(得分:5)
drhagen已经很好地回答了你问题的第一部分,所以我只想对第二部分说一点:你什么时候使用哪种类型?
一般来说:在编写函数时,让它接受支持你需要的操作的最常规类型。到目前为止,很明显。
Category
非常抽象,很少有用。
Iterable
,{{1}等流操作),则应使用 filter
等等。)。
关于map
要考虑的另一件事是它在命名参数中有一些额外的语法糖:
Iterable
类型void printAll({Anything*} things, String prefix = "") {
for (thing in things) {
print(prefix + (thing?.string else "<null>"));
}
}
printAll { "a", "b", "c" };
printAll { prefix = "X"; "a", "b", "c" };
的任何参数都可以作为命名参数列表末尾的逗号分隔参数列表提供。也就是说,
Iterable
相当于
printAll { "a", "b", "c" };
这允许你制作DSL风格的表达; tour有一些不错的例子。
printAll { things = { "a", "b", "c" }; };
与Collection
一样,相当抽象,根据我的经验很少直接使用。
Correspondence
听起来应该是经常使用的类型,但实际上我不记得使用它了很多。我不知道为什么。我似乎跳过它并将我的参数声明为List
或Iterable
。
Sequential
和Sequential
是您需要不可变的固定长度列表的时间。它还有一些语法糖:Sequence
等变量方法是void foo(String* bar)
或Sequential
参数的快捷方式。 Sequence
还允许您使用Sequential
运算符,该运算符通常可与nonempty
和first
结合使用:
rest
我通常会使用String commaSeparated(String[] strings) {
if (nonempty strings) {
value sb = StringBuilder();
sb.append(strings.first); // known to exist
for (string in strings.rest) { // skip the first one
sb.append(", ").append(string);
// we don’t need a separate boolean to track if we need the comma or not :)
}
return sb.string;
} else {
return "";
}
}
和Sequential
当我要多次迭代一个流时(对于通用Sequence
而言可能很昂贵),尽管Iterable
可能是更好的界面。
List
永远不应该用作Tuple
(除非在极少数情况下您将对其进行抽象),但使用Tuple
语法糖它通常很有用。您通常可以将[X, Y, Z]
成员细化为子类中的Sequential
,例如: G。超类有一个Tuple
,它在一个子类中被称为<String|Integer>[] elements
。
[String, Integer] elements
我从未用作参数类型,很少作为实例化和使用的类。