From f6d94a61fcc2d126fa38512bbac03362c2737877 Mon Sep 17 00:00:00 2001 From: Dmitri Zagidulin Date: Mon, 30 Mar 2015 16:33:55 -0400 Subject: [PATCH] Streams - wrap lines to 80 chars --- Streams/Streams.pillar | 403 ++++++++++++++++++++--------------------- 1 file changed, 199 insertions(+), 204 deletions(-) diff --git a/Streams/Streams.pillar b/Streams/Streams.pillar index 1d043b4..a245d11 100644 --- a/Streams/Streams.pillar +++ b/Streams/Streams.pillar @@ -1,56 +1,60 @@ !Streams @cha:streams - - - - Streams are used to iterate over sequences of elements such as sequenced -collections, files, and network streams. -Streams may be either readable, or writeable, or both. -Reading or writing is always relative to the current position in the stream. -Streams can easily be converted to collections, and vice versa. - +collections, files, and network streams. Streams may be either readable, or +writeable, or both. Reading or writing is always relative to the current +position in the stream. Streams can easily be converted to collections, and vice +versa. !!Two sequences of elements -A good metaphor to understand a stream is the following: A stream -can be represented as two sequences of elements: a past element sequence -and a future element sequence. The stream is positioned between the two -sequences. Understanding this model is important since all stream -operations in Smalltalk rely on it. -For this reason, most of the ==Stream== classes are subclasses of ==PositionableStream==. -*fig:_abcde* presents a stream which contains five characters. This stream is in its original position, ''i.e.'', there is no element in the past. You can go back to this position using the message ==PositionableStream>>reset==. +A good metaphor to understand a stream is the following: A stream can be +represented as two sequences of elements: a past element sequence and a future +element sequence. The stream is positioned between the two sequences. +Understanding this model is important since all stream operations in Smalltalk +rely on it. For this reason, most of the ==Stream== classes are subclasses of +==PositionableStream==. *fig:_abcde* presents a stream which contains five +characters. This stream is in its original position, ''i.e.'', there is no +element in the past. You can go back to this position using the message +==PositionableStream>>reset==. -+A stream positioned at its beginning.>file://figures/_abcdeStef.png|label=fig:_abcde+ ++A stream positioned at its +beginning.>file://figures/_abcdeStef.png|label=fig:_abcde+ -Reading an element conceptually means removing the first element of the future element sequence and putting it after the last element in the past -element sequence. After having read one element using the message ==next==, the state of your stream is that shown in *fig:a_bcde*. +Reading an element conceptually means removing the first element of the future +element sequence and putting it after the last element in the past element +sequence. After having read one element using the message ==next==, the state of +your stream is that shown in *fig:a_bcde*. -+The same stream after the execution of the method ==next==: the character ==a== is ''in the past'' whereas ==b==, ==c==, ==d== and ==e== are ''in the future''.>file://figures/a_bcdeStef.png|label=fig:a_bcde+ ++The same stream after the execution of the method ==next==: the character ==a== +is ''in the past'' whereas ==b==, ==c==, ==d== and ==e== are ''in the +future''.>file://figures/a_bcdeStef.png|label=fig:a_bcde+ -Writing an element means replacing the first element of the future sequence by the new one and moving it to the past. *fig:ax_cde* shows the state of the same stream after having written an ==x== using the message ==Stream>>nextPut:== ==anElement==. +Writing an element means replacing the first element of the future sequence by +the new one and moving it to the past. *fig:ax_cde* shows the state of the same +stream after having written an ==x== using the message ==Stream>>nextPut:== +==anElement==. -+The same stream after having written an ==x==.>file://figures/ax_cdeStef.png|label=fig:ax_cde+ ++The same stream after having written an +==x==.>file://figures/ax_cdeStef.png|label=fig:ax_cde+ !!Streams vs. collections - -The collection protocol supports the storage, removal and enumeration -of the elements of a collection, but does not allow these operations -to be intermingled. For example, if the elements of an -==OrderedCollection== are processed by a ==OrderedCollection>>do:== method, it is not -possible to add or remove elements from inside the ==do:== block. -Nor does the collection protocol offer ways to iterate over two -collections at the same time, choosing which collection goes forward -and which does not. Procedures like these require that a traversal index or -position reference is maintained outside of the collection itself: -this is exactly the role of ==ReadStream==, ==WriteStream== and +The collection protocol supports the storage, removal and enumeration of the +elements of a collection, but does not allow these operations to be +intermingled. For example, if the elements of an ==OrderedCollection== are +processed by a ==OrderedCollection>>do:== method, it is not possible to add or +remove elements from inside the ==do:== block. Nor does the collection protocol +offer ways to iterate over two collections at the same time, choosing which +collection goes forward and which does not. Procedures like these require that a +traversal index or position reference is maintained outside of the collection +itself: this is exactly the role of ==ReadStream==, ==WriteStream== and ==ReadWriteStream==. -These three classes are defined to ''stream over'' some collection. -For example, the following snippet creates a stream on an interval, -then it reads two elements. +These three classes are defined to ''stream over'' some collection. For example, +the following snippet creates a stream on an interval, then it reads two +elements. [[[ r := ReadStream on: (1 to: 1000). @@ -60,6 +64,7 @@ r atEnd.--> false ]]] ==WriteStream==s can write data to the collection: + [[[ w := WriteStream on: (String new: 5). w nextPut: $a. @@ -67,24 +72,22 @@ w nextPut: $b. w contents. --> 'ab' ]]] -It is also possible to create ==ReadWriteStream==s that support both -the reading and writing protocols. - -The main problem with ==WriteStream== and ==ReadWriteStream== is -that they only support arrays and strings in Pharo. This is currently -being changed by the development of a new library named Nile, -but for now if you try to stream over another kind of -collection, you will get an error: +It is also possible to create ==ReadWriteStream==s that support both the reading +and writing protocols. +The main problem with ==WriteStream== and ==ReadWriteStream== is that they only +support arrays and strings in Pharo. This is currently being changed by the +development of a new library named Nile, but for now if you try to stream over +another kind of collection, you will get an error: [[[ w := WriteStream on: (OrderedCollection new: 20). w nextPut: 12. --> raises an error ]]] -Streams are not only meant for collections, they can be used for files or sockets -too. The following example creates a file named ==test.txt==, writes two strings to it, separated by a carriage return, and closes the file. - +Streams are not only meant for collections, they can be used for files or +sockets too. The following example creates a file named ==test.txt==, writes two +strings to it, separated by a carriage return, and closes the file. [[[ StandardFileStream @@ -95,25 +98,23 @@ StandardFileStream nextPutAll: 'abcd']. ]]] - The following sections present the protocols in more depth. !!Streaming over collections - -Streams are really useful when dealing with collections of -elements. They can be used for reading and writing elements in -collections. We will now explore the stream features for the -collections. +Streams are really useful when dealing with collections of elements. They can be +used for reading and writing elements in collections. We will now explore the +stream features for the collections. !!!Reading collections +This section presents features used for reading collections. Using a stream to +read a collection essentially provides you a pointer into the collection. That +pointer will move forward on reading and you can place it wherever you want. The +class ==ai=={ReadStream} should be used to read elements from collections. -This section presents features used for reading collections. Using a -stream to read a collection essentially provides you a pointer into the collection. That pointer will move forward on reading and you can place it wherever you want. The class ==ai=={ReadStream} should be used to read elements from collections. - -Methods ==ReadStream>>next== and ==ReadStream>>next:== are used to -retrieve one or more elements from the collection. +Methods ==ReadStream>>next== and ==ReadStream>>next:== are used to retrieve one +or more elements from the collection. [[[ stream := ReadStream on: #(1 (a b c) false). @@ -122,7 +123,6 @@ stream next. --> #(#a #b #c) stream next. --> false ]]] - [[[ stream := ReadStream on: 'abcdef'. stream next: 0. --> '' @@ -131,8 +131,8 @@ stream next: 3. --> 'bcd' stream next: 2. --> 'ef' ]]] -The message ==PositionableStream>>peek== is used when you want to know what is the next element in -the stream without going forward. +The message ==PositionableStream>>peek== is used when you want to know what is +the next element in the stream without going forward. [[[ stream := ReadStream on: '-143'. @@ -146,8 +146,9 @@ number. --> '143' This code sets the boolean variable ==negative== according to the sign of the number in the stream and ==number== to its absolute value. The method ==ReadStream>>upToEnd== returns everything from the current position to the end -of the stream and sets the stream to its end. This code can be -simplified using ==PositionableStream>>peekFor:==, which moves forward if the following element equals the parameter and doesn't move otherwise. +of the stream and sets the stream to its end. This code can be simplified using +==PositionableStream>>peekFor:==, which moves forward if the following element +equals the parameter and doesn't move otherwise. [[[ stream := '-143' readStream. @@ -155,23 +156,23 @@ stream := '-143' readStream. stream upToEnd --> '143' ]]] -==peekFor:== also returns a boolean -indicating if the parameter equals the element. +==peekFor:== also returns a boolean indicating if the parameter equals the +element. -You might have noticed a new way of constructing a stream in -the above example: one can simply send ==SequenceableCollection>>readStream== to a sequenceable collection to get a reading stream on that particular -collection. +You might have noticed a new way of constructing a stream in the above example: +one can simply send ==SequenceableCollection>>readStream== to a sequenceable +collection to get a reading stream on that particular collection. -!!!!!Positioning. - There are methods to position the stream -pointer. If you have the index, you can go directly to it using -==PositionableStream>>position:==. You can request the current position using -==PositionableStream>>position==. Please remember that a stream is not positioned on an -element, but between two elements. The index corresponding to the -beginning of the stream is 0. +!!!!!Positioning -You can obtain the state of the stream depicted in -*fig:ab_cde* with the following code: +There are methods to position the stream pointer. If you have the index, you can +go directly to it using ==PositionableStream>>position:==. You can request the +current position using ==PositionableStream>>position==. Please remember that a +stream is not positioned on an element, but between two elements. The index +corresponding to the beginning of the stream is 0. + +You can obtain the state of the stream depicted in *fig:ab_cde* with the +following code: [[[ stream := 'abcde' readStream. @@ -181,14 +182,13 @@ stream peek --> $c +A stream at position 2>file://figures/ab_cdeStef.png|label=fig:ab_cde+ -To position the stream at the beginning or the end, -you can use -==PositionableStream>>reset== or ==PositionableStream>>setToEnd==. ==PositionableStream>>skip:== and ==PositionableStream>>skipTo:== are used to -go forward to a location relative to the current position: ==skip:== -accepts a number as argument and skips that number of elements whereas -==skipTo:== skips all elements in the stream until it finds an -element equal to its parameter. Note that it positions the stream -after the matched element. +To position the stream at the beginning or the end, you can use +==PositionableStream>>reset== or ==PositionableStream>>setToEnd==. +==PositionableStream>>skip:== and ==PositionableStream>>skipTo:== are used to go +forward to a location relative to the current position: ==skip:== accepts a +number as argument and skips that number of elements whereas ==skipTo:== skips +all elements in the stream until it finds an element equal to its parameter. +Note that it positions the stream after the matched element. [[[ stream := 'abcdef' readStream. @@ -206,14 +206,19 @@ stream contents. --> 'abcdef' As you can see, the letter ==e== has been skipped. -The method ==PositionableStream>>contents== always returns a copy of the entire stream. +The method ==PositionableStream>>contents== always returns a copy of the entire +stream. -!!!!!Testing. - Some methods allow you to test the state of the current stream: -==PositionableStream>>atEnd== returns true if and only if no more elements can be read whereas ==PositionableStream>>isEmpty== returns true if and only if there is no element at all in the collection. +!!!!!Testing -Here is a possible implementation of an algorithm using ==atEnd== that takes two sorted collections as parameters and merges those collections into another sorted collection: +Some methods allow you to test the state of the current stream: +==PositionableStream>>atEnd== returns true if and only if no more elements can +be read whereas ==PositionableStream>>isEmpty== returns true if and only if +there is no element at all in the collection. +Here is a possible implementation of an algorithm using ==atEnd== that takes two +sorted collections as parameters and merges those collections into another +sorted collection: [[[ stream1 := #(1 4 9 11 12 13) readStream. @@ -237,12 +242,13 @@ result. --> an OrderedCollection(1 1 2 3 4 4 5 9 10 11 12 13 13 14 15) !!!Writing to collections +We have already seen how to read a collection by iterating over its elements +using a ==ReadStream==. We'll now learn how to create collections using +==ai=={WriteStream}{}s. -We have already seen how to read a collection by iterating over its -elements using a ==ReadStream==. We'll now learn how to create -collections using ==ai=={WriteStream}{}s. - -==WriteStream==s are useful for appending a lot of data to a collection at various locations. They are often used to construct strings that are based on static and dynamic parts as in this example: +==WriteStream==s are useful for appending a lot of data to a collection at +various locations. They are often used to construct strings that are based on +static and dynamic parts as in this example: [[[ stream := String new writeStream. @@ -258,9 +264,8 @@ This is really a lot.' ]]] This technique is used in the different implementations of the method -==printOn:== for example. There is a simpler and more efficient way -of creating streams if you are only interested in the content of the -stream: +==printOn:== for example. There is a simpler and more efficient way of creating +streams if you are only interested in the content of the stream: [[[ string := String streamContents: @@ -276,12 +281,10 @@ string := String streamContents: string. --> '#(1 2 3) size = 3' ]]] -The method ==SequenceableCollection class>>streamContents:== -@sec:streamContents - creates a collection and a stream on -that collection for you. It then executes the block you gave passing -the stream as a parameter. When the block ends, ==streamContents:== -returns the content of the collection. +The method ==SequenceableCollection class>>streamContents:== @sec:streamContents +creates a collection and a stream on that collection for you. It then executes +the block you gave passing the stream as a parameter. When the block ends, +==streamContents:== returns the content of the collection. The following ==WriteStream== methods are especially useful in this context: @@ -290,21 +293,19 @@ The following ==WriteStream== methods are especially useful in this context: ;==nextPutAll:== :adds each element of the collection, passed as a parameter, to the stream; ;==print:== -:adds the textual representation of the parameter to the stream. +:adds the textual representation of the parameter to the stream. - -There are also methods useful for printing different kinds of characters to -the stream like ==WriteStream>>space==, ==WriteStream>>tab== and -==WriteStream>>cr== (carriage return). Another useful -method is ==WriteStream>>ensureASpace== which ensures that the last character -in the stream is a space; if the last character isn't a space it adds one. +There are also methods useful for printing different kinds of characters to the +stream like ==WriteStream>>space==, ==WriteStream>>tab== and ==WriteStream>>cr== +(carriage return). Another useful method is ==WriteStream>>ensureASpace== which +ensures that the last character in the stream is a space; if the last character +isn't a space it adds one. !!!!!About Concatenation. -Using ==WriteStream>>nextPut:== and ==WriteStream>>nextPutAll:== on a ==WriteStream== is often the best way to -concatenate characters. Using the comma concatenation operator (==,==) is far -less efficient: - +Using ==WriteStream>>nextPut:== and ==WriteStream>>nextPutAll:== on a +==WriteStream== is often the best way to concatenate characters. Using the comma +concatenation operator (==,==) is far less efficient: [[[ [| temp | @@ -319,49 +320,51 @@ less efficient: temp contents] timeToRun --> 1262 "(milliseconds)" ]]] -The reason that using a stream can be much more efficient is that -comma creates a new string containing -the concatenation of the receiver and the argument, so it must copy both of them. -When you repeatedly concatenate onto the same receiver, it gets longer and longer each time, -so that the number of characters that must be copied goes up exponentially. -This also creates a lot of garbage, which must be collected. Using -a stream instead of string concatenation is a well-known optimization. +The reason that using a stream can be much more efficient is that comma creates +a new string containing the concatenation of the receiver and the argument, so +it must copy both of them. When you repeatedly concatenate onto the same +receiver, it gets longer and longer each time, so that the number of characters +that must be copied goes up exponentially. This also creates a lot of garbage, +which must be collected. Using a stream instead of string concatenation is a +well-known optimization. -In fact, you can use ==SequenceableCollection class>>streamContents:== (mentioned on page \pageref{sec:streamContents}) to help you do this: +In fact, you can use ==SequenceableCollection class>>streamContents:== +(mentioned on page \pageref{sec:streamContents}) to help you do this: [[[ String streamContents: [ :tempStream | (1 to: 100000) - do: [:i | tempStream nextPutAll: i asString; space]] + do: [:i | tempStream nextPutAll: i asString; space]] ]]] !!!Reading and writing at the same time +It's possible to use a stream to access a collection for reading and writing at +the same time. Imagine you want to create an ==History== class which will manage +backward and forward buttons in a web browser. A history would react as in +figures from *fig:emptyStream* to *fig:page4*. -It's possible to use a stream to access a collection for reading and -writing at the same time. -Imagine you want to create an ==History== class which will manage -backward and forward buttons in a web browser. -A history would react as in figures from *fig:emptyStream* to -*fig:page4*. - -+A new history is empty. Nothing is displayed in the web browser.>file://figures/emptyStef.png|label=fig:emptyStream+ ++A new history is empty. Nothing is displayed in the web +browser.>file://figures/emptyStef.png|label=fig:emptyStream+ +The user opens to page 1.>file://figures/page1Stef.png|label=fig:page1+ -+The user clicks on a link to page 2.>file://figures/page2Stef.png|label=fig:page2+ - -+The user clicks on a link to page 3.>file://figures/page3Stef.png|label=fig:page3+ ++The user clicks on a link to page +2.>file://figures/page2Stef.png|label=fig:page2+ -+The user clicks on the back button. He is now viewing page 2 again.>file://figures/page2_Stef.png|label=fig:page2_+ ++The user clicks on a link to page +3.>file://figures/page3Stef.png|label=fig:page3+ -+The user clicks again the back button. Page 1 is now displayed.>file://figures/page1_Stef.png|label=fig:page1_+ ++The user clicks on the back button. He is now viewing page 2 +again.>file://figures/page2_Stef.png|label=fig:page2_+ -+From page 1, the user clicks on a link to page 4. The history forgets pages 2 and 3.>file://figures/page4Stef.png|label=fig:page4+ ++The user clicks again the back button. Page 1 is now +displayed.>file://figures/page1_Stef.png|label=fig:page1_+ ++From page 1, the user clicks on a link to page 4. The history forgets pages 2 +and 3.>file://figures/page4Stef.png|label=fig:page4+ This behaviour can be implemented using a ==ReadWriteStream==. - [[[ Object subclass: #History instanceVariableNames: 'stream' @@ -374,8 +377,8 @@ History>>initialize stream := ReadWriteStream on: Array new. ]]] -Nothing really difficult here, we define a new class which contains a -stream. The stream is created during the ==initialize== method. +Nothing really difficult here, we define a new class which contains a stream. +The stream is created during the ==initialize== method. We need methods to go backward and forward: @@ -390,19 +393,19 @@ History>>goForward ^ stream next ]]] -Until then, the code was pretty straightforward. Now, we have to deal -with the ==goTo:== method which should be activated when the user -clicks on a link. A possible solution is: +Until then, the code was pretty straightforward. Now, we have to deal with the +==goTo:== method which should be activated when the user clicks on a link. A +possible solution is: [[[ History>>goTo: aPage stream nextPut: aPage. ]]] -This version is incomplete however. This is because when the user -clicks on the link, there should be no more future pages to go to, -''i.e.'', the forward button must be deactivated. To do this, the simplest -solution is to write ==nil== just after to indicate the history end: +This version is incomplete however. This is because when the user clicks on the +link, there should be no more future pages to go to, ''i.e.'', the forward +button must be deactivated. To do this, the simplest solution is to write +==nil== just after to indicate the history end: [[[ History>>goTo: anObject @@ -411,12 +414,11 @@ History>>goTo: anObject stream back. ]]] -Now, only methods ==canGoBackward== and ==canGoForward== have to be -implemented. +Now, only methods ==canGoBackward== and ==canGoForward== have to be implemented. -A stream is always positioned between two elements. To go backward, -there must be two pages before the current position: one page is the -current page, and the other one is the page we want to go to. +A stream is always positioned between two elements. To go backward, there must +be two pages before the current position: one page is the current page, and the +other one is the page we want to go to. [[[ History>>canGoBackward @@ -427,12 +429,14 @@ History>>canGoForward ]]] Let us add a method to peek at the contents of the stream: + [[[ History>>contents ^ stream contents ]]] And the history works as advertised: + [[[ History new goTo: #page1; @@ -446,37 +450,32 @@ History new !!Using streams for file access - -You have already seen how to stream over collections of elements. It's -also possible to stream over files on your hard disk. -Once created, a stream on a file is really like a stream on a -collection: you will be able to use the same protocol to read, write -or position the stream. -The main difference appears in the creation of the stream. -There are several different ways to create file streams, as we shall now see. +You have already seen how to stream over collections of elements. It's also +possible to stream over files on your hard disk. Once created, a stream on a +file is really like a stream on a collection: you will be able to use the same +protocol to read, write or position the stream. The main difference appears in +the creation of the stream. There are several different ways to create file +streams, as we shall now see. !!!Creating file streams - @sec:creat-file-stre - -To create file streams, you will have to use one of the following -instance creation methods offered by the class ==ai=={FileStream}: +To create file streams, you will have to use one of the following instance +creation methods offered by the class ==ai=={FileStream}: ;==fileNamed:== -:Open a file with the given name for reading and writing. If the file already exists, its prior contents may be modified or replaced, but the file will not be truncated on close. If the name has no directory part, then the file will be created in the default directory. +:Open a file with the given name for reading and writing. If the file already exists, its prior contents may be modified or replaced, but the file will not be truncated on close. If the name has no directory part, then the file will be created in the default directory. ;==newFileNamed:== -:Create a new file with the given name, and answer a stream opened for writing on that file. If the file already exists, ask the user what to do. +:Create a new file with the given name, and answer a stream opened for writing on that file. If the file already exists, ask the user what to do. ;==forceNewFileNamed:== -:Create a new file with the given name, and answer a stream opened for writing on that file. If the file already exists, delete it without asking before creating the new file. +:Create a new file with the given name, and answer a stream opened for writing on that file. If the file already exists, delete it without asking before creating the new file. ;==oldFileNamed:== -:Open an existing file with the given name for reading and writing. If the file already exists, its prior contents may be modified or replaced, but the file will not be truncated on close. If the name has no directory part, then the file will be created in the default directory. +:Open an existing file with the given name for reading and writing. If the file already exists, its prior contents may be modified or replaced, but the file will not be truncated on close. If the name has no directory part, then the file will be created in the default directory. ;==readOnlyFileNamed:== -:Open an existing file with the given name for reading. +:Open an existing file with the given name for reading. - -You have to remember that each time you open a stream on a file, you -have to close it too. This is done through the ==FileStream>>close== method. +You have to remember that each time you open a stream on a file, you have to +close it too. This is done through the ==FileStream>>close== method. [[[ stream := FileStream forceNewFileNamed: 'test.txt'. @@ -490,16 +489,14 @@ stream contents. --> 'This text is written in a file named ''test.txt''' stream close. ]]] - - -The method ==FileStream>>localName== answers the last component of the name of the file. You can -also access the full path name using the method +The method ==FileStream>>localName== answers the last component of the name of +the file. You can also access the full path name using the method ==StandardFileStream>>fullName==. -You will soon notice that manually closing the file stream is -painful and error-prone. That's why ==FileStream== offers a message called ==FileStream class>>forceNewFileNamed:do:== to -automatically close a new stream after evaluating a block that -sets its contents. +You will soon notice that manually closing the file stream is painful and +error-prone. That's why ==FileStream== offers a message called ==FileStream +class>>forceNewFileNamed:do:== to automatically close a new stream after +evaluating a block that sets its contents. [[[ FileStream @@ -514,26 +511,23 @@ string := FileStream string --> 'This text is written in a file named ''test.txt''' ]]] -The stream-creation methods that take a block as an argument first -create a stream on a file, then execute the block with the stream -as an argument, and finally close the stream. These methods return what -is returned by the block, which is to say, the value of the last -expression in the block. This is used in the previous example to get -the content of the file and put it in the variable ==string==. +The stream-creation methods that take a block as an argument first create a +stream on a file, then execute the block with the stream as an argument, and +finally close the stream. These methods return what is returned by the block, +which is to say, the value of the last expression in the block. This is used in +the previous example to get the content of the file and put it in the variable +==string==. !!!Binary streams - @sec:binary-streams +By default, created streams are text-based which means you will read and write +characters. If your stream must be binary, you have to send the message +==FileStream>>binary== to your stream. -By default, created streams are text-based which means you will read -and write characters. If your stream must be binary, you have to send -the message ==FileStream>>binary== to your stream. - -When your stream is in binary mode, you can only write numbers from 0 -to 255 (1 Byte). If you want to use ==nextPutAll:== to write more -than one number at a time, you have to pass a ==ByteArray== as -argument. +When your stream is in binary mode, you can only write numbers from 0 to 255 (1 +Byte). If you want to use ==nextPutAll:== to write more than one number at a +time, you have to pass a ==ByteArray== as argument. [[[ FileStream @@ -553,12 +547,13 @@ FileStream ]. ]]] -Here is another example which creates a picture in a file named -''test.pgm'' (portable graymap file format). You can open this file with your favorite drawing program. +Here is another example which creates a picture in a file named ''test.pgm'' +(portable graymap file format). You can open this file with your favorite +drawing program. [[[ FileStream - forceNewFileNamed: 'test.pgm' + forceNewFileNamed: 'test.pgm' do: [:stream | stream nextPutAll: 'P5'; cr; @@ -574,17 +569,17 @@ FileStream This creates a 4x4 checkerboard as shown in *fig:checkerboard4x4*. -+A 4x4 checkerboard you can draw using binary streams.>file://figures/checkerboard4x4.png|label=fig:checkerboard4x4+ ++A 4x4 checkerboard you can draw using binary +streams.>file://figures/checkerboard4x4.png|label=fig:checkerboard4x4+ !!Chapter summary - -Streams offer a better way than collections to incrementally read and write a sequence of elements. There are easy ways to convert back and forth between streams and collections. +Streams offer a better way than collections to incrementally read and write a +sequence of elements. There are easy ways to convert back and forth between +streams and collections. -Streams may be either readable, writeable or both readable and writeable. -To convert a collection to a stream, define a stream ''on'' a collection, ''e.g.'', ==ReadStream on: (1 to: 1000)==, or send the messages ==readStream==, etc. to the collection. -To convert a stream to a collection, send the message ==contents==. -To concatenate large collections, instead of using the comma operator, it is more efficient to create a stream, append the collections to the stream with ==nextPutAll:==, and extract the result by sending ==contents==. -File streams are by default character-based. Send ==binary== to explicitly make them binary. - -