在Scala中组合数组时的有趣发现

arg*_*vos 12 arrays scala

我在Scala中理解Array组合时遇到了一些麻烦.

以下工作正常:

scala> Array('0', '1', '2') ++ Array('A', 'B', 'C')
res0: Array[Char] = Array(0, 1, 2, A, B, C)
Run Code Online (Sandbox Code Playgroud)

但是这个没有:

scala> ('0' to '9').toArray ++ ('A' to 'Z').toArray
<console>:8: error: polymorphic expression cannot be instantiated to expected type;
 found   : [B >: Char]Array[B]
 required: scala.collection.GenTraversableOnce[?]
              ('0' to '9').toArray ++ ('A' to 'Z').toArray
                                                   ^
Run Code Online (Sandbox Code Playgroud)

而且,以下似乎有效:

scala> ('0' to '9').toArray
res1: Array[Char] = Array(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)

scala> ('A' to 'Z').toArray
res2: Array[Char] = Array(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z)

scala> res1 ++ res2
res3: Array[Char] = Array(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z)
Run Code Online (Sandbox Code Playgroud)

另一个有趣的我发现:

scala> (('0' to '9').toArray) ++ (('A' to 'Z').toArray[Char])
res4: Array[Char] = Array(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z)
Run Code Online (Sandbox Code Playgroud)

我无法解释这一点 - 我错过了什么吗?

小智 6

++方法被定义在ArrayOps:

def ++[B](that: GenTraversableOnce[B]): Array[B]
Run Code Online (Sandbox Code Playgroud)

返回一个新的可变索引序列,其中包含左手操作数中的元素,后跟右手操作数中的元素.可变索引序列的元素类型是包含两个操作数的元素类型的最特定的超类.

推断存储结果的Array的类型.

('0' to '9').toArray ++ ('A' to 'Z').toArray
Run Code Online (Sandbox Code Playgroud)

('0' to '9').toArray效果很好,我们回来了Array[Char].

8: materializing requested scala.reflect.type.ClassTag[Char] using `package`
.this.materializeClassTag[Char]...
Run Code Online (Sandbox Code Playgroud)

根据我们接受的方法定义GenTraversableOnce[B].在我们的例子中,我们接受GenTraversableOnce[Char].

如果我们使用该-Ytyper-debug选项运行scala ,我们可以看到类型推断的实际效果.

//it tries to find the type of arg0
typing '0'.to('1').toArray.$plus$plus('A'.to('B').toArray): pt = ?: undetparams=, implicitsEnabled=true, enrichmentEnabled=true, mode=EXPRmode, silent=false, context.owner=value res1
    typing '0'.to('1').toArray.$plus$plus: pt = ?: undetparams=, implicitsEnabled=true, enrichmentEnabled=true, mode=EXPRmode BYVALmode POLYmode FUNmode, silent=true, context.owner=value res1
        typing '0'.to('1').toArray: pt = ?: undetparams=, implicitsEnabled=true, enrichmentEnabled=true, mode=EXPRmode POLYmode QUALmode, silent=true, context.owner=value res1
            typing '0'.to('1'): pt = ?: undetparams=, implicitsEnabled=true, enrichmentEnabled=true, mode=EXPRmode POLYmode QUALmode, silent=true, context.owner=value res1
                typing '0'.to: pt = ?: undetparams=, implicitsEnabled=true, enrichmentEnabled=true, mode=EXPRmode BYVALmode POLYmode FUNmode, silent=true, context.owner=value res1
                    typing '0': pt = ?: undetparams=, implicitsEnabled=true, enrichmentEnabled=true, mode=EXPRmode POLYmode QUALmode, silent=true, context.owner=value res1
                    typed '0': Char('0')
                    adapted '0': Char to ?,
//It succeeds.
//Now it infers a scala type 
    infer implicit view {
  tree         '0'
  pt           Char('0') => ?{def to: ?}

  }
               new ImplicitSearch(floatWrapper) {...}
               ...
                new ImplicitSearch(charWrapper) {...}

   } 

 //The interesting bit...
     typedImplicit1 charWrapper, pt=Char('0') => ?{def to: ?}, from implicit charWrapper:(c: Char)scala.runtime.RichChar   
     Implicit search yielded: SearchResult(scala.this.Predef.charWrapper, )
            typed scala.this.Predef.charWrapper('0').to('1'): scala.collection.immutable.NumericRange.Inclusive[Char]
            adapted scala.this.Predef.charWrapper('0').to('1'): scala.collection.immutable.NumericRange.Inclusive[Char] to ?,
        typed scala.this.Predef.charWrapper('0').to('1').toArray: [B >: Char](implicit evidence$1: scala.reflect.ClassTag[B])Array[B]

 //repeat the process 
 Infer implicit {
 tree         scala.this.Predef.charWrapper('0').to('1').toArray[Char]
  pt           scala.reflect.ClassTag[Char]
  undetparams
 }     
