2011-12-18 279 views
0

我正在写一个软件的并行计算。该软件是完全通用的,因此我需要一个类来包装序列。奇怪的铸造(.asInstanceOf [T])在斯卡拉行为2.9

现在,我在我的地图功能中遇到了奇怪的行为。一个map函数将一个函数对象作为输入,它可以输出与其输入参数不同的类型。

看看下面的代码:

class SeqMPJ[T](indexedSeq: IndexedSeq[T]) { 

    val seq = indexedSeq.view //Lazy 
    val freeRanks = MPJEnv.getFreeRanks(seq.size) 
    //Get n free ranks 
    val commGroup = MPI.COMM_WORLD.group.Incl(freeRanks.toArray) 
    //Communicator for this sequence 
    val comm = MPI.COMM_WORLD.Create(commGroup) 

    val opIndex = globalRank % seq.size 
    var operand: Any = seq(opIndex) 

    if (!isOnline) 
    error("Cannot use MPJ-abstractions outside parallelize body...") 

    //Only works for p=n now 
    def mapMPJ[U](f: (T) => U): SeqMPJView[U] = { 
    if (freeRanks.contains(globalRank)) { //Process is part of this operation 
     operand = f(operand.asInstanceOf[T]) 
    } 
    return new SeqMPJView(operand.asInstanceOf[U], comm, freeRanks) 
    } 

...

注意的是,在功能mapMPJ[U](f: (T) => U):SeqMPJView[U],函数f具有输入类型T和输出类型U.这意味着,应用后f赋给变量“操作数”,操作数的类型为U,但是,这发生在if块的内部。换句话说,根据状态,操作数的类型为U或T.现在,当我投向U时,它总是成功。即使if-block中的条件失败。正如我所看到的,如果程序没有输入if块,那么在执行operand.asInstanceOf [U]时程序会失败。

一个例子的用法是在这个矩阵乘法:

val M = 2 
val N = 2 

val A = Array(
    Array(1.0, 2.0), 
    Array(3.0, 4.0)) 

val B = Array(
    Array(1.0, 2.0), 
    Array(3.0, 4.0)) 

val Bt = B.transpose 

/* 
* DNS using underlying MPI 
*/ 
parallelize(args) { 

    for (i <- 0 until M; j <- 0 until N) 
    A(i) zip Bt(j) mapMPJ { case (a, b) => a * b } reduceMPJ (_ + _) 

    if (globalRank == 0) 
    println("CHECK RESULT:\n" + Matrix(A) * Matrix(B)) 

} 

程序编译和运行完全利用最新蚀阶IDE。我还没有尝试过其他的编译器,但它可能是我是盲人,但我花了这么多时间,所以我希望一些帮助:)

编辑

有来自阵列的隐式转换到seqMPJ,仅供参考。

+0

你欺骗typechecker的方式是令人不安的,可能是不必要的。你有没有考虑过使用[A,B]作为操作数的类型?评论提示您可能不需要另一个状态的操作数。 如果if没有被采用,您是否想稍后将该函数应用于操作数?在这种情况下,我会使用: '特性结果[U] {} def res:U } case class Computed [U](res:U)extends Result [U] case class ToCompute [T,U] (t:T,f:T => U)扩展Result [U] {def} = f(t) }' '我会让你现在称之为'operand'的类型为'Result [U]' (并重命名它)。 – Blaisorblade 2011-12-19 01:50:21

+0

在if子句失败的情况下,我不需要操作数。但是,我必须返回一个新的SeqMPJView!为[A,B]?是关键字? – Felix 2011-12-19 07:49:37

+0

''或''是一个标准的Scala数据类型;可能'Option'应该用于定义'SeqMPJView',以指定你不需要传递一个值。 '[A,B]'允许提供类型'A'或类型'B'的值,就像_type-safe_ union一样。 – Blaisorblade 2011-12-20 00:10:33

回答

4

当谈到泛型类型参数时,Casting只是向编译器断言,您知道您在做什么以及类型究竟是什么。他们都只是AnyRef == java.lang.Object(或任何边界类型)。如果你对它撒谎,它会相信你(直到在使用类型的地方有错误类型导致运行时异常)。如果你想知道你是否有正确的类型,你必须检查清单。

下面是一个例子:

def example[A: ClassManifest,B: ClassManifest](a: A) = { 
    if (implicitly[ClassManifest[A]] <:< implicitly[ClassManifest[B]]) a.asInstanceOf[B] 
    else throw new Exception("Wrong!") 
} 

如果试图铸造这是行不通的,这会骂你:

scala> example[List[Int],List[String]](List()) 
java.lang.Exception: Wrong! 

您可以相应地修改你的代码,例如

implicitly[ClassManifest[U]].erasure.isAssignableFrom(operand.getClass) 
+0

护理来阐述这一部分: '含蓄[ClassManifest [A] <<隐含[ClassManifest [B]' 而且我不知道我知道这样做的目的: 隐含[ClassManifest [C ]] erasure.isAssignableFrom(operand.getClass) – Felix 2011-12-18 23:07:16

+0

隐式[ClassManifest [U]] erasure.isAssignableFrom(operand.getClass)是用于类型兼容性运行时测试:如果你可以指定操作数U型的变量,则返回true ,也就是说,操作数的运行时类型是U的子类型( http://docs.oracle.com/javase/6/docs/api/java/lang/Class.html#isAssignableFrom(java.lang.Class )) – Blaisorblade 2011-12-19 01:44:56

+0

@Felix - '<:<'手段 “是的子类”,和'隐式[ClassManifest [A]]'表示“获取实际类清单对应于' A'”。 Blaisorblade已经解释了另一个调用,所以事实证明它们都是运行时检查,确保类型是您要求的(或其子类型)。 – 2011-12-19 03:28:21