import scala.scalajs.js
def toScalaArray(input: js.typedarray.Uint8Array): Array[Byte] =
// code in question
input.view.map(_.toByte).toArray
原始答案
我对Scala-js并不是非常熟悉,但我可以详细解释一些评论中出现的问题,并改进你的自我回答。
另外,我不太明白为什么需要 toByte 调用
class Uint8Array extends Object with TypedArray[Short, Uint8Array]
Scala将Uint8Array视为Short
的集合,而您期望它是一个Byte
的集合。
Uint8Array的toArray
方法说明:
该成员是通过在
scala.scalajs.js.LowestPrioAnyImplicits
中执行iterableOps
方法由Uint8Array
转换为IterableOps [Short]
而添加的隐式转换。
因此,该方法返回一个Array [Short]
,然后您可以使用.map
将Short
转换为Byte
。
在您的答案中,您发布了
input.toArray.map(_.toByte)
这种写法在技术上是正确的,但是它的缺点是会分配一个 Shorts 的中间数组。为了避免这种分配,你可以在数组的 .view
上执行 .map
操作,然后在视图上调用 .toArray
。
Scala (以及 Scala.js) 中的 Views 是轻量级对象,它们引用原始集合加上某种转换/过滤函数,可以像任何其他集合一样迭代。你可以组合许多转换/过滤器在一个 view 上而不必分配中间集合来表示结果。有关更多信息,请参见链接的文档页面。
input.view.map(_.toByte).toArray
根据您打算如何传递结果值,您甚至可能不需要调用.toArray
。例如,如果您只需要稍后迭代元素,则可以将视图作为Iterable[Byte]
传递而无需分配单独的数组。
所有当前的答案都需要在用户空间中迭代数组。
Scala.js具有优化器支持的类型数组转换(事实上,Array[Byte]
在现代配置中就是类型数组)。通过这样做,您可能会获得更好的性能:
import scala.scalajs.js.typedarray._
def toScalaArray(input: Uint8Array): Array[Byte] = {
// Create a view as Int8 on the same underlying data.
new Int8Array(input.buffer, input.byteOffset, input.length).toArray
}
需要额外的 new Int8Array
来将底层缓冲区重新解释为有符号的(Byte
类型是有符号的)。只有这样,Scala.js 才会提供内置的转换到 Array[Byte]
。
当查看生成的代码时,你会发现不需要用户空间循环:内置的 slice
方法用于复制 TypedArray。在性能方面,几乎肯定无法通过任何用户空间循环来超越它。
$c_Lhelloworld_HelloWorld$.prototype.toScalaArray__sjs_js_typedarray_Uint8Array__AB = (function(input) {
var array = new Int8Array(input.buffer, $uI(input.byteOffset), $uI(input.length));
return new $ac_B(array.slice())
});
如果我们将其与当前接受的答案进行比较(input.view.map(_.toByte).toArray
),我们会发现有很大的区别(我的评论):
$c_Lhelloworld_HelloWorld$.prototype.toScalaArray__sjs_js_typedarray_Uint8Array__AB = (function(input) {
var this$2 = new $c_sjs_js_IterableOps(input);
var this$5 = new $c_sc_IterableLike$$anon$1(this$2);
// We need a function
var f = new $c_sjsr_AnonFunction1(((x$1$2) => {
var x$1 = $uS(x$1$2);
return ((x$1 << 24) >> 24)
}));
new $c_sc_IterableView$$anon$1();
// Here's the view: So indeed no intermediate allocations.
var this$8 = new $c_sc_IterableViewLike$$anon$6(this$5, f);
var len = $f_sc_TraversableOnce__size__I(this$8);
var result = new $ac_B(len);
// This function actually will traverse.
$f_sc_TraversableOnce__copyToArray__O__I__V(this$8, result, 0);
return result
});
import scala.scalajs.js
def toScalaArray(input: js.typedarray.Uint8Array): Array[Byte] =
input.toArray.map(_.toByte)
Array
吗?或者一个类似于Seq
的足够类序列结构是否就足够了呢?因为可能有可能编写一个包装器类,实现Seq
而不需要拷贝数组。 - Silvio Mayolo