Scala 2.13:返回相同的集合类型(甚至数组和字符串)

Posted

技术标签:

【中文标题】Scala 2.13:返回相同的集合类型(甚至数组和字符串)【英文标题】:Scala 2.13: return same collection type (even Array and String) 【发布时间】:2021-07-25 13:51:28 【问题描述】:

这是faro shuffle 的基本实现。这是一个 out-shuffle(“faro out,man!”)只是因为它比 in-shuffle 更容易编码。

def faroOut[A](cards: List[A]): List[A] =
  List.unfold(cards.splitAt((cards.size + 1) / 2)) 
    case (a,b) => Option.when(a.nonEmpty)(a.head -> (b, a.tail))
  

faroOut(List("AS","KD","QC","JH","2S","3D","4C","5H"))
//res0: List[String] = List(AS, 2S, KD, 3D, QC, 4C, JH, 5H)
faroOut(List(1,2,3,4,5,6,7))
//res1: List[Int] = List(1, 5, 2, 6, 3, 7, 4)

这在其元素类型上是通用的,但在其集合类型上不是。让我们尝试解决这个问题。

import scala.collection.Factory

def faroOut[A, CC[x] <: Iterable[x]](cards:CC[A]
                                    )(implicit fac: Factory[A,CC[A]]
                                     ): CC[A] =
  Iterator.unfold(cards.splitAt((cards.size + 1) / 2)) 
    case (a, b) => Option.when(a.nonEmpty)(a.head -> (b, a.tail))
  .to(fac)

faroOut(LazyList("AS","KD","QC","JH","2S","3D","4C","5H"))
faroOut(Vector(1,2,3,4,5,6,7))
//faroOut(Array(3,4,5,6))  <-- won't compile

将此转换为扩展方法并不太复杂,但我们不必在此关心。

所以这适用于列表和向量,但不适用于 ArrayString,因为它们来自 Java 领域并且不属于 Scala Iterable 层次结构。为此,我们需要引入 IsSeq 类型类。

有趣的是,这在 Scala-3 中非常简单。

import scala.collection.generic.IsSeq
import scala.collection.Factory

def faroOut[Repr](cards: Repr
                 )(using seq: IsSeq[Repr]
                       , fac: Factory[seq.A,Repr]): Repr =
  val seqOps = seq(cards).toIterable
  Iterator.unfold(seqOps.splitAt((seqOps.size + 1) / 2)) 
    case (a, b) => Option.when(a.nonEmpty)(a.head -> (b, a.tail))
  .to(fac)

这是Scastie to prove it。

将其转换为扩展方法几乎是微不足道的,但我们在这里不必关心。

注意Factory[_,_] 的第一个类型参数如何依赖于参数组中的前一个参数。在 Scala-2 上不可能实现一个很酷的 Scala-3 增强功能。

在Scala docs page 和this SO Q/A 花了一些时间之后,我留下了 QUESTION (at long last):难道没有更小和/或更简单的解决方案?我们是否真的需要将其转换为具有所有implicit 转换等的扩展方法?

【问题讨论】:

在您的示例中,“扩展方法”和“隐式转换”是什么意思? 1 - 我对将其转换为 List(...).faroOut 不感兴趣。 2 - 提供的链接中概述的implicit def ... 代码。 另一个 SO 问题在他们的代码示例中明确要求扩展方法。 Scala 中没有其他方法可以将方法添加到我们不拥有的类中。但是,如果您不关心语法,您可以调用faroOut(yourCollection),只要您在范围内拥有工厂,就像您已经做过的那样。所以不知道是什么问题 重读问题。我不想向任何类添加方法。我想要 faroOut(myArray)faroOut("myString")。我可以用 Scala-3 做到这一点,但用 Scala 2.13 似乎不可能。 【参考方案1】:

要在单个参数列表限制内绕过 Scala 2 依赖类型,请尝试使用类型细化

IsIterable[Repr]  type A = E 

或Aux 输入别名模式

type AuxA[Repr, E] = IsIterable[Repr]  type A = E 

例如

def faroOut[Repr, E](cards: Repr)(
  implicit 
  seq: IsIterable[Repr]  type A = E , 
  fac: Factory[E, Repr]
): Repr = 
  val seqOps = seq(cards).toIterable
  Iterator.unfold(seqOps.splitAt((seqOps.size + 1) / 2)) 
    case (a, b) => Option.when(a.nonEmpty)(a.head -> (b, a.tail))
  .to(fac)


faroOut(Array(3,4,5,6)) // : Array[Int] = Array(3, 5, 4, 6)
faroOut("ABCxyz")       // : String = AxByCz

scastie

【讨论】:

马里奥。当您偷偷更新时,我正在处理字符串问题。干得好,非常感谢。我不会猜到元素类型可以单独表示E,同时作为Repr 的一部分表示,或者推理引擎可以从这些引用中确定E 的含义在第二个(隐式)参数组中。该编译器中的一些令人印象深刻的聪明才智。

以上是关于Scala 2.13:返回相同的集合类型(甚至数组和字符串)的主要内容,如果未能解决你的问题,请参考以下文章

scala入门系列--数组

Spark记录-Scala集合

Scala 集合如何从映射操作中返回正确的集合类型?

Scala集合

scala集合算子大全及分类汇总——倾心整理

scala集合算子大全及分类汇总——倾心整理