在 Scala 中编写 read-while 循环的正确方法是啥?

Posted

技术标签:

【中文标题】在 Scala 中编写 read-while 循环的正确方法是啥?【英文标题】:What is the proper way to code a read-while loop in Scala?在 Scala 中编写 read-while 循环的正确方法是什么? 【发布时间】:2011-03-01 23:10:48 【问题描述】:

在 Scala 中编写标准 read-while 循环的“正确性”是什么?确切地说,我的意思是用类似 Scala 的方式而不是类似 Java 的方式编写。

这是我在 Java 中的代码:

MessageDigest md = MessageDigest.getInstance( "MD5" );
InputStream input = new FileInputStream( "file" );
byte[] buffer = new byte[1024];
int readLen;
while( ( readLen = input.read( buffer ) ) != -1 )
    md.update( buffer, 0, readLen );
return md.digest();

这是我在 Scala 中的代码:

val md = MessageDigest.getInstance( hashInfo.algorithm )
val input = new FileInputStream( "file" )
val buffer = new Array[ Byte ]( 1024 )
var readLen = 0
while( readLen != -1 )

    readLen = input.read( buffer )
    if( readLen != -1 )
        md.update( buffer, 0, readLen )

md.digest

Scala 代码是正确且有效的,但感觉非常不符合 Scala 风格。一方面,它是对 Java 代码的直译,没有利用 Scala 的任何优点。此外,它实际上比 Java 代码长!我真的觉得我错过了什么,但我不知道是什么。

我对 Scala 还很陌生,所以我提出这个问题是为了避免陷入在 Scala 中编写 Java 样式代码的陷阱。我对 Scala 解决此类问题的方法更感兴趣,而不是 Scala API 可能提供的用于散列文件的任何特定辅助方法。

(我提前为我在整个问题中使用的 Scala 形容词道歉。)

【问题讨论】:

我对@9​​87654321@ 的回答可能会有所帮助。 @Rex 我会使用Iterator 而不是Stream。毕竟,它一次性的,不可重复使用。此外,Iterator 在此类任务中具有更好的内存性能。 @Daniel - 同意。我相信我之前使用Stream 有一个很好的理由,但我不再记得是什么(而且我认为它仍然不是真的)。无论如何,这里Iterator.continually 应该没问题。 【参考方案1】:

根据他提到的 Rex 的帖子:

Stream.continually(input.read(buffer)).takeWhile(_ != -1).foreach(md.update(buffer, 0, _))

您应该用它替换 var readLen + while ... 行,它会产生相同的结果。

正如 Rex 所说,它适用于 scala 2.8。

【讨论】:

如果 Stream 给你威力,你也可以只使用 Iterator.continually。 当我在从 Process 获得的 InputStream 上尝试它时,foreach 方法只是为第一个字符调用然后停止。当与 while 一起使用时,我得到所有数据。知道为什么吗? 如何获取从“input.read(...)”读取的字节数以在“foreach”中使用它?【参考方案2】:

Rex Kerr 在他的评论中建议如下:

val md = MessageDigest.getInstance("MD5")
val input = new FileInputStream("foo.txt")
val buffer = new Array[ Byte ]( 1024 )
Stream.continually(input.read(buffer))
  .takeWhile(_ != -1)
  .foreach(md.update(buffer, 0, _))
md.digest

密钥是Stream.continually。它得到一个不断求值的表达式,创建一个无限的Stream 求值表达式。 takeWhilewhile 条件的转换。 foreachwhile 循环的主体。

【讨论】:

【参考方案3】:

柯里化函数呢?你的 11 行 Scala 代码变成:

val md = MessageDigest.getInstance(hashInfo.algorithm)
val input = new FileInputStream("file")
iterateStream(input) (data, length) => 
    md.update(data, 0, length)

md.digest

第 3 行的 iterateStream 函数可以添加到库中:

def iterateStream(input: InputStream)(f: (Array[Byte], Int) => Unit)
    val buffer = new Array[Byte](512)
    var curr = input.read(buffer)
    while(curr != -1)
        f(buffer, curr)
        curr = input.read(buffer)
    

丑陋的重复代码(读取输入的地方)最终会在库中,经过良好测试并远离程序员。感觉第一块代码没有Iterator.continually的方案复杂。

【讨论】:

以上是关于在 Scala 中编写 read-while 循环的正确方法是啥?的主要内容,如果未能解决你的问题,请参考以下文章

在 Scala 中迭代 Java 集合

在scala foreach循环中赋值

如何在 Scala 中跳出循环?

为啥必须在scala的for循环中为模式匹配定义过滤器?

Scala:如何在循环中合并数据帧

scala简单的功能实现~weekone