Ich habe eine API zum Lesen mehrdimensionaler Arrays, die einen Vektor von Bereichen zum Lesen von Unterrechtecken (oder Hypercubes) aus dem Backing-Array übergeben müssen. Ich möchte dieses Array "linear" lesen, alle Elemente in einer bestimmten Reihenfolge mit beliebigen Chunk-Größen. Somit besteht die Aufgabe darin, mit einem off
und einem len
die durch diesen Bereich abgedeckten Elemente in den kleinstmöglichen Satz von Hyperwürfeln zu übersetzen, d.h. die kleinste Anzahl von Lesebefehlen, die in der API ausgegeben werden.Lineares Lesen eines mehrdimensionalen Arrays, das dimensionalen Unterabschnitten folgt
Zum Beispiel können wir Indexvektoren für den Satz von Dimensionen geben einen linearen Index berechnen:
def calcIndices(off: Int, shape: Vector[Int]): Vector[Int] = {
val modsDivs = shape zip shape.scanRight(1)(_ * _).tail
modsDivs.map { case (mod, div) =>
(off/div) % mod
}
}
Nehmen wir an, die Form ist dies, was ein Array mit Rang 4 und 120 Elemente in Summe:
val sz = Vector(2, 3, 4, 5)
val num = sz.product // 120
ein Dienstprogramm diese Indexvektoren für einen Bereich von linearen Offsets drucken:
def printIndices(off: Int, len: Int): Unit =
(off until (off + len)).map(calcIndices(_, sz))
.map(_.mkString("[", ", ", "]")).foreach(println)
Wir können alle diese Vektoren erzeugen:
printIndices(0, num)
[0, 0, 0, 0]
[0, 0, 0, 1]
[0, 0, 0, 2]
[0, 0, 0, 3]
[0, 0, 0, 4]
[0, 0, 1, 0]
[0, 0, 1, 1]
[0, 0, 1, 2]
[0, 0, 1, 3]
[0, 0, 1, 4]
[0, 0, 2, 0]
[0, 0, 2, 1]
[0, 0, 2, 2]
[0, 0, 2, 3]
[0, 0, 2, 4]
[0, 0, 3, 0]
[0, 0, 3, 1]
[0, 0, 3, 2]
[0, 0, 3, 3]
[0, 0, 3, 4]
[0, 1, 0, 0]
...
[1, 2, 1, 4]
[1, 2, 2, 0]
[1, 2, 2, 1]
[1, 2, 2, 2]
[1, 2, 2, 3]
[1, 2, 2, 4]
[1, 2, 3, 0]
[1, 2, 3, 1]
[1, 2, 3, 2]
[1, 2, 3, 3]
[1, 2, 3, 4]
auf ein Beispiel Brocken aussehen lassen, die gelesen werden soll, die ersten sechs Elemente:
val off1 = 0
val len1 = 6
printIndices(off1, len1)
Ich werde schon die Ausgabe von Hand in Hyper partitionieren:
// first hypercube or read
[0, 0, 0, 0]
[0, 0, 0, 1]
[0, 0, 0, 2]
[0, 0, 0, 3]
[0, 0, 0, 4]
// second hypercube or read
[0, 0, 1, 0]
So ist die Aufgabe, ein Verfahren
zu definierendef partition(shape: Vector[Int], off: Int, len: Int): List[Vector[Range]]
, die die richtige Liste ausgibt und die kleinstmögliche Listengröße verwendet. So für off1
und len1
, haben wir das erwartete Ergebnis:
val res1 = List(
Vector(0 to 0, 0 to 0, 0 to 0, 0 to 4),
Vector(0 to 0, 0 to 0, 1 to 1, 0 to 0)
)
assert(res1.map(_.map(_.size).product).sum == len1)
Ein zweites Beispiel, Elemente bei Indizes 6 bis 22, mit manueller Partitionierung gibt drei Hyper oder Lesebefehle:
val off2 = 6
val len2 = 16
printIndices(off2, len2)
// first hypercube or read
[0, 0, 1, 1]
[0, 0, 1, 2]
[0, 0, 1, 3]
[0, 0, 1, 4]
// second hypercube or read
[0, 0, 2, 0]
[0, 0, 2, 1]
[0, 0, 2, 2]
[0, 0, 2, 3]
[0, 0, 2, 4]
[0, 0, 3, 0]
[0, 0, 3, 1]
[0, 0, 3, 2]
[0, 0, 3, 3]
[0, 0, 3, 4]
// third hypercube or read
[0, 1, 0, 0]
[0, 1, 0, 1]
expected result:
val res2 = List(
Vector(0 to 0, 0 to 0, 1 to 1, 1 to 4),
Vector(0 to 0, 0 to 0, 2 to 3, 0 to 4),
Vector(0 to 0, 1 to 1, 0 to 0, 0 to 1)
)
assert(res2.map(_.map(_.size).product).sum == len2)
Beachten Sie, dass für val off3 = 6; val len3 = 21
würden wir vier Lesungen benötigen.