Multidimensional array-like structure of different types


#1

Hi,

I’m wondering if someone might help me with a problem that has me stumped. I’m new to Scala, but have some experience with c, Pascal and Erlang.


I’m writing some music software and have a class called ‘Note’. It is constructed as:

class Note (
var delta: Int = 0, // time until event occurs
var pitch: Int = 0, // middle C = 60
var velocity: Int = 0, // amplitude corresponds to MIDI velocity
var duration: Int = 0,
var panposition: Int = 0
)

I need to construct a two-dimensional data structure that has two different types: one, an Int, so that I can access the musical track as 0, 1 etc. – and the other, a mutable list of notes as shown above.

Conceptually, the is would be: Array[Int][Note]

Clearly an Array.ofDim doesn’t work as the types are different and I’ve feel like I’m groping in the dark with ListBuffers, tuples, etc. Any suggestion as to how to do this would be most welcome.

Thanks.

Don


#2

Can you use something along the lines of

val myNotes: List[List[Note]]

#3

Can you use Map[Int, ListBuffer[Note]] perhaps?

-dave-


#4

If you just want something indexed by integers, use Array[Array[Note]] where the Array[Note] is the track, and the other array holds the tracks in order. (You could also use Vector or ArrayBuffer instead of Array, depending on what features you want the collection to have. ArrayBuffer would be the best choice for something that can be appended to; Vector is good if you want it immutable entirely; Array allows you to change the elements but not the length.)

If for some reason the indexing might be sparse, you can as Dave suggested, use a Map[Int, Array[Note]]. (Where again, Array should perhaps be an ArrayBuffer or something else.)


#5

The statement

var myNoteBuffer = Map[Int, ListBuffer[Note]]

produces:

error: missing argument list for method apply in class GenMapFactory
Unapplied methods are only converted to functions when a function type is expected.
You can make this conversion explicit by writing apply _ or apply(_) instead of apply.
var myNoteBuffer = Map[Int, ListBuffer[Note]]


#6

This statement produces:

error: ‘;’ expected but ‘val’ found.
\ val myNotes: List[List[Note]]
^
one error found


#7

Sorry, the ‘’ messed it up. The statement:

val myNotes: List[List[Note]]

produces:

error: only classes can have declared but undefined members
val myNotes: List[List[Note]]


#8

The statement:

val myNotes: Array[Array[Note]]

produces:

error: only classes can have declared but undefined members
val myNotes: Array[Array[Note]]


#9

Well yes of course. You’ve declared a val with a type, but without a
value. You need to give it a value, probably of an empty map.

// Try this
val myNotes: List[List[Note]] = List.empty
// or this
val myNotes: Map[Int, ListBuffer[Note]] = Map.empty

das http://users.scala-lang.org/u/das
December 20

Ichoran:

Array[Array[Note]]

Sorry, the ‘’ messed it up. The statement:

val myNotes: List[List[Note]]

produces:

error: only classes can have declared but undefined members
val myNotes: List[List[Note]]

Visit Topic
http://users.scala-lang.org/t/multidimensional-array-like-structure-of-different-types/2089/7
or reply to this email to respond.

In Reply To

das http://users.scala-lang.org/u/das

December 20
This statement produces: error: ‘;’ expected but ‘val’ found. \ val
myNotes: List[List[Note]] ^ one error found

Visit Topic
http://users.scala-lang.org/t/multidimensional-array-like-structure-of-different-types/2089/7
or reply to this email to respond.

You are receiving this because you enabled mailing list mode.


#10

Thanks everyone. After much flailing around – and since the actual components of the type Note are all Ints, the easiest solution seems to be to create a three dimensional array of Ints, as in:

var noteBuffer = Array.ofDim[Int](NumTracks, MaxBuf, NoteParameters)

and assign to (or read from) it as follows:

noteBuffer(trackID)(noteBufferPointer(trackID))(Delta) = note.delta
noteBuffer(trackID)(noteBufferPointer(trackID))(Pitch) = note.pitch
noteBuffer(trackID)(noteBufferPointer(trackID))(Velocity) = note.velocity
noteBuffer(trackID)(noteBufferPointer(trackID))(Duration) = note.duration
noteBuffer(trackID)(noteBufferPointer(trackID))(PanPosition) = note.panposition


#11

Yikes.

While you certainly can do this, you’ve basically lost all type safety by
doing so. Nothing will be preventing you from accidentally storing a
note.pitch into the wrong element of your 3d array, except much vigilance
(and as usage grows, the chance of this being correct drops dramatically)

-dave-


#12

I’m with Dave on this. An Array[Array[Note]] would be better, but perhaps even better than that would be a Map[NoteKey, Note] where NoteKey is a case class that has the two Ints you normally use to look up the Note. This solution would be particularly beneficial if your array is sparse. You want the NoteKey to be a case class because that will automatically provide you with a good hashCode method for using with a Map.


#13

The 245th note in one track of music has no interesting relationship with the 245th note in another. Changing from nested indices to a NoteKey lookup is therefore not a good idea: it obscures the important relationship (sequential notes) and is awkward.

Having notes parameterized in an ordered way inside a class is a good idea, though.


#14

I don’t know a lot about music, but I would guess a note is a value, not a mutable object with an identity. So a case class Note (without vars) would make more sense to me, and will probably make writing this program a lot less painful.