From: pereges
Subject: reading strings from terminal and writing them to a file
Date: 
Message-ID: <a23af137-f13f-4f64-b82b-870fee2d7d0c@d19g2000yqb.googlegroups.com>
Hi, I've been trying this question:

Q. Write a function that reads strings from the terminal and writes
the strings on different lines to a file called myfile.   Write five
strings to the file.  Show the process of writing those strings.

And here's my attempt:

[11]> (defun write-string-to-file ()
(setf x (read))
(let ((stream (open "test.txt")))
(write-line x)
(close stream)))
WRITE-STRING-TO-FILE
[12]> (write-string-to-file)
"abcd"
abcd
T


The program runs correctly but when I opened the test.txt file, it had
no contents in it. What is the reason ?

From: Paul Donnelly
Subject: Re: reading strings from terminal and writing them to a file
Date: 
Message-ID: <87prfyxird.fsf@plap.localdomain>
pereges <·······@gmail.com> writes:

> Hi, I've been trying this question:
>
> Q. Write a function that reads strings from the terminal and writes
> the strings on different lines to a file called myfile.   Write five
> strings to the file.  Show the process of writing those strings.
>
> And here's my attempt:
>
> [11]> (defun write-string-to-file ()
> (setf x (read))
> (let ((stream (open "test.txt")))
> (write-line x)
> (close stream)))
> WRITE-STRING-TO-FILE
> [12]> (write-string-to-file)
> "abcd"
> abcd
> T
>
>
> The program runs correctly but when I opened the test.txt file, it had
> no contents in it. What is the reason ?

You didn't (WRITE-LINE X) *to* anything but the console.
From: pereges
Subject: Re: reading strings from terminal and writing them to a file
Date: 
Message-ID: <0dc4e00d-75e3-4c82-a02f-9b61617c967b@o11g2000yql.googlegroups.com>
On Mar 31, 3:18 am, Paul Donnelly <·············@sbcglobal.net> wrote:
>
> You didn't (WRITE-LINE X) *to* anything but the console.

Thanks, I got it to work :


(defun write-string-to-file ()
(setf x (read))
(let ((stream (open "myfile.txt" :direction :output :if-
exists :append)))
(write-line x stream)
(close stream)))


Although I notice one thing - this program will not work if the file
myfile.txt does not already exist in the first place.
From: Marco Antoniotti
Subject: Re: reading strings from terminal and writing them to a file
Date: 
Message-ID: <7f361a40-1509-40ff-8aa0-e35186a4caad@c9g2000yqm.googlegroups.com>
On Mar 31, 10:13 am, pereges <·······@gmail.com> wrote:
> On Mar 31, 3:18 am, Paul Donnelly <·············@sbcglobal.net> wrote:
>
>
>
> > You didn't (WRITE-LINE X) *to* anything but the console.
>
> Thanks, I got it to work :
>
> (defun write-string-to-file ()
> (setf x (read))
> (let ((stream (open "myfile.txt" :direction :output :if-
> exists :append)))
> (write-line x stream)
> (close stream)))
>
> Although I notice one thing - this program will not work if the file
> myfile.txt does not already exist in the first place.

Try

