Ceylon 对可能都被视为某种数组的事物有几个不同的概念:List
, Tuple
, Sequence
, Sequential
, Iterable
, Array
, Collection
,Category
等。这些类型有什么不同,我应该什么时候使用它们?
2 回答
开始了解这些基本知识的最佳地点是锡兰之旅。而深入了解这些东西的地方就是模块 API。查看这些的源文件也很有帮助。
像所有优秀的现代编程语言一样,前几个接口是超级抽象的。它们围绕一个正式成员构建,并通过一堆默认成员和实际成员提供其功能。(在 Java 8 之前创建的编程语言中,您可能听说过这些称为“特征”,以将它们与只有正式成员且没有功能的传统接口区分开来。)
类别
我们先从界面说起Category
。它表示您可以询问“此集合是否包含此对象”的类型,但您不一定能够从集合中获取任何成员。它的正式成员是:
shared formal Boolean contains(Element element)
一个例子可能是一个大数的所有因子的集合——你可以有效地测试任何整数是否是因子,但不能有效地得到所有因子。
可迭代
的一个子类型Category
是 interface Iterable
。它表示您可以一次获取每个元素的类型,但不一定索引元素。元素可能没有明确定义的顺序。这些元素甚至可能还不存在,而是动态生成的。收藏甚至可能无限长!它的正式成员是:
shared formal Iterator<Element> iterator()
一个例子是像标准输出这样的字符流。另一个例子是提供给 for 循环的整数范围,一次生成一个数字的内存效率更高。
这是 Ceylon 中的一种特殊类型,可以缩写{Element*}
或分别{Element+}
取决于可迭代对象是否为空或绝对不为空。
收藏
的子类型之一Iterable
是接口Collection
。它有一个正式成员:
shared formal Collection<Element> clone()
但这并不重要。定义 a 的重要内容Collection
是文档中的这一行:
所有
Collection
s 都必须支持明确定义的值相等概念,但相等的定义取决于集合的类型。
基本上,aCollection
是一个集合,其结构定义良好,足以相互等同且可克隆。对定义良好的结构的这种要求意味着这是最后一个超级抽象接口,其余的将看起来像更熟悉的集合。
列表
的子类型之一Collection
是接口List
。它表示一个集合,我们可以通过索引 ( myList[42]
) 获取其元素。当你的函数需要一个数组来取出东西时使用这种类型,但不关心它是可变的还是不可变的。它有一些正式的方法,但重要的一个来自它的另一个超类型Correspondence
:
shared formal Item? get(Integer key)
顺序,顺序,空
最重要List
的子类型是接口Sequential
。它代表一个不可变的List
. Ceylon 喜欢这种类型,并围绕它构建了很多语法。它被称为[Element*]
和Element[]
。它有两个子类型:
Empty
(aka[]
),代表空集合Sequence
(aka[Element+]
),代表非空集合。
因为集合是不可变的,所以你可以用它们做很多事情,而不能用可变集合做。一方面,许多操作可能会null
在空列表上失败,比如reduce
and first
,但是如果您首先测试类型是,Sequence
那么您可以保证这些操作将始终成功,因为集合以后不能变为空(它们毕竟是不可变的) .
元组
Sequence
is的一个非常特殊的子类型,Tuple
这里列出的第一个真正的类。与Sequence
所有元素都被限制为一种 type不同Element
,aTuple
对每个元素都有一个类型。它在 Ceylon 中获得了特殊的语法,其中[String, Integer, String]
是一个不可变的列表,其中正好包含三个元素,这些元素完全按照该顺序排列。
大批
List
is的另一个子类型Array
,也是一个真正的类。这是熟悉的 Java 数组,一个可变的固定大小的元素列表。
drhagen 已经很好地回答了你的问题的第一部分,所以我只想说一下第二部分:你什么时候使用哪种类型?
一般来说:编写函数时,使其接受支持所需操作的最通用类型。到目前为止,如此明显。
Category
非常抽象,很少有用。
Iterable
如果您期望一些您将要迭代的元素流(或使用流操作,如 , 等),则应该使用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" };
相当于
printAll { things = { "a", "b", "c" }; };
这允许您制作 DSL 样式的表达式;这次旅行有一些很好的例子。
Collection
就像 一样Correspondence
,相当抽象,根据我的经验,很少直接使用。
List
听起来它应该是一种经常使用的类型,但实际上我不记得经常使用它。我不确定为什么。我似乎跳过它并将我的参数声明为Iterable
or Sequential
。
Sequential
当你想要一个不可变的、Sequence
固定长度的列表时。它还有一些语法糖:可变参数方法void foo(String* bar)
是 aSequential
或Sequence
参数的快捷方式。Sequential
还允许您使用运算符,它通常与andnonempty
结合使用可以很好地工作: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
andSequence
当我要对流进行多次迭代时(这对于泛型来说可能很昂贵Iterable
),尽管List
可能是更好的接口。
Tuple
永远不应该被用作Tuple
(除非在极少数情况下你对它们进行抽象),但使用[X, Y, Z]
语法糖它通常很有用。您通常可以将Sequential
成员细化Tuple
为子类中的 a,例如。G。超类具有 a <String|Integer>[] elements
,其中一个子类中已知为 a [String, Integer] elements
。
Array
我从来没有用作参数类型,只是很少用作类来实例化和使用。