2010-06-10 32 views
24

"thích hợp" khi viết vòng lặp đọc chuẩn trong Scala là gì? Theo đúng nghĩa tôi đã được viết theo cách giống như Scala như trái ngược với một cách giống như Java.Cách thích hợp để mã hóa một vòng lặp đọc trong Scala là gì?

Đây là mã tôi có trong 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(); 

Đây là mã tôi có trong 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 

Mã Scala là chính xác và hoạt động, nhưng cảm thấy rất un-Scala -. Đối với một nó là một bản dịch chữ của mã Java, lợi dụng không có lợi thế của Scala. Hơn nữa nó thực sự dài hơn mã Java! Tôi thực sự cảm thấy như tôi đang thiếu một cái gì đó, nhưng tôi không thể tìm ra những gì.

Tôi khá mới đối với Scala và vì vậy tôi đặt câu hỏi để tránh rơi vào cạm bẫy khi viết mã kiểu Java trong Scala. Tôi quan tâm nhiều hơn đến cách Scala để giải quyết loại vấn đề này hơn bất kỳ phương thức trợ giúp cụ thể nào có thể được API Scala cung cấp để băm một tệp.

(Tôi xin lỗi trước cho quảng cáo hoc Scala tính từ của tôi trong suốt câu hỏi này.)

+4

Câu trả lời của tôi cho http://stackoverflow.com/questions/2849303 có thể hữu ích. –

+5

@Rex Tôi muốn sử dụng 'Iterator' thay vì' Stream'. Sau khi tất cả, nó _is_ một shot và không tái sử dụng được. Bên cạnh đó, 'Iterator' có hiệu suất bộ nhớ tốt hơn trong các nhiệm vụ như vậy. –

+0

@Daniel - Đồng ý. Tôi tin rằng có một số lý do chính đáng tại một thời điểm nào đó tại sao tôi lại sử dụng 'Stream' trước đây, nhưng tôi không thể nhớ lại những gì (và tôi không nghĩ nó vẫn đúng). Bất kể, ở đây 'Iterator.continually' sẽ ổn thôi. –

Trả lời

25

Dựa trên bài Rex rằng ông đề cập:

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

Bạn nên thay thế các var readLen + trong khi {.. .} dòng với nó, nó tạo ra kết quả tương tự.

Như Rex đã đề cập, nó hoạt động với scala 2.8.

+2

Nếu luồng cung cấp cho bạn các cảnh quan, bạn cũng có thể chỉ sử dụng Iterator.continually. –

+0

Khi tôi thử nó trên một InputStream tôi nhận được từ một Process, phương thức foreach chỉ được gọi cho ký tự đầu tiên và sau đó dừng lại. Khi được sử dụng với thời gian, tôi nhận được tất cả dữ liệu. Bất kỳ ý tưởng về lý do tại sao? – pommedeterresautee

+0

làm cách nào để nhận được số byte được đọc từ "input.read (...)" để sử dụng nó trong "foreach"? –

7

gì Rex Kerr gợi ý trong bình luận của ông như sau:

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 

Điều quan trọng là Stream.continually. Nó nhận được một biểu thức được đánh giá liên tục, tạo ra một biểu thức được đánh giá là Stream vô hạn. takeWhile là bản dịch từ điều kiện while. foreach là phần thân của vòng lặp while.

0

Còn chức năng được kết hôn thì sao? Bạn 11 dòng mã Scala trở thành:

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

Các iterateStream chức năng trên dòng 3, mà bạn có thể thêm vào một thư viện là:

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) 
    } 
} 

Mã trùng lặp xấu xí (nơi đầu vào được đọc) đầu trong thư viện, được kiểm tra và ẩn đi khỏi lập trình viên. Tôi cảm thấy rằng khối mã đầu tiên ít phức tạp hơn giải pháp Iterator.continually.

Các vấn đề liên quan