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

extract numeric from multiple lines

50 views
Skip to first unread message

Jinsong Zhao

unread,
Apr 9, 2014, 5:07:20 AM4/9/14
to
Hi there,

I hope to extract TWO numeric side by side from the following lines when
a position provided.

two situation should be considered. a) the TWO numbers are in one line.
It's simple; b) One is at the end of a line, and the other is at the
beginning of next line. For example, if position is set to 6, then
-12.46277 and -11.13900 are the result.

-38.18381 -26.49595 -18.00396 -15.52326 -15.51745 -12.46277
-11.13900 3.50872 3.89552 4.22668 4.38951 5.64658
1.90757 0.94853 2.31361 2.76930 2.31454 0.00000

I write some code, but it's dirty and ugly. I can't find a elegant way.

(defun get-num (line)
(with-input-from-string (is line)
(loop :for num := (read is nil nil)
:while num
:collect num )))


(defun get-numeric (path position)
(with-open-file (stream path)
(let ((a)
(b)
(nln (floor position 6))
(pos (mod position 6)))
(if
(and (> nln 0) (= pos 0))
(decf nln))
(if
(> nln 0)
(loop :repeat nln
:do (read-line stream)))
(cond ((= pos 0) (setf a (elt (get-num (read-line stream)) 5))
(setf b (elt (get-num (read-line stream)) 0)))
(t (let ((line (read-line stream)))
(setf a (elt (get-num line) (1- pos)))
(setf b (elt (get-num line) pos))))))))

Any suggestions? Thanks!

Best wishes,
Jinsong

Dimitri Fontaine

unread,
Apr 9, 2014, 8:21:22 AM4/9/14
to
Jinsong Zhao <jsz...@yeah.net> writes:
> two situation should be considered. a) the TWO numbers are in one line. It's
> simple; b) One is at the end of a line, and the other is at the beginning of
> next line. For example, if position is set to 6, then -12.46277 and
> -11.13900 are the result.
>
> -38.18381 -26.49595 -18.00396 -15.52326 -15.51745 -12.46277
> -11.13900 3.50872 3.89552 4.22668 4.38951 5.64658
> 1.90757 0.94853 2.31361 2.76930 2.31454 0.00000

I feel so lazy today:

