Scala中的"逐行扫描"列表

tom*_*mek 9 scala partitioning list

我有一个字节列表,表示从音频接口读入的原始样本.根据使用情况和H/W,每个样本可以是1到4个字节长的任何地方,并且"流"中的信道总数可以或多或少是任意的.每个样本的通道数和位数在运行时都是已知的.

我举一个例子来说明我的意思.流中有四个通道,每个样本是两个字节.

List(A1, A2, B1, B2, C1, C2, D1, D2, A3, A4, B3, B4, C3, C4, D3, D4)

A1是通道A的第一个样本的第一个A2字节,是同一个样本的第二个字节,依此类推.

我需要做的是将每个频道的样本提取到他们自己的列表中,如下所示:

List(List(A1, A2, A3, A4), List(B1, B2, B3, B4), List(C1, C2, C3, C4), List(D1, D2, D3, D4))

我将如何在惯用的Scala中执行此操作?我刚刚开始学习Scala几个小时前,我提出的唯一非必要的解决方案显然不是最理想的:

def uninterleave(samples: Array[Byte], numChannels: Int, bytesPerSample: Int) = {
val dropAmount = numChannels * bytesPerSample
  def extractChannel(n: Int) = {
    def extrInner(in: Seq[Byte], acc: Seq[Byte]): Seq[Byte] = {
      if(in == List()) acc
      else extrInner(in.drop(dropAmount), in.take(bytesPerSample) ++ acc)
    }
    extrInner(samples.drop(n * bytesPerSample), Nil)
  }

  for(i <- 0 until numChannels) yield extractChannel(i)
}
Run Code Online (Sandbox Code Playgroud)

Did*_*ont 12

我会做

samples.grouped(bytesPerSample).grouped(numChannels).toList
  .transpose.map(_.flatten)
Run Code Online (Sandbox Code Playgroud)

我不会担保它的表现.我宁愿避免列表,不幸的是grouped产生它们.

也许

samples.grouped(bytesPerSample).map(_.toArray)
  .grouped(numChannels).map(_.toArray)
  .toArray
  .transpose
  .map(flatten)
Run Code Online (Sandbox Code Playgroud)

还有很多名单.


Dan*_*ral 6

didierd回答几乎是完美的,但是,唉,我认为可以改善它.他关注所有列表创建,并且转置也是一个相当繁重的操作.如果您可以同时处理所有数据,那么它可能已经足够好了.

但是,我要去Stream,并使用一个小技巧来避免移调.

首先,分组是一样的,只有我把东西变成了流:

def getChannels[T](input: Iterator[T], elementsPerSample: Int, numOfChannels: Int) =
  input.toStream.grouped(elementsPerSample).toStream.grouped(numOfChannels).toStream
Run Code Online (Sandbox Code Playgroud)

接下来,我将为您提供从中提取一个通道的功能:

def streamN[T](s: Stream[Stream[Stream[T]]])(channel: Int) = s flatMap (_(channel))
Run Code Online (Sandbox Code Playgroud)

有了这些,我们可以像这样解码流:

// Sample input
val input = List('A1, 'A2, 'B1, 'B2, 'C1, 'C2, 'D1, 'D2, 'A3, 'A4, 'B3, 'B4, 'C3, 'C4, 'D3, 'D4)

// Save streams to val, to avoid recomputing the groups
val streams = getChannels(input.iterator, elementsPerSample = 2, numOfChannels = 4)

// Decode each one
def demuxer = streamN(streams) _
val aa = demuxer(0)
val bb = demuxer(1)
val cc = demuxer(2)
val dd = demuxer(3)
Run Code Online (Sandbox Code Playgroud)

这将为每个通道返回单独的流,而不需要手头的整个流.如果您需要实时处理输入,这可能很有用.这里有一些输入源来测试它读取输入到特定元素的输入的距离:

def source(elementsPerSample: Int, numOfChannels: Int) = Iterator.from(0).map { x =>
  "" + ('A' + x / elementsPerSample % numOfChannels).toChar +
  (x % elementsPerSample 
   + (x / (numOfChannels * elementsPerSample)) * elementsPerSample 
   + 1)
}.map { x => println("Saw "+x); x }
Run Code Online (Sandbox Code Playgroud)

然后你可以试试这样的东西:

val streams = getChannels(source(2, 4), elementsPerSample = 2, numOfChannels = 4)
def demuxer = streamN(streams) _
val cc = demuxer(2)
println(cc take 20 toList)
val bb = demuxer(1)
println(bb take 30 toList)
Run Code Online (Sandbox Code Playgroud)