(defun write-string-to-file (&optional (file "mufile.txt"))
   (with-open-file (f file :direction :output :if-does-not-
exist :create
                           :if-exists :append)
       (let ((x (read-line))
          (write-line x f))))

WITH-OPEN-FILE opens and closes the file for you.

Cheers
--
Marco
From: pereges
Subject: Re: reading strings from terminal and writing them to a file
Date: 
Message-ID: <ad23496d-93a9-4b88-a752-dff94c2c1aeb@o6g2000yql.googlegroups.com>
On Mar 31, 4:27 am, Marco Antoniotti <·······@gmail.com> wrote:

> Try
>
> (defun write-string-to-file (&optional (file "mufile.txt"))
>    (with-open-file (f file :direction :output :if-does-not-
> exist :create
>                            :if-exists :append)
>        (let ((x (read-line))
>           (write-line x f))))
>
> WITH-OPEN-FILE opens and closes the file for you.
>
> Cheers
> --
> Marco

done..btw this also works very well:

(defun write-string-to-file ()
(setf x (read))
(let ((stream (open "myfile.txt" :direction :output :if-
exists :append :if-does-not-exist :create)))
(write-line x stream)
(close stream)))
From: Pascal J. Bourguignon
Subject: Re: reading strings from terminal and writing them to a file
Date: 
Message-ID: <7cvdpqovrw.fsf@pbourguignon.anevia.com>
pereges <·······@gmail.com> writes:

> On Mar 31, 4:27�am, Marco Antoniotti <·······@gmail.com> wrote:
>
>> Try
>>
>> (defun write-string-to-file (&optional (file "mufile.txt"))
>> � �(with-open-file (f file :direction :output :if-does-not-
>> exist :create
>> � � � � � � � � � � � � � �:if-exists :append)
>> � � � �(let ((x (read-line))
>> � � � � � (write-line x f))))
>>
>> WITH-OPEN-FILE opens and closes the file for you.
>>
>> Cheers
>> --
>> Marco
>
> done..btw this also works very well:
>
> (defun write-string-to-file ()
> (setf x (read))
> (let ((stream (open "myfile.txt" :direction :output :if-
> exists :append :if-does-not-exist :create)))
> (write-line x stream)
> (close stream)))

No, it doesn't work as well.  For example, if an error or a user
interruption occurs while executing WRITE-LINE, the stream won't be
closed. 

Moreover, you are assigning a value to an undefined variables.  This
is implementation dependant, and this may have bad consequences.  For
example, it may change the meaning of other unrelated functions.

The correct way to write it is, I'll repeat:

(defun write-string-to-file (&optional (file "myfile.txt"))
  (with-open-file (f file :direction :output
                          :if-does-not-exist :create
                          :if-exists :append)
      (let ((x (read-line)))
         (write-line x f))))


However, given your problem statement,  you should divide it in reusable pieces.


(defun copy-lines (input-stream output-stream &key count eof-error-p)
   "Copies at most count lines, or until EOF, from input-stream to output-stream"
   (if count
      (loop :repeat count 
            :for line = (read-line input-stream eof-error-p input-stream)
            :until (eq line input-stream)
            :do (write-line line output-stream))
      (loop :for line = (read-line input-stream eof-error-p input-stream)
            :until (eq line input-stream)
            :do (write-line line output-stream))))



(defun copy-5-lines-from-terminal-to-myfile ()
   (with-open-file (output-stream "myfile.txt"
                       :direction :output
                       :if-does-not-exist :create
                       :if-exists :supersede)
      (copy-lines *terminal-io* ; remember, the problem statement says the TERMINAL
                  output-stream
                  :count 5)))

 
-- 
__Pascal Bourguignon__
From: Mark Wooding
Subject: Re: reading strings from terminal and writing them to a file
Date: 
Message-ID: <873acthox6.fsf.mdw@metalzone.distorted.org.uk>
pereges <·······@gmail.com> writes:

> On Mar 31, 4:27 am, Marco Antoniotti <·······@gmail.com> wrote:
> > WITH-OPEN-FILE opens and closes the file for you.
>
> done..btw this also works very well:
>
> (let ((stream (open "myfile.txt" :direction :output :if-
[...]
> (close stream)))

Marco's suggestion works better if the code in the middle does a
nonlocal transfer (e.g., there's an I/O error).  Your version risks
leaking an OS-level file handle -- the Lisp system might or might not
have a finalizer on streams which does this for you when the stream is
garbage-collected.

-- [mdw]
From: pereges
Subject: Re: reading strings from terminal and writing them to a file
Date: 
Message-ID: <cbaa19ff-56c1-4da6-a0b1-98c3dbd456d5@e5g2000vbe.googlegroups.com>
Thank you very much for the help.

There is a second part of the problem as well:

Write a function that takes a filename and returns a list of the
strings in the file.  Test your function on the strings of #1.

And this is how I solved it:

[12]> (defun return-list-of-strings (fname)
(setf y nil)
(let ((in (open fname :if-does-not-exist nil)))
  (when in
    (loop for line = (read-line in nil)
         while line do (setf y (cons line y)))
(close in)) y))
RETURN-LIST-OF-STRINGS

I guess I will modify it use with-open-file here as well.
From: Pascal J. Bourguignon
Subject: Re: reading strings from terminal and writing them to a file
Date: 
Message-ID: <7cab71pxma.fsf@pbourguignon.anevia.com>
pereges <·······@gmail.com> writes:

> Thank you very much for the help.
>
> There is a second part of the problem as well:
>
> Write a function that takes a filename and returns a list of the
> strings in the file.  Test your function on the strings of #1.
>
> And this is how I solved it:
>
> [12]> (defun return-list-of-strings (fname)
> (setf y nil)
> (let ((in (open fname :if-does-not-exist nil)))
>   (when in
>     (loop for line = (read-line in nil)
>          while line do (setf y (cons line y)))
> (close in)) y))
> RETURN-LIST-OF-STRINGS
>
> I guess I will modify it use with-open-file here as well.

You should forget SETF for a while.  Use LET instead.

  (let ((y nil))
     (do-stuff
        (setf y (some-thing y)))
     y)



But LOOP can COLLECT items itself, so you don't need to deal with Y at
all:

    (loop :for line = (read-line in nil)
          :while line :collect line)

And if you use WITH-OPEN-FILE, then you don't have the problem of
saving the result to return it later, since this is handled by
WITH-OPEN-FILE.

-- 
__Pascal Bourguignon__
From: pereges
Subject: Re: reading strings from terminal and writing them to a file
Date: 
Message-ID: <4c73a358-09a0-4090-998b-ebdde5b6b625@r36g2000vbr.googlegroups.com>
On Mar 31, 10:37 am, ····@informatimago.com (Pascal J. Bourguignon)
wrote:
> pereges <·······@gmail.com> writes:
> > Thank you very much for the help.
>
> > There is a second part of the problem as well:
>
> > Write a function that takes a filename and returns a list of the
> > strings in the file.  Test your function on the strings of #1.
>
> > And this is how I solved it:
>
> > [12]> (defun return-list-of-strings (fname)
> > (setf y nil)
> > (let ((in (open fname :if-does-not-exist nil)))
> >   (when in
> >     (loop for line = (read-line in nil)
> >          while line do (setf y (cons line y)))
> > (close in)) y))
> > RETURN-LIST-OF-STRINGS
>
> > I guess I will modify it use with-open-file here as well.
>
> You should forget SETF for a while.  Use LET instead.
>
>   (let ((y nil))
>      (do-stuff
>         (setf y (some-thing y)))
>      y)
>
> But LOOP can COLLECT items itself, so you don't need to deal with Y at
> all:
>
>     (loop :for line = (read-line in nil)
>           :while line :collect line)
>
> And if you use WITH-OPEN-FILE, then you don't have the problem of
> saving the result to return it later, since this is handled by
> WITH-OPEN-FILE.
>
> --
> __Pascal Bourguignon__


Are there any advantages of using let over setf ?
From: Raffael Cavallaro
Subject: Re: reading strings from terminal and writing them to a file
Date: 
Message-ID: <572fe1d0-93da-4eaf-9b36-09c5443f2e33@n33g2000vba.googlegroups.com>
On Mar 31, 11:01 am, pereges <·······@gmail.com> wrote:

> Are there any advantages of using let over setf ?

<http://en.wikipedia.org/wiki/Scope_(programming)
#Static_versus_dynamic_scoping>

a partial quote referring to lexical scope (i.e., let, not setf):

"Because matching a variable to its binding only requires analysis of
the program text, this type of [static] scoping is sometimes also
called lexical scoping. Static scope is standard in modern functional
languages such as ML and Haskell because it allows the programmer to
reason as if variable bindings are carried out by substitution. Static
scoping also makes it much easier to make modular code and reason
about it, since its binding structure can be understood in isolation.
In contrast, dynamic scope forces the programmer to anticipate all
possible dynamic contexts in which the module's code may be invoked."
From: Nick Saika
Subject: Re: reading strings from terminal and writing them to a file
Date: 
Message-ID: <8bd05197-b7db-45a9-90bf-518cc8249000@j12g2000vbl.googlegroups.com>
> Are there any advantages of using let over setf ?

Yes - lexical scoping, as Pascal put it, allows you to quickly know
what value a variable holds, and how it got that value. Plus, it
nearly eliminates memory work of SETF-/SETQ-ing variables. Very rarely
will/should you ever have to use SETF or SETQ; in which case, the only
times I would ever use SETF/SETQ is when I'm mucking about in the REPL
and I have several global variables defined (with DEFVAR and/or
DEFPARAMETER).

Ah - yes, which alludes into a situation where you would use SETF (or
SETQ): when you have a global variable defined and you want to change
its value.

For the other 99% of stuff involving variables, do it all within a LET
or LET* form.
From: Pascal J. Bourguignon
Subject: Re: reading strings from terminal and writing them to a file
Date: 
Message-ID: <7c63hppv36.fsf@pbourguignon.anevia.com>
pereges <·······@gmail.com> writes:

> On Mar 31, 10:37�am, ····@informatimago.com (Pascal J. Bourguignon)
> wrote:
>> You should forget SETF for a while. �Use LET instead.
>>
>> � (let ((y nil))
>> � � �(do-stuff
>> � � � � (setf y (some-thing y)))
>> � � �y)
>
> Are there any advantages of using let over setf ?

Yes, a lot.

Using let, you can know what the value of a variable is just looking
at the source text.  On the other hand, when using setf, you have to
take into account the time and the execution paths.

But the problem is worse, since  you didn't declare any variable named
Y, anything can happen.  For example, Y may be declared automatically
special, and this may change the meaning of other functions using this
variable.  (That's the reason why special variables must be named (by
convention) with stars before and after: *y*, to avoid collision with
lexical variables).

-- 
__Pascal Bourguignon__
From: Raffael Cavallaro
Subject: Re: reading strings from terminal and writing them to a file
Date: 
Message-ID: <271ccce5-a26e-4166-8ad6-cf7d983ca827@r28g2000vbp.googlegroups.com>
On Mar 31, 10:37 am, ····@informatimago.com (Pascal J. Bourguignon)
wrote:

> You should forget SETF for a while.  Use LET instead.


Right. Pereges, notice how let and with-open-file are structurally
similar? That's because conceptually they do similar things. They give
you the ability to ensure that their effects (binding a variable,
opening a file) are limited to the scope of that block of code only.
This is a very frequent common lisp idiom, so it would be a good idea
to internalize it and use it in your code.
From: William James
Subject: Re: reading strings from terminal and writing them to a file
Date: 
Message-ID: <gqtgbd0j9n@enews2.newsguy.com>
Marco Antoniotti wrote:

> On Mar 31, 10:13�am, pereges <·······@gmail.com> wrote:
> > On Mar 31, 3:18�am, Paul Donnelly <·············@sbcglobal.net>
> > wrote:
> > 
> > 
> > 
> > > You didn't (WRITE-LINE X) to anything but the console.
> > 
> > Thanks, I got it to work :
> > 
> > (defun write-string-to-file ()
> > (setf x (read))
> > (let ((stream (open "myfile.txt" :direction :output :if-
> > exists :append)))
> > (write-line x stream)
> > (close stream)))
> > 
> > Although I notice one thing - this program will not work if the file
> > myfile.txt does not already exist in the first place.
> 
> Try
> 
> (defun write-string-to-file (&optional (file "mufile.txt"))
>    (with-open-file (f file :direction :output :if-does-not-
> exist :create
>                            :if-exists :append)
>        (let ((x (read-line))
>           (write-line x f))))
> 
> WITH-OPEN-FILE opens and closes the file for you.
> 
> Cheers

Everything is more difficult when using CommuneLisp.

Ruby:

def write_string_to_file  filename = "junk0"
  File.open( filename, "a" ){|f| f.puts gets }
end

This works correctly even if the file doesn't exist.


-- 
Common Lisp is a significantly ugly language.  --- Dick Gabriel 
The good news is, it's not Lisp that sucks, but Common Lisp.
 --- Paul Graham
Common LISP is the PL/I of Lisps.  ---  Jeffrey M. Jacobs
From: Thomas A. Russ
Subject: Re: reading strings from terminal and writing them to a file
Date: 
Message-ID: <ymihc196yqc.fsf@blackcat.isi.edu>
"William James" <> writes:
> Ruby:
> 
> def write_string_to_file  filename = "junk0"
>   File.open( filename, "a" ){|f| f.puts gets }
> end
> 
> This works correctly even if the file doesn't exist.

But does it work correctly if the file DOES exist?

What does it do then?  Append to the file?  Or overwrite it?  Or warn
you? or what?

-- 
Thomas A. Russ,  USC/Information Sciences Institute
From: Evans Winner
Subject: Re: reading strings from terminal and writing them to a file
Date: 
Message-ID: <86tz59q9m0.fsf@timbral.net>
"William James" <> writes:

    Everything is more difficult when using CommuneLisp.
 
    Ruby:
 
    def write_string_to_file  filename = "junk0"
      File.open( filename, "a" ){|f| f.puts gets }
    end

Is this a joke?  How do you tell where your s-expressions
start and end without the parentheses?  And all those
brackets and dots and pipes... I think I'd better stick to
something simple like Lisp.

Anyway, this is was a good thread.  Who says c.l.l is
unfriendly to polite requests for help?
From: Kaz Kylheku
Subject: Re: reading strings from terminal and writing them to a file
Date: 
Message-ID: <20090410121750.39@gmail.com>
On 2009-04-01, Evans Winner <······@timbral.net> wrote:
> "William James" <> writes:
>
>     Everything is more difficult when using CommuneLisp.
>  
>     Ruby:
>  
>     def write_string_to_file  filename = "junk0"
>       File.open( filename, "a" ){|f| f.puts gets }
>     end
>
> Is this a joke?

Everything is more difficult when using Common Lisp, depending on what
universe you have in mind when you say ``everything''.

To a moron like William James, ``everything'' refers to some trivial three line
programs.

> How do you tell where your s-expressions
> start and end without the parentheses?

If the program can fit on less than half a page of text in a book, it doesn't
matter. You can explain it in the caption of its text box.

> And all those
> brackets and dots and pipes... I think I'd better stick to
> something simple like Lisp.

Anyone with two brain cells to rub together would write the above using the
POSIX shell.

  #
  # Read string from terminal into $input variable;
  # append this as a line to the file named by $filename.
  #

  read -r input; echo "$input" >> "$filename"

Way, way, way, clearer than the Ruby gobbledygook. It's far more portable: it's
written in a formally standardized language, and runs on many systems without
the need to install any additional software. The language has multiple
implementations, too, both free and proprietary.

The shell can wipe the floor with Ruby as a scripting language, and Ruby is not
suitable for large software engineering tasks either.
From: namekuseijin
Subject: Re: reading strings from terminal and writing them to a file
Date: 
Message-ID: <47d5d5ce-6776-4c27-9046-3afdb41fefcf@e3g2000vbe.googlegroups.com>
On Apr 1, 1:51 pm, Kaz Kylheku <········@gmail.com> wrote:
> > "William James" <> writes:
> >       File.open( "filename", "a" ){|f| f.puts gets }
>
>   read -r input; echo "$input" >> "$filename"
>
> Way, way, way, clearer than the Ruby gobbledygook.

Not at all.  It's just another kind of gobbledygook.

In any case, *both* are far less gobbledygook than:
(with-open-file (f "filename"
                     :direction :output
                     :if-does-not-exist :create
                     :if-exists :append)
   (let ((x (read-line))
      (write-line x f)))

hands down!

CL has many great strengths, conciseness for short one-liners isn't
one of them.  Feeding the troll won't help it.

> It's far more portable:
> written in a formally standardized language, and runs on many systems without
> the need to install any additional software.

It's standardized in the *nix world, not on Windows where you'd have
to install cygwin or ruby anyway.
From: David Golden
Subject: Re: reading strings from terminal and writing them to a file
Date: 
Message-ID: <gr15gd$t3k$1@aioe.org>
namekuseijin wrote:


> It's standardized in the *nix world, not on Windows where you'd have
> to install cygwin or ruby anyway.


Or Microsoft's  "Windows Services for UNIX" (a.k.a. Interix), less
featureful than cygwin in practice, but includes ksh and tcsh -
http://technet.microsoft.com/en-us/library/bb463207.aspx
- and of course all officially microsofty, like.
From: Marco Antoniotti
Subject: Re: reading strings from terminal and writing them to a file
Date: 
Message-ID: <21ccf6a8-209b-413a-a284-b2e8e4dd3d4a@h28g2000yqd.googlegroups.com>
On Mar 31, 6:28 pm, "William James" <> wrote:
> Marco Antoniotti wrote:
> > On Mar 31, 10:13 am, pereges <·······@gmail.com> wrote:
> > > On Mar 31, 3:18 am, Paul Donnelly <·············@sbcglobal.net>
> > > wrote:
>
> > > > You didn't (WRITE-LINE X) to anything but the console.
>
> > > Thanks, I got it to work :
>
> > > (defun write-string-to-file ()
> > > (setf x (read))
> > > (let ((stream (open "myfile.txt" :direction :output :if-
> > > exists :append)))
> > > (write-line x stream)
> > > (close stream)))
>
> > > Although I notice one thing - this program will not work if the file
> > > myfile.txt does not already exist in the first place.
>
> > Try
>
> > (defun write-string-to-file (&optional (file "mufile.txt"))
> >    (with-open-file (f file :direction :output :if-does-not-
> > exist :create
> >                            :if-exists :append)
> >        (let ((x (read-line))
> >           (write-line x f))))
>
> > WITH-OPEN-FILE opens and closes the file for you.
>
> > Cheers
>
> Everything is more difficult when using CommuneLisp.
>
> Ruby:
>
> def write_string_to_file  filename = "junk0"
>   File.open( filename, "a" ){|f| f.puts gets }
> end
>
> This works correctly even if the file doesn't exist.

Hey.... it has been a while...  Where's your homework?

Cheers
--
Marco