Allright... i've almost finished my second reading of Winston&Horn, i've skimmed through some web-available tutorials, i'm a professional programmer, i should be able to do this Lisp thing now, right? wrong.
I'm having trouble going from the tutorials to the real world. For example, a lot of what i do is related to processing text files. So, i though i'd write a simple/stupid program to count the number of lines in a file. Here it is:
;; count number of lines in file key.html (with-open-file (ifile "key.html" :direction :input) (setf nlines 0) (loop (if (read-line ifile nil) (setf nlines (+ 1 nlines)) (return nlines))))
It doesn't work (it just hangs, so i think it's stuck in the loop). I thought it would read lines until the end of the file, when the read- line would return nil, and that would trigger the second form inside the if (a return, to get out of the loop).
I'm sorry for posting something so stupid, but why doesn't this work?
-- Glauber Ribeiro theglau...@my-deja.com "Opinions stated are my own and not representative of Experian"
> It doesn't work (it just hangs, so i think it's stuck in the loop). I
[...]
OK. Using TRACE, i found that read-line wasn't returning nil, but something like #<END-OF-FILE>. So i added the third optional parameter, the one that tells it what to return at end of file. This made me do the (IF) backwards:
The Glauber wrote: > ;; count number of lines in file key.html > (with-open-file (ifile "key.html" :direction :input) > (setf nlines 0) > (loop > (if (read-line ifile nil) > (setf nlines (+ 1 nlines)) > (return nlines))))
> It doesn't work (it just hangs, so i think it's stuck in the loop). I > thought it would read lines until the end of the file, when the read- > line would return nil, and that would trigger the second form inside > the if (a return, to get out of the loop).
> I'm sorry for posting something so stupid, but why doesn't this work?
It works for me in cmucl (warning on the nondefined variable "nlines") It works in Allegro Trial Edition 5.0.1 for Linux (without warnings) It works in Lispworks Personal Edition 4.1.18 (without warnings) But it seems NOT to work with clisp !!! (as the former author noted, it blocks)
have not looked further into it, but I found this interesting enough to post it.
* The Glauber <theglau...@my-deja.com> | I'm having trouble going from the tutorials to the real world. For | example, a lot of what i do is related to processing text files. | So, i though i'd write a simple/stupid program to count the number | of lines in a file. Here it is: | | ;; count number of lines in file key.html | (with-open-file (ifile "key.html" :direction :input) | (setf nlines 0) | (loop | (if (read-line ifile nil) | (setf nlines (+ 1 nlines)) | (return nlines))))
You're using a free variable, nlines, in this code. You should not use a free variable unless you have very specific need for it. This does not affect the correctness of your code, however.
| It doesn't work (it just hangs, so i think it's stuck in the loop).
Try being explicit in the value of eof-value, i.e.
(read-line ifile nil nil)
| I thought it would read lines until the end of the file, when the | read- line would return nil, and that would trigger the second form | inside the if (a return, to get out of the loop).
I think it would be instructive to print the value from read-line:
(print (read-line ifile nil))
print returns its value.
| I'm sorry for posting something so stupid, but why doesn't this work?
Seems like a bug in your Common Lisp implementation. Which is it?
#:Erik -- If this is not what you expected, please alter your expectations.
> So, i plod along, undaunted... > I'm pretty sure this is an ugly program, and i appreciate suggestions.
Ok, a few suggestions, then. As Erik mentioned, nlines is a free variable. Use a let form. It's probably stylistic, but I prefer the much-more-compact (incf) function to using (set x (+ 1 x)). Also, you can replace the if construct with features of the loop facility -- use while and finally. Some people may argue this stylistically, but I like prefer it [1].
So, the revised, slightly cleaner code looks like this:
(with-open-file (ifile "key.html" :direction :input) (let ((nlines 0)) (loop while (read-line ifile nil nil) do (incf nlines) finally (return nlines))))
The even more compact version uses the for and from loop functionality:
(with-open-file (ifile "key.html" :direction :input) (loop for nlines from 0 while (read-line ifile nil nil) finally (return nlines)))
Thom
[1] I learned my Lisp somewhat in a vacuum, so I sometimes find that what I consider elegant or clean, other people consider ugly or unclear.
Thom Goodsell Scientist t...@cra.com Charles River Analytics (617) 491-3474 x574 Cambridge, MA, USA http://www.cra.com/
> So, i plod along, undaunted... > I'm pretty sure this is an ugly program, and i appreciate suggestions.
This isn't meant to reopen the great LOOP flame war but one way of doing it is:
(with-open-file (ifile "key.html" :direction :input) (loop for line = (read-line ifile nil) while line counting 1))
If the loop implementation of your CL system supports extending loop paths you can even write a loop path that gives you each line in the file. I have such a thing in my .clinit.
Then you can do:
(loop for line being each line of ifile count 1)
If you use the simple loop, you might consider do:
> > It doesn't work (it just hangs, so i think it's stuck in the loop). I > [...]
> OK. Using TRACE, i found that read-line wasn't returning nil, but > something like #<END-OF-FILE>. So i added the third optional parameter, > the one that tells it what to return at end of file. This made me do > the (IF) backwards:
I think you should report that as a bug to the implementors of CLISP. The HyperSpec is quite clear in that the default value for eof-value should be nil, and therefore (read-line stream nil) should always return nil at the end of file. At least that is my reading of the standard.
BTW: I very often supply the eof-value explicitly, for reasons of clarity.
> So, i plod along, undaunted... > I'm pretty sure this is an ugly program, and i appreciate suggestions.
OK, you asked for it ;)
- The most obviously mistake lies in your use of setf to establish a new variable. In the absence of any binding, this will be treated as a reference to a global special variable, which you don't want. Use let to introduce local lexical variables. Once such a binding exists, you can alter the value of the binding via setf & friends, if you want to.
- The variable names are distinctly unlispy: Since CL (and Scheme) allows useful separator characters in symbols, and your environment relieves you of the burden of retyping long names, you are free to use long, self-descriptive names.
- Wrap your code fragment in a function definition, and you get a ready made line-counting function for free...
- While working with low-level looping constructs can be instructive, most programmers would use a higher-level looping construct to more clearly express the intent of the code.
- Don't print the result yourself, since the surrounding code or the top-level read-eval-print loop will already do this for you
- Use documentation strings to document your functions
Given all of the above, your function might be written like this
(defun count-lines-in-file (filename) "Count the lines in the file indicated by `filename'." (with-open-file (stream filename :direction :input) (do ((line-count 0 (1+ line-count))) ((null (read-line stream nil nil)) line-count))))
Or, using the extended loop facility:
(defun count-lines-in-file (filename) "Count the lines in the file indicated by `filename'." (with-open-file (stream filename :direction :input) (loop while (read-line stream nil nil) sum 1)))
Or, using simple loop, like you did:
(defun count-lines-in-file (filename) "Count the lines in the file indicated by `filename'." (with-open-file (stream filename :direction :input) (let ((line-count 0)) (loop (if (read-line stream nil nil) (incf line-count) (return line-count))))))
Beware that all of the code is untested, though ;)
Regs, Pierre.
-- Pierre Mai <p...@acm.org> PGP and GPG keys at your nearest Keyserver "One smaller motivation which, in part, stems from altruism is Microsoft- bashing." [Microsoft memo, see http://www.opensource.org/halloween1.html]
this works fine for me in the current version of CLISP. in older versions of CLISP, (read-line VAR nil) returned #<EOF> and not NIL on eof. you should add a third arg (read-line ifile nil nil) and it will work. sorry about the bug. You will get a faster response if you ask CLISP-specific questions on the clisp-list mailing list (see http://clisp.cons.org)
* Lieven Marchand <m...@bewoner.dma.be> | This isn't meant to reopen the great LOOP flame war but one way of | doing it is: | | (with-open-file (ifile "key.html" :direction :input) | (loop for line = (read-line ifile nil) | while line | counting 1))
Simply using while (read-line ifile nil nil) seems even more compact.
However, I have this negative gut reaction to wanton waste, as in effectively allocating as many strings as there are lines for no good reason, just exercising the garbage collector, so while we're at it, how about
(loop for char = (read-char ifile nil nil) while char count (char= char #\newline))
Note that read-line will return nil on an empty file, but the data leading up to the end-of-file if no newline intervened and then nil on the next call, effectively counting a non-empty file containing no newlines as having one line. This may be relevant.
| (loop for line being each line of ifile | count 1)
Elegant.
#:Erik -- If this is not what you expected, please alter your expectations.
>> It doesn't work (it just hangs, so i think it's stuck in the loop). I >[...]
>OK. Using TRACE, i found that read-line wasn't returning nil, but >something like #<END-OF-FILE>. So i added the third optional parameter, >the one that tells it what to return at end of file. This made me do >the (IF) backwards:
#<END-OF-FILE> may be "pretty cool," but certainly isn't conformant with the standard.
>So, i plod along, undaunted... >I'm pretty sure this is an ugly program, and i appreciate suggestions.
I somewhat like the following: (with-open-file (ifile "c:/home/html/lynx-boo.htm" :direction :input) (loop for line = (read-line ifile nil 'eof) for lines = 0 then (+ lines 1) ;;; Line count... until (eql line 'eof) finally (return lines)))
The use of "for" clauses has the merit of eliminating the extra control structures inside the loop. -- cbbro...@acm.org - <http://www.ntlug.org/~cbbrowne/lsf.html> Rules of the Evil Overlord #132. "Before appointing someone as my trusted lieutenant, I will conduct a thorough background investigation and security clearance. <http://www.eviloverlord.com/>
>> It doesn't work (it just hangs, so i think it's stuck in the loop). I >> thought it would read lines until the end of the file, when the read- >> line would return nil, and that would trigger the second form inside >> the if (a return, to get out of the loop).
>> I'm sorry for posting something so stupid, but why doesn't this work?
>It works for me in cmucl (warning on the nondefined variable "nlines") >It works in Allegro Trial Edition 5.0.1 for Linux (without warnings) >It works in Lispworks Personal Edition 4.1.18 (without warnings) >But it seems NOT to work with clisp !!! (as the former author noted, it >blocks)
>have not looked further into it, but I found this interesting enough to >post it.
It indeed sounds like a CLISP bug; the HyperSpec indicates that for: read-line &optional input-stream eof-error-p eof-value recursive-p
the default value for eof-value is nil, which I'd expect to provide the desired result.
The code's a bit ugly, but I'd sure expect the call to return "nil" without much ado... -- cbbro...@ntlug.org - <http://www.ntlug.org/~cbbrowne/lsf.html> "Consistency is the single most important aspect of *ideology.* Reality is not nearly so consistent." - <cbbro...@hex.net>
Jochen Schmidt wrote: > It works for me in cmucl (warning on the nondefined variable "nlines") > It works in Allegro Trial Edition 5.0.1 for Linux (without warnings) > It works in Lispworks Personal Edition 4.1.18 (without warnings)
Both Allegro and Lispworks have both interpreter and compiler. I believe both would signal a warning if the code were compiled, but choose not to signal warnings in interpreted execution. If they did, the poor programmer would continually be harrassed by warnings when executing typical debugging forms at the top-level listener, such as
(setq foo ...)
BTW, in an earlier post Erik wrote:
You're using a free variable, nlines, in this code. You should not use a free variable unless you have very specific need for it. This does not affect the correctness of your code, however.
I think this is incorrect. Use of a free, undeclared variable name is illegal. See ANS 3.1.2.1.1.2 Dynamic Variables. It happens that every known implementation will treat a free reference to an undeclared variable as if the variable had been declared special (and typically warn in the compiler) but I know of nothing in CL semantics that requires this. Signalling error on reference to an undeclared free variable name would also conform. (That's what Java does, ;-)
* "Steven M. Haflich" <hafl...@pacbell.net> | BTW, in an earlier post Erik wrote: | | You're using a free variable, nlines, in this code. You should not | use a free variable unless you have very specific need for it. This | does not affect the correctness of your code, however. | | I think this is incorrect. Use of a free, undeclared variable name | is illegal.
"Free variable" is orthogonal to "undeclared variable". Your gripe is about "undeclared variables". I talked about "free variable". Please, some precision when you pick nits.
#:Erik -- If this is not what you expected, please alter your expectations.
Erik Naggum wrote: > * "Steven M. Haflich" <hafl...@pacbell.net> > | BTW, in an earlier post Erik wrote: > | > | You're using a free variable, nlines, in this code. You should not > | use a free variable unless you have very specific need for it. This > | does not affect the correctness of your code, however. > | > | I think this is incorrect. Use of a free, undeclared variable name > | is illegal.
> "Free variable" is orthogonal to "undeclared variable". Your gripe > is about "undeclared variables". I talked about "free variable". > Please, some precision when you pick nits.
> #:Erik > -- > If this is not what you expected, please alter your expectations.
>> * "Steven M. Haflich" <hafl...@pacbell.net> >> | BTW, in an earlier post Erik wrote: >> | >> | You're using a free variable, nlines, in this code. You should not >> | use a free variable unless you have very specific need for it. This >> | does not affect the correctness of your code, however. >> | >> | I think this is incorrect. Use of a free, undeclared variable name >> | is illegal.
>> "Free variable" is orthogonal to "undeclared variable". Your gripe >> is about "undeclared variables". I talked about "free variable". >> Please, some precision when you pick nits.
>What is a "free variable?"
One that is malloc()ed and then released under the GNU General Public License.
[Now ducking quickly, to dodge bullets, boiling oil, and projectile vomit...] -- aa...@freenet.carleton.ca - <http://www.hex.net/~cbbrowne/> I knew you weren't really interested.
> > * "Steven M. Haflich" <hafl...@pacbell.net> > > | BTW, in an earlier post Erik wrote: > > | > > | You're using a free variable, nlines, in this code. You should not > > | use a free variable unless you have very specific need for it. This > > | does not affect the correctness of your code, however. > > | > > | I think this is incorrect. Use of a free, undeclared variable name > > | is illegal.
> > "Free variable" is orthogonal to "undeclared variable". Your gripe > > is about "undeclared variables". I talked about "free variable". > > Please, some precision when you pick nits.
This is a reply to Erik's comment, not Steve Long's, but Erik's posting has not yet appeared on my news server.
Indeed, the "free" use of a variable is orthogonal to the "undeclared" use of a variable, but that does in itself not mean anything. A Usenet posting may be factually incorrect, and it may be stupid. But it may be stupid without being incorrect, and it may be incorrect without being stupid, and it may be both stupid and incorrect. These two properties are orthogonal. I know this because I have both read and writen many Usenet postings over the past 17 years.
Similarly, a reference to a variable may be either or both of "undeclared" and "free". Rather than muck around in the meaning of these terms, I'd ask Erik the following focussed question:
Assume a freshly-booted ANS-compliant CL implementation. What behavior, if any, is guaranteed by execution of the following form?
(print (progn (setq pie 22/7) pie))
Now, in every implementation I know, this will print an approximation of PI, with (if in the interative top level) various prompting and whitespace handling. It might (especially in compiled code) or might not warn about the undeclared variable PI. None of this matters to me.
I want to know whether this behavior (modulo toplevel interaction, prompting, and newlinification) is defined by the ANS. If you claim that it is, please cite the ANS section references. I claim that the behavior is either "is an error" or "is undefined".
> Indeed, the "free" use of a variable is orthogonal to the "undeclared" > use of a variable, but that does in itself not mean anything. A Usenet > posting may be factually incorrect, and it may be stupid. But it may > be stupid without being incorrect, and it may be incorrect without > being stupid, and it may be both stupid and incorrect. These two > properties are orthogonal. I know this because I have both read and > writen many Usenet postings over the past 17 years.
Indeed, and anyone who has been using Usenet for that length of time and claims (s)he hasn't made posts of both kinds is a liar.
> Similarly, a reference to a variable may be either or both of "undeclared" > and "free". Rather than muck around in the meaning of these terms, I'd > ask Erik the following focussed question:
> Assume a freshly-booted ANS-compliant CL implementation. What behavior, > if any, is guaranteed by execution of the following form?
> (print (progn (setq pie 22/7) pie))
From the point of view of the argument here, aren't both the print form and the progn forms redundent? Doesn't
* (setq pie 22/7) ==> 22/7 * pie ==> 22/7
illustrate your point just as well?
> I want to know whether this behavior (modulo toplevel interaction, > prompting, and newlinification) is defined by the ANS. If you claim that > it is, please cite the ANS section references. I claim that the behavior > is either "is an error" or "is undefined".
If a form is a symbol that is not a symbol macro, then it is the name of a variable, and the value of that variable is returned. There are three kinds of variables: lexical variables, dynamic variables, and constant variables. A variable can store one object. The main operations on a variable are to read[1] and to write[1] its value.
An error of type unbound-variable should be signaled if an unbound variable is referenced.
So, in your example, setq binds the name 'pie' in the current dynamic environment, by assigning a value to that name in the namespace (all possible names exist (in a platonic sense) in the namespace, it's just that the overwhelming majority of them are never instantiated by being bound). The environment happens to be top-level. The following evaluation of the name pie retrieves the value from the namespace.
Therefore the behaviour you describe is mandated by the hyperspec. I don't have the text of the ANSI specification to hand, but have sufficient trust in the hyperspec editors to believe that if it's mandated by the one, it's mandated by the other.
* "Steven M. Haflich" <hafl...@pacbell.net> | Similarly, a reference to a variable may be either or both of "undeclared" | and "free". Rather than muck around in the meaning of these terms, I'd | ask Erik the following focussed question:
I answered it years ago, to your satisfaction if I recall correctly.
However, both the question and the answer have exactly _nothing_ to do with anything anybody else are or have been discussing. You brought up "undeclared" in attempt to quibble my "free variable" to death, and now you bring up "top-level" because it obviously didn't work to quibble over "undeclared" in the context we were discussing this. I have better things to do. You should go soak your head.
#:Erik -- If this is not what you expected, please alter your expectations.
* Simon Brooke <si...@jasmine.org.uk> | SETQ is defined to bind variables to forms.
Wrong.
| A variable is just any name in the 'variable' namespace
Wrong.
| (Yeuch! LISP2! **Nasty**).
Idiot.
| So, in your example, setq binds the name 'pie' in the current dynamic | environment, by assigning a value to that name in the namespace (all | possible names exist (in a platonic sense) in the namespace, it's just | that the overwhelming majority of them are never instantiated by being | bound). The environment happens to be top-level. The following | evaluation of the name pie retrieves the value from the namespace.
Bogus from A to Z.
| Therefore the behaviour you describe is mandated by the hyperspec.
Wrong. (If it is, it isn't because of your reasoning.)
A more interesting question than Steven M Haflich's stupid quibbling is whether (setq foo 1) is identical to (setf (symbol-value 'foo) 1) if foo is not lexically bound. If it is, then it is completely beside the point whether foo is "declared" or not.
#:Erik -- If this is not what you expected, please alter your expectations.