d設計された方法で命令型に動作するように設計されたJavaクラスを使用することに特に問題はないと思います。慣用的なScalaには、スタイルが少し衝突する場合でも、意図したとおりに慣用的なJavaを使用できることが含まれています。
ただし、演習として、またはロジックがわずかに明確になっているために、より機能的な変数のない方法でこれを実行したい場合は、そうすることができます。2.8では特に素晴らしいので、2.7.7を使用していても、2.8の答えを出します。
まず、問題を設定する必要がありますが、完全には設定していませんが、次のようなものがあるとします。
import java.io._
import java.util.zip._
import scala.collection.immutable.Stream
val fos = new FileOutputStream("new.zip")
val zipOut = new ZipOutputStream(new BufferedOutputStream(fos))
val zipIn = new ZipInputStream(new FileInputStream("old.zip"))
def entryIsValid(ze: ZipEntry) = !ze.isDirectory
これを前提として、zipファイルをコピーします。使用できるトリックは、のcontinually
メソッドですcollection.immutable.Stream
。それが行うことは、遅延評価されたループを実行することです。次に、結果を取得してフィルタリングし、必要なものを終了して処理できます。イテレータにしたいものがある場合に使用するのに便利なパターンですが、そうではありません。(アイテムがそれ自体を更新する場合は.iterate
、Iterable
またはで使用できますIterator
。通常はさらに優れています。)これがこの場合のアプリケーションです。2回使用されます。1回はエントリの取得に、もう1回はデータのチャンクの読み取り/書き込みに使用されます。
val buffer = new Array[Byte](1024)
Stream.continually(zipIn.getNextEntry).
takeWhile(_ != null).filter(entryIsValid).
foreach(entry => {
zipOut.putNextEntry(new ZipEntry("subdir/"+entry.getName))
Stream.continually(zipIn.read(buffer)).takeWhile(_ != -1).
foreach(count => zipOut.write(buffer,0,count))
})
}
zipIn.close
zipOut.close
.
いくつかの行の終わりに細心の注意を払ってください!私は通常これを1つの長い行に書き込みますが、ここですべてを確認できるように折り返す方がよいでしょう。
明確でない場合に備えて、の使用法の1つを開梱しましょうcontinually
。
Stream.continually(zipIn.read(buffer))
zipIn.read(buffer)
これは、結果の整数を格納して、必要な回数だけ呼び出しを続けるように要求します。
.takeWhile(_ != -1)
これは、必要な回数を指定し、長さが不定のストリームを返しますが、。にヒットすると終了し-1
ます。
.foreach(count => zipOut.write(buffer,0,count))
これにより、ストリームが処理され、各アイテムが順番に(カウント)取得され、それを使用してバッファーが書き込まれます。これは少し卑劣な方法で機能します。これは、ストリームの次の要素を取得するために呼び出されたばかりの事実に依存しているためですzipIn
。ストリームを1回通過するのではなく、もう一度これを実行しようとすると、失敗します。buffer
上書きされます。しかし、ここでは大丈夫です。
つまり、それは次のとおりです。少しコンパクトで、おそらく理解しやすく、おそらくより機能的で理解しにくい方法です(ただし、まだ多くの副作用があります)。対照的に、2.7.7では、利用できないため、実際にはJavaの方法で実行します。この場合Stream.continually
、カスタムを構築するオーバーヘッドは価値がありません。Iterator
(ただし、より多くのzipファイル処理を実行し、コードを再利用できれば、それだけの価値があります。)
編集:ゼロを探す方法は、zipファイルの終わりを検出するための一種の不安定な方法です。null
「正しい」方法は、から戻るまで待つことだと思いますgetNextEntry
。そのことを念頭に置いて、以前のコードを編集し(takeWhile(_ => zipIn.available==1)
現在はtakeWhile(_ != null)
)、以下に2.7.7イテレーターベースのバージョンを提供しました(イテレーターの定義作業を終えたら、メインループがどれほど小さいかに注意してください)。 、確かにvarsを使用します):
val buffer = new Array[Byte](1024)
class ZipIter(zis: ZipInputStream) extends Iterator[ZipEntry] {
private var entry:ZipEntry = zis.getNextEntry
private var cached = true
private def cache { if (entry != null && !cached) {
cached = true; entry = zis.getNextEntry
}}
def hasNext = { cache; entry != null }
def next = {
if (!cached) cache
cached = false
entry
}
}
class DataIter(is: InputStream, ab: Array[Byte]) extends Iterator[(Int,Array[Byte])] {
private var count = 0
private var waiting = false
def hasNext = {
if (!waiting && count != -1) { count = is.read(ab); waiting=true }
count != -1
}
def next = { waiting=false; (count,ab) }
}
(new ZipIter(zipIn)).filter(entryIsValid).foreach(entry => {
zipOut.putNextEntry(new ZipEntry("subdir/"+entry.getName))
(new DataIter(zipIn,buffer)).foreach(cb => zipOut.write(cb._2,0,cb._1))
})
zipIn.close
zipOut.close