Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    primarykey
    data
    text
    <p>dI don't think there's anything particularly wrong with using Java classes that are designed to work in imperative fashion in the fashion they were designed. Idiomatic Scala includes being able to use idiomatic Java as it was intended, even if the styles do clash a bit.</p> <p>However, if you want--perhaps as an exercise, or perhaps because it does slightly clarify the logic--to do this in a more functional var-free way, you can do so. In 2.8, it's particularly nice, so even though you're using 2.7.7, I'll give a 2.8 answer.</p> <p>First, we need to set up the problem, which you didn't entirely, but let's suppose we have something like this:</p> <pre><code>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 </code></pre> <p>Now, given this we want to copy the zip file. The trick we can use is the <code>continually</code> method in <code>collection.immutable.Stream</code>. What it does is perform a lazily-evaluated loop for you. You can then take and filter the results to terminate and process what you want. It's a handy pattern to use when you have something that you want to be an iterator, but it isn't. (If the item updates itself you can use <code>.iterate</code> in <code>Iterable</code> or <code>Iterator</code>--that's usually even better.) Here's the application to this case, used twice: once to get the entries, and once to read/write chunks of data:</p> <pre><code>val buffer = new Array[Byte](1024) Stream.continually(zipIn.getNextEntry). takeWhile(_ != null).filter(entryIsValid). foreach(entry =&gt; { zipOut.putNextEntry(new ZipEntry("subdir/"+entry.getName)) Stream.continually(zipIn.read(buffer)).takeWhile(_ != -1). foreach(count =&gt; zipOut.write(buffer,0,count)) }) } zipIn.close zipOut.close </code></pre> <p>Pay close attention to the <code>.</code> at the end of some lines! I would normally write this on one long line, but it's nicer to have it wrap so you can see it all here.</p> <p>Just in case it isn't clear, let's unpack one of the uses of <code>continually</code>.</p> <pre><code>Stream.continually(zipIn.read(buffer)) </code></pre> <p>This asks to keep calling <code>zipIn.read(buffer)</code> for as many times as necessary, storing the integer that results.</p> <pre><code>.takeWhile(_ != -1) </code></pre> <p>This specifies how many times are necessary, returning a stream of indefinite length but which will quit when it hits a <code>-1</code>.</p> <pre><code>.foreach(count =&gt; zipOut.write(buffer,0,count)) </code></pre> <p>This processes the stream, taking each item in turn (the count), and using it to write the buffer. This works in a slightly sneaky way, since you rely upon the fact that <code>zipIn</code> has just been called to get the next element of the stream--if you tried to do this again, not on a single pass through the stream, it would fail because <code>buffer</code> would be overwritten. But here it's okay.</p> <p>So, there it is: a slightly more compact, possibly easier to understand, possibly less easy to understand method that is more functional (though there are still side-effects galore). In 2.7.7, in contrast, I would actually do it the Java way because <code>Stream.continually</code> isn't available, and the overhead of building a custom <code>Iterator</code> isn't worth it for this one case. (It would be worth it if I was going to do more zip file processing and could reuse the code, however.)</p> <hr> <p>Edit: The looking-for-available-to-go-zero method is kind of flaky for detecting the end of the zip file. I think the "correct" way is to wait until you get a <code>null</code> back from <code>getNextEntry</code>. With that in mind, I've edited the previous code (there was a <code>takeWhile(_ =&gt; zipIn.available==1)</code> that is now a <code>takeWhile(_ != null)</code>) and provided a 2.7.7 iterator based version below (note how small the main loop is, once you get through the work of defining the iterators, which do admittedly use vars):</p> <pre><code>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 &amp;&amp; !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 &amp;&amp; count != -1) { count = is.read(ab); waiting=true } count != -1 } def next = { waiting=false; (count,ab) } } (new ZipIter(zipIn)).filter(entryIsValid).foreach(entry =&gt; { zipOut.putNextEntry(new ZipEntry("subdir/"+entry.getName)) (new DataIter(zipIn,buffer)).foreach(cb =&gt; zipOut.write(cb._2,0,cb._1)) }) zipIn.close zipOut.close </code></pre>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. VO
      singulars
      1. This table or related slice is empty.
    2. VO
      singulars
      1. This table or related slice is empty.
    3. VO
      singulars
      1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload