8

Ceylon 对可能都被视为某种数组的事物有几个不同的概念:List, Tuple, Sequence, Sequential, Iterable, Array, Collection,Category等。这些类型有什么不同,我应该什么时候使用它们?

4

2 回答 2

7

开始了解这些基本知识的最佳地点是锡兰之旅。而深入了解这些东西的地方就是模块 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是文档中的这一行:

所有Collections 都必须支持明确定义的值相等概念,但相等的定义取决于集合的类型。

基本上,aCollection是一个集合,其结构定义良好,足以相互等同且可克隆。对定义良好的结构的这种要求意味着这是最后一个超级抽象接口,其余的将看起来像更熟悉的集合。

列表

的子类型之一Collection是接口List。它表示一个集合,我们可以通过索引 ( myList[42]) 获取其元素。当你的函数需要一个数组来取出东西时使用这种类型,但不关心它是可变的还是不可变的。它有一些正式的方法,但重要的一个来自它的另一个超类型Correspondence

shared formal Item? get(Integer key)

顺序,顺序,空

最重要List的子类型是接口Sequential。它代表一个不可变的List. Ceylon 喜欢这种类型,并围绕它构建了很多语法。它被称为[Element*]Element[]。它有两个子类型:

  • Empty(aka []),代表空集合
  • Sequence(aka [Element+]),代表非空集合。

因为集合是不可变的,所以你可以用它们做很多事情,而不能用可变集合做。一方面,许多操作可能会null在空列表上失败,比如reduceand first,但是如果您首先测试类型是,Sequence那么您可以保证这些操作将始终成功,因为集合以后不能变为空(它们毕竟是不可变的) .

元组

Sequenceis的一个非常特殊的子类型,Tuple这里列出的第一个真正的类。与Sequence所有元素都被限制为一种 type不同Element,aTuple对每个元素都有一个类型。它在 Ceylon 中获得了特殊的语法,其中[String, Integer, String]是一个不可变的列表,其中正好包含三个元素,这些元素完全按照该顺序排列。

大批

Listis的另一个子类型Array,也是一个真正的类。这是熟悉的 Java 数组,一个可变的固定大小的元素列表。

于 2016-01-30T11:47:05.020 回答
4

drhagen 已经很好地回答了你的问题的第一部分,所以我只想说一下第二部分:你什么时候使用哪种类型?

一般来说:编写函数时,使其接受支持所需操作的最通用类型。到目前为止,如此明显。

Category非常抽象,很少有用。

Iterable如果您期望一些您将要迭代的元素流(或使用流操作,如 , 等),则应该使用filtermap

要考虑的另一件事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听起来它应该是一种经常使用的类型,但实际上我不记得经常使用它。我不确定为什么。我似乎跳过它并将我的参数声明为Iterableor Sequential

Sequential当你想要一个不可变的、Sequence固定长度的列表时。它还有一些语法糖:可变参数方法void foo(String* bar)是 aSequentialSequence参数的快捷方式。Sequential还允许您使用运算符,它通常与andnonempty结合使用可以很好地工作:firstrest

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 "";
    }
}

在线尝试

我通常使用SequentialandSequence当我要对流进行多次迭代时(这对于泛型来说可能很昂贵Iterable),尽管List可能是更好的接口。

Tuple永远不应该被用作Tuple(除非在极少数情况下你对它们进行抽象),但使用[X, Y, Z]语法糖它通常很有用。您通常可以将Sequential成员细化Tuple为子类中的 a,例如。G。超类具有 a <String|Integer>[] elements,其中一个子类中已知为 a [String, Integer] elements

Array我从来没有用作参数类型,只是很少用作类来实例化和使用。

于 2016-01-30T22:59:31.797 回答