Run Code Online (Sandbox Code Playgroud)

推理过程重复,并且从该类型的方法调用进一步推断出类型,即++.('0' to '1').toArray ++最终解决了类型ArrayOps.

 Implicit search yielded: SearchResult(scala.this.Predef.charArrayOps, )

 adapted this.charArrayOps(charWrapper('0').to('1').toArray[Char](ClassTag.Char)).++: [B >: Char, That](that: scala.collection.GenTraversableOnce[B])(implicit bf: scala.collection.generic.CanBuildFrom[Array[Char],B,That])That to ?, undetparams=type B, type That
Run Code Online (Sandbox Code Playgroud)

Scala然后键入我们的第二个arg ...

 typed scala.this.Predef.charWrapper('A').to('B'): scala.collection.immutable.NumericRange.Inclusive[Char]
        adapted scala.this.Predef.charWrapper('A').to('B'): scala.collection.immutable.NumericRange.Inclusive[Char] to ?,
    typed scala.this.Predef.charWrapper('A').to('B').toArray: [B >: Char](implicit evidence$1: scala.reflect.ClassTag[B])Array[B]
Run Code Online (Sandbox Code Playgroud)

最后,我们看看我们Array[B]是否有这个GenTraversableOnce特质.

 infer implicit view {
 tree         <empty>
  pt           Array[?B] => scala.collection.GenTraversableOnce[?]
  undetparams
}
Run Code Online (Sandbox Code Playgroud)

当然,它没有,我们得到:

typing private[this] val res1: <error> = '0'.to('1').toArray.$plus$plus('A'.to('B').toArray): pt = ?: undetparams=, implicitsEnabled=true, enrichmentEnabled=true, mode=EXPRmode BYVALmode, silent=false, context.owner=object $iw
                typed private[this] val res1: <error> = scala.this.Predef.charArrayOps(scala.this.Predef.charWrapper('0').to('1').toArray[Char](ClassTag.Char)).++[B, That]('A'.to('B').toArray): <notype>
Run Code Online (Sandbox Code Playgroud)

有关这种情况的原因,请参阅/sf/answers/428692421/.

解决方案是使用++:

def ++:[B >: T, That](that: collection.Traversable[B])(implicit bf: CanBuildFrom[Array[T], B, That]): That
Run Code Online (Sandbox Code Playgroud)

它与++的不同之处在于右操作数确定了结果集合的类型而不是左集合.助记符:COLon位于新的COLlection类型的一侧.

它也是一个ArrayOps来自Arrays,所以这就是你需要的.

我们的输入调试输出证实了这一点:

typing {
  <synthetic> val x$1 = '0'.to('1').toArray;
  'A'.to('B').toArray.$plus$plus$colon(x$1)
}:
...
typed scala.this.Predef.charWrapper('0').to('1').toArray: [B >: Char](implicit evidence$1: scala.reflect.ClassTag[B])Array[B]
...
typing scala.this.Predef.charArrayOps(scala.this.Predef.charWrapper('A').to('B').toArray(ClassTag.Char())).++:(scala.this.Predef.wrapCharArray(x$1), scala.this.Array.canBuildFrom(ClassTag.Char())): pt = Array[Char]: undetparams=, implicitsEnabled=false, enrichmentEnabled=false, mode=EXPRmode BYVALmode, silent=false, context.owner=value res2
...
<synthetic> val x$1: Array[Char] = new runtime.RichChar(scala.this.Predef.charWrapper('0')).to(scala.Char.box('1')).toArray(ClassTag.Char()).$asInstanceOf[Array[Char]]();
  scala.this.Predef.charArrayOps(new runtime.RichChar(scala.this.Predef.charWrapper('A')).to(scala.Char.box('B')).toArray(ClassTag.Char()).$asInstanceOf[Array[Char]]()).++:(scala.this.Predef.wrapCharArray(x$1), scala.this.Array.canBuildFrom(ClassTag.Char())).$asInstanceOf[Array[Char]]()
Run Code Online (Sandbox Code Playgroud)