繁体   English   中英

scala,返回类型指南——当更喜欢 seq、iterable、traversable 时

[英]scala, guidelines on return type - when prefer seq, iterable, traversable

您何时选择将给定函数的返回类型键入为Seq vs Iterable vs Traversable (或者在Seq的层次结构中更深)?

你是如何做出这个决定的? 我们有很多默认返回Seq的代码(通常从数据库查询和连续转换的结果开始)。 我倾向于在默认情况下使返回类型为Traversable ,并在特别期望给定顺序时使返回类型为Seq 但我没有充分的理由这样做。

我非常熟悉每个特征的定义,所以请不要定义术语来回答。

这是一个很好的问题。 你必须平衡两个问题:

  • (1) 尽量保持你的 API 通用,这样你可以稍后更改实现
  • (2) 给调用者一些有用的操作来对集合执行

其中 (1) 要求您对类型尽可能不具体(例如Iterable over Seq ),而 (2) 要求您相反。

即使返回类型只是Iterable ,你仍然可以返回让我们说一个Vector ,所以如果调用者希望获得额外的权力,它可以只调用.toSeq.toIndexedSeq ,并且该操作对于Vector来说是便宜的。

作为平衡的衡量标准,我要补充第三点:

  • (3) 使用一种反映数据组织方式的类型。 例如,当您可以假设数据确实有一个序列时,给出Seq 如果您可以假设不会出现两个相等的对象,请给出一个Set 等等。

这是我的经验法则:

  • 尝试只使用一小组集合: SetMapSeqIndexedSeq
  • 不过,我经常违反之前的规则,使用List Seq 它允许调用者与 cons 提取器进行模式匹配
  • 仅使用不可变类型(例如collection.immutable.Setcollection.immutable.IndexedSeq
  • 不使用具体实现( Vector ),而是使用提供相同 API 的通用类型( IndexedSeq
  • 如果你正在封装一个可变结构,只返回Iterator实例,那么调用者可以很容易地生成一个严格的结构,例如通过在它上面调用toList
  • 如果您的 API 很小并且明确针对“大数据吞吐量”进行调整,请使用IndexedSeq

当然,这是我个人的选择,但我希望这听起来是理智的。

  • 在任何地方都默认使用Seq
  • 当需要按索引访问时使用IndexedSeq
  • 仅在特殊情况下使用其他任何东西。

这些是“常识”准则。 它们简单、实用,并且在平衡原理和性能的同时在实践中运行良好。 原则是:

  1. 使用反映数据组织方式的类型(感谢 OP 和 ziggystar)。
  2. 在方法参数和返回类型中使用接口类型。 API 的输入和返回类型都受益于通用性的灵活性。

Seq满足这两个原则。 http://docs.scala-lang.org/overviews/collections/seqs.html中所述:

序列是一种具有 [有限] 长度且其元素具有从 0 开始的固定索引位置的可迭代对象。

90% 的时间,您的数据是 Seq。

其他注意事项:

  • List是一种实现类型,因此您不应在 API 中使用它。 例如,不经过转换就不能将Vector用作List
  • Iterable没有定义length 跨有限序列和潜在无限流的可Iterable抽象。 大多数时候,人们处理的是有限序列,因此您“有一个长度”,而Seq反映了这一点。 通常你实际上不会使用长度。 但它经常被需要,而且很容易提供,所以使用Seq

缺点:

这些“常识性”约定有一些轻微的缺点。

脚注:

使您的方法的返回类型尽可能具体。 然后,如果调用者想将其保留为SuperSpecializedHashMap或将其键入为GenTraversableOnce ,他们可以。 这就是编译器默认推断最具体类型的原因。

我遵循的经验法则是,根据实现,使返回类型尽可能具体,使参数类型尽可能通用。 这是一个易于遵循的规则,它以最大的自由度为您提供类型属性的一致保证。

比如说,如果你有一个函数实现,它只使用mapfilterfold等方法Traversable数据结构——那些在 traversable 特征中实现的方法,你可以期望它在任何类型的输入集合上同样执行——无论是List , Vector , HashSet甚至是HashMap ,所以你的输入参数应该指定为Traversable[T] 函数输出类型的选择应该只取决于它的实现:在这种情况下它也应该是Traversable 但是,如果在您的函数中您使用toListtoSeqtoSet等方法将此数据结构强制为某种更具体的类型,则您应该指定适当的类型。 注意到实现和返回类型之间的一致性了吗?

如果您的函数按索引访问 input 的元素,则输入应指定为IndexedSeq ,因为它是最通用的类型,可为您提供有效实施方法apply的保证。

对于抽象成员,同样的规则适用,唯一的区别是你应该根据你计划如何使用它们而不是实现来指定返回类型,因此通常它们比实现更通用。 类别选择SeqSetMap是最令人期待的。

遵循这条规则,您可以保护自己免受非常常见的瓶颈情况的影响,例如,项目被附加到ListcontainsSeq而不是Set上被调用,但您的程序仍然具有很好的自由度并且在选择意义上是一致的的类型。

快速说明:在 Scala 2.13.x 中, Traversable不再适用。 Iterable更通用,并且确定继续二元性是不合理的。 Iterable现在位于集合层次结构的顶部, Traversable已被弃用。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM