Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

File I/O & String conversion - CLISP

4 views
Skip to first unread message

Carla

unread,
Oct 20, 2008, 8:23:09 PM10/20/08
to
Hi.
I have a txt file containing a combination of D/./*/F/1 characters. In
the example below, the first row has 5 elements D, ., *, ., F and
second row also has 5 elements ., ., ., ., .

D.*.F
..1..

I have to read this data line by line, and convert it into list of
lists, of individual strings.

(("D" "." *" "." "F") ("." "." "1" "." "."))

Can someone help me out with this please?

Rob Warnock

unread,
Oct 20, 2008, 10:05:53 PM10/20/08
to
Carla <crashove...@gmail.com> wrote:
+---------------

| D.*.F
| ..1..
|
| I have to read this data line by line, and convert it into list of
| lists, of individual strings.
|
| (("D" "." *" "." "F") ("." "." "1" "." "."))
+---------------

LOOP, READ-LINE, and STRING are your friends. It's almost a "one-liner".

[O.k., depending on how fanatical you are about formatting, it's
actually a 2- or 3-liner. But it still took less than 40 seconds
by the stopwatch to write & test a solution that works with your
sample input data set.]

Note: It's considered polite in this list to first show us us your
best efforts so far [the code you wrote], and the problems you have
experienced [the exact error messages]. rather than just starting off
with "Do my homework for me!".


-Rob

-----
Rob Warnock <rp...@rpw3.org>
627 26th Avenue <URL:http://rpw3.org/>
San Mateo, CA 94403 (650)572-2607

Carla

unread,
Oct 20, 2008, 11:29:15 PM10/20/08
to
I am trying with this version of my function.

(let ((in (open "file1.txt" :if-does-not-exist nil))
(main-list nil))
(when in
(loop for line = (read-line in nil)
while line do (let ((line-string line)
(string-form nil))
(loop for i from 1 to (length line-string)
do
(push (subseq line-string (1- i) i) string-form))
(push string-form main-list)))
(close in)) (return main-list))

But this is just returning true, even after I am explicitly returning
main-list before the end of let block.

Jason

unread,
Oct 21, 2008, 12:24:56 AM10/21/08
to

I changed the (return main-list) to simply return-list, and prefaced
your push statements with nreverse, and the code works as expected.

(let ((in (open "file1.txt" :if-does-not-exist nil))
(main-list nil))
(when in
(loop for line = (read-line in nil)
while line do (let ((line-string line)
(string-form nil))
(loop for i from 1 to (length line-string)
do
(push (subseq line-string (1- i) i)
string-form))

(push (nreverse string-form) main-list)))
(close in))
(nreverse main-list))

-Jason

Rob Warnock

unread,
Oct 21, 2008, 5:09:10 AM10/21/08
to
Jason <jem...@gmail.com> wrote:
+---------------

| Carla <crashoverride...@gmail.com> wrote:
| > I am trying with this version of my function.
...[trimmed]...

| > But this is just returning true, even after I am explicitly returning
| > main-list before the end of let block.
|
| I changed the (return main-list) to simply return-list, and prefaced
| your push statements with nreverse, and the code works as expected.
|
| (let ((in (open "file1.txt" :if-does-not-exist nil))
| (main-list nil))
| (when in
| (loop for line = (read-line in nil)
| while line do (let ((line-string line)
| (string-form nil))
| (loop for i from 1 to (length line-string)
| do
| (push (subseq line-string (1- i) i)
| string-form))
| (push (nreverse string-form) main-list)))
| (close in))
| (nreverse main-list))
+---------------

Carla, now that Jason has debugged the proximate problem, we can
start working on style and using common CL idioms, especially
within the LOOPs.

First, there's a standard CL macro that takes care of opening and
closing a file for you, so instead of:

(let ((in (open "file1.txt" :if-does-not-exist nil)))
(let ((main-list nil))
(when in
...{body which PUSHes onto MAIN-LIST}...)
(close in))
main-list)

you can write this:

(with-open-file (in "file1.txt" :if-does-not-exist nil)
(let ((main-list nil))
...{body}...
main-list))

Next, you can eliminate the LINE-STRING variable -- just use LINE,
it's still in scope.

Your LOOPs just *cry* out for using the COLLECT feature of the
LOOP macro instead of setting up results variables (MAIN-LIST,
STRING-FORM) and PUSH'ing stuff onto them. Plus, COLLECT keeps
things in forward order so you don't have to REVERSE (or NREVERSE)
them. First let's do the outer LOOP:

(with-open-file (in "file1.txt" :if-does-not-exist nil)


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

collect (let ((string-form nil))
(loop for i from 1 to (length line) do
(push (subseq line (1- i) i) string-form))
string-form)))

and now the inner:

(with-open-file (in "file1.txt" :if-does-not-exist nil)


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

collect (loop for i from 1 to (length line)
collect (subseq line (1- i) i))))

Finally, you can use the FOR...ACROSS array scanning feature
of LOOP to walk each LINE character by character, with STRING
to turn each character back into a string:

(with-open-file (in "file1.txt" :if-does-not-exist nil)


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

collect (loop for c across line collect (string c))))

And there's the 3-line solution I mentioned before
[well, 4 lines, with the addition of the WITH-OPEN-FILE].


-Rob

p.s. People differ it how much they like to compress vertical
whitespace. You can crunch the above to 3 lines this way:

(with-open-file (in "file1.txt" :if-does-not-exist nil)


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

collect (loop for c across line collect (string c))))

though many people prefer a looser layout that's more easily
parsable by eye [and looks like the other LOOPs you're likely
to find in CL code]:

(with-open-file (in "file1.txt" :if-does-not-exist nil)


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

collect (loop for c across line
collect (string c))))

Dimiter "malkia" Stanev

unread,
Oct 21, 2008, 1:56:48 PM10/21/08
to
Thanks, Rob!

That was a good lesson in refactoring for me, and I'm sure Carla would
appreciate it too!

Rob Warnock wrote:
> Jason <jem...@gmail.com> wrote:
> +---------------
> | Carla <crashoverride...@gmail.com> wrote:
> | > I am trying with this version of my function.

> ....[trimmed]...

Stelian Ionescu

unread,
Oct 21, 2008, 8:41:40 PM10/21/08
to
On Tue, 21 Oct 2008 04:09:10 -0500, Rob Warnock wrote:
[snip]

> p.s. People differ it how much they like to compress vertical
> whitespace. You can crunch the above to 3 lines this way:
>
> (with-open-file (in "file1.txt" :if-does-not-exist nil)
> (loop for line = (read-line in nil) while line
> collect (loop for c across line collect (string c))))

or even simpler:

(with-open-file (in "file1.txt" :if-does-not-exist nil)
(loop for line = (read-line in nil) while line

collect (map 'list #'string line)))

--
Stelian Ionescu a.k.a. fe[nl]ix
Quidquid latine dictum sit, altum videtur.

William James

unread,
Oct 22, 2008, 4:01:06 PM10/22/08
to

Ruby:

IO.readlines("junk0").map{|line| line.strip.split("") }

Marco Antoniotti

unread,
Oct 22, 2008, 5:51:10 PM10/22/08
to

(defun process-file (file)
(mapcar (lambda (line) (split-sequence line)) (readlines file)))

(compile 'process-file)

Now, which part of the Ruby code is not rendered in CL?

Cheers
--
Marco

Pascal Bourguignon

unread,
Oct 22, 2008, 6:03:41 PM10/22/08
to

No, which part of CL code is not rendered in Ruby?

;-)


--
__Pascal Bourguignon__
http://www.informatimago.com

Rob Warnock

unread,
Oct 23, 2008, 6:38:15 AM10/23/08
to
Pascal Bourguignon <p...@informatimago.com> wrote:
+---------------
+---------------

COMPILE, of course! ;-}


-Rob

0 new messages