CL-USER> (let* ((input " -38.18381 -26.49595 -18.00396 -15.52326 -15.51745 -12.46277
-11.13900 3.50872 3.89552 4.22668 4.38951 5.64658
1.90757 0.94853 2.31361 2.76930 2.31454 0.00000
")
(vector (read-from-string (format nil "#(~a)"input))))
(list (aref vector 5) (aref vector 6)))
(-12.46277 -11.139)

Regards,
--
dim

Jinsong Zhao

unread,
Apr 9, 2014, 11:33:10 AM4/9/14
to
Thanks a lot for the lazy method. However, I have to read the necessary
lines from file. I don't know how to join all the read-in lines together.

Regards,
Jinsong

Dimitri Fontaine

unread,
Apr 9, 2014, 12:20:09 PM4/9/14
to
Jinsong Zhao <jsz...@yeah.net> writes:
> Thanks a lot for the lazy method. However, I have to read the necessary
> lines from file. I don't know how to join all the read-in lines together.

Transform each line to a vector and keep counting elements separately,
it should be easy enough right?

Damn. So here's the code I came up with, for loop lovers:

(defpackage #:jszhao
(:use cl))

(in-package #:jszhao)

(defparameter *contents* "
-38.18381 -26.49595 -18.00396 -15.52326 -15.51745 -12.46277
-11.13900 3.50872 3.89552 4.22668 4.38951 5.64658
1.90757 0.94853 2.31361 2.76930 2.31454 0.00000
")

(defun read-line-into-vector (line)
"Parse a LINE string containing whitespace separated numbers as a vector
of numbers, and return that number."
(let ((clean-line (string-trim '(#\Space #\Return #\Tab) line)))
(when clean-line
(read-from-string (format nil "#(~a)" clean-line)))))

(defun aref-offset (vector index offset)
"Consider that the given VECTOR actually starts at OFFSET rather than 0
and return its value at this INDEX."
(aref vector (- index offset)))

(defun read-pair-from (content index &aux (target (- index 1)))
"CONTENT is expected to be a string maybe spanning more than one line and
containing whitespace separated numbers. Read numbers at indexes INDEX
and INDEX+1, considering that INDEX should start at 1."
(with-input-from-string (s content)
(loop
:for start := 0 :then (+ start len)
:for line := (read-line s nil nil)
:while line
:for vector := (read-line-into-vector line)
:for len := (length vector)

:when (<= start target (+ start len -1))
:collect (aref-offset vector target start)

:when (<= start (+ target 1) (+ start len -1))
:collect (aref-offset vector (+ target 1) start))))

JSZHAO> (read-pair-from *contents* 6)
(-12.46277 -11.139)

HTH,
--
dim

Raymond Wiker

unread,
Apr 9, 2014, 1:20:25 PM4/9/14
to
You don't actually need to distinguish the two cases, as long as you use
read (or read-from-string) --- anyway, no need to use read-line:

(defun extract-numeric (s i)
(let ((*read-eval* nil))
(loop for index from 0 upto i
for prev = nil then curr
for curr = (read s nil)
finally return (list prev curr))))

(with-input-from-string (s *sample-input*)
(extract-numeric s 6))

(defun get-numeric (path i)
(with-open-file (s path :direction :input)
(extract-numeric s i)))

Jinsong Zhao

unread,
Apr 10, 2014, 10:26:00 PM4/10/14
to
On 2014/4/9 9:20, Dimitri Fontaine wrote:
> Jinsong Zhao <jsz...@yeah.net> writes:
>> Thanks a lot for the lazy method. However, I have to read the necessary
>> lines from file. I don't know how to join all the read-in lines together.
>
> Transform each line to a vector and keep counting elements separately,
> it should be easy enough right?
>

Yes, it easy enough. In fact, in the final code, I used your method.

I'm not a loop lover. I just don't know how to do that using mapping
mechanism.

> Damn. So here's the code I came up with, for loop lovers:
>
> (defpackage #:jszhao
> (:use cl))
>
> (in-package #:jszhao)
>
> (defparameter *contents* "
> -38.18381 -26.49595 -18.00396 -15.52326 -15.51745 -12.46277
> -11.13900 3.50872 3.89552 4.22668 4.38951 5.64658
> 1.90757 0.94853 2.31361 2.76930 2.31454 0.00000
> ")
>

I the previous post, I wanted to say the above lines is saved in a file,
I should read it out. Thus I have to read each line out by a loop (or
something else??). So I have to joint the lines together, and then use
your method to deal with it.
Thanks a lot.

Regards,
Jinsong

P.S., sorry to send the reply to your mailbox.

Jinsong Zhao

unread,
Apr 10, 2014, 10:35:34 PM4/10/14
to
Thank you very much. It works. And it's much easy to understand the code.

Regards,
Jinsong

Pascal J. Bourguignon

unread,
Apr 11, 2014, 3:09:05 AM4/11/14
to
Jinsong Zhao <jsz...@yeah.net> writes:

> On 2014/4/9 9:20, Dimitri Fontaine wrote:
>> Jinsong Zhao <jsz...@yeah.net> writes:
>>> Thanks a lot for the lazy method. However, I have to read the necessary
>>> lines from file. I don't know how to join all the read-in lines together.
>>
>> Transform each line to a vector and keep counting elements separately,
>> it should be easy enough right?
>>
>
> Yes, it easy enough. In fact, in the final code, I used your method.
>
> I'm not a loop lover. I just don't know how to do that using mapping
> mechanism.
>
>> Damn. So here's the code I came up with, for loop lovers:
>>
>> (defpackage #:jszhao
>> (:use cl))
>>
>> (in-package #:jszhao)
>>
>> (defparameter *contents* "
>> -38.18381 -26.49595 -18.00396 -15.52326 -15.51745 -12.46277
>> -11.13900 3.50872 3.89552 4.22668 4.38951 5.64658
>> 1.90757 0.94853 2.31361 2.76930 2.31454 0.00000
>> ")
>>
>
> I the previous post, I wanted to say the above lines is saved in a
> file, I should read it out. Thus I have to read each line out by a
> loop (or something else??). So I have to joint the lines together, and
> then use your method to deal with it.


>> (defun read-pair-from (content index &aux (target (- index 1)))
>> "CONTENT is expected to be a string maybe spanning more than one line and
>> containing whitespace separated numbers. Read numbers at indexes INDEX
>> and INDEX+1, considering that INDEX should start at 1."
>> (with-input-from-string (s content)
>> (loop
>> :for start := 0 :then (+ start len)
>> :for line := (read-line s nil nil)
>> :while line
>> :for vector := (read-line-into-vector line)
>> :for len := (length vector)
>>
>> :when (<= start target (+ start len -1))
>> :collect (aref-offset vector target start)
>>
>> :when (<= start (+ target 1) (+ start len -1))
>> :collect (aref-offset vector (+ target 1) start))))

1- notice that CL I/O functions are still used. Instead of reading
from the string you can read directly from a file if you wish so.

2- in any case a function should only perform one task. Opening a file
(or a string) and parsing a file are TWO different functions. This
read-pair-from needs refactoring anyways.

3- in any case, you should not use random code without understanding
what it does. For example, there's a bug in the following function:

>> (defun read-line-into-vector (line)
>> "Parse a LINE string containing whitespace separated numbers as a vector
>> of numbers, and return that number."
>> (let ((clean-line (string-trim '(#\Space #\Return #\Tab) line)))
>> (when clean-line
>> (read-from-string (format nil "#(~a)" clean-line)))))

3- and since it doesn't perform any I/O it shouldn't be named READ-…
anyways.



>> (defun aref-offset (vector index offset)
>> "Consider that the given VECTOR actually starts at OFFSET rather than 0
>> and return its value at this INDEX."
>> (aref vector (- index offset)))






(defun parse-line-into-vector (line)
"Parse a LINE string containing whitespace separated numbers as a vector
of numbers, and return that vector of numbers."
(let ((clean-line (string-trim '(#\Space #\Return #\Tab) line)))
(when (and clean-line (plusp (length line)))
(read-from-string (format nil "#(~a)" clean-line)))))

(defun aref-offset (vector index offset)
"Consider that the given VECTOR actually starts at OFFSET rather than 0
and return its value at this INDEX."
(aref vector (- index offset)))

(defun read-pair-from (stream index &aux (target (- index 1)))
"CONTENT is expected to be a string maybe spanning more than one line and
containing whitespace separated numbers. Read numbers at indexes INDEX
and INDEX+1, considering that INDEX should start at 1."
(loop
:for start := 0 :then (+ start len)
:for line := (read-line stream nil nil)
:while line
:for vector := (parse-line-into-vector line)
:for len := (length vector)

:when (<= start target (+ start len -1))
:collect (aref-offset vector target start)

:when (<= start (+ target 1) (+ start len -1))
:collect (aref-offset vector (+ target 1) start)))


(with-input-from-string (stream *contents*)
(read-pair-from stream 6))
--> (-12.46277 -11.139)

(with-open-file (stream "/tmp/contents")
(read-pair-from stream 6))
--> (-12.46277 -11.139)

>>
>> JSZHAO> (read-pair-from *contents* 6)
>> (-12.46277 -11.139)
>>
>> HTH,

--
__Pascal Bourguignon__
http://www.informatimago.com/
"Le mercure monte ? C'est le moment d'acheter !"

Dimitri Fontaine

unread,
Apr 14, 2014, 6:14:58 AM4/14/14
to
"Pascal J. Bourguignon" <p...@informatimago.com> writes:
> 1- notice that CL I/O functions are still used. Instead of reading
> from the string you can read directly from a file if you wish so.

Yes, my point exactly, the code was written so that reading a file
rather than a string was an easy exercise…

> 2- in any case a function should only perform one task. Opening a file
> (or a string) and parsing a file are TWO different functions. This
> read-pair-from needs refactoring anyways.

Point taken, using a stream in the API is cleaner indeed. Thanks.

> (with-input-from-string (stream *contents*)
> (read-pair-from stream 6))
> --> (-12.46277 -11.139)
>
> (with-open-file (stream "/tmp/contents")
> (read-pair-from stream 6))
> --> (-12.46277 -11.139)

Having the input handling within the read-pair-from function wasn't bright.
--
dim
0 new messages