Jump to content

Retrieving the last consecutive integers of a string


Recommended Posts

Posted (edited)

Hi all,

 

This might be a silly question coming from someone who has quite a bit of experience with AutoLISP, but what's the best way to retrieve the consecutive integers at the end of a string?

 

For example, consider the texts "Test123", or "ComputerNumber_55", or "Puzzle #50-23". How can I extract just "123" from "Test123", "55" from "ComputerNumber_55", and "23" from Puzzle #50-23".

 

(They're only integers, no real numbers like [for instance], "Version 1.2" or "Give me $5.00")

 

I can create a function with like 30 lines or so, but I feel it's wrong and might slow my CAD down considering hundreds of data. Any help is appreciated.

 

Thanks,

Jonathan Handojo

Edited by Jonathan Handojo
Posted (edited)

Here is my attempt.

(defun end:digits ( str / new rtn )
  (and (wcmatch str "*#")
       (setq new "" str (vl-list->string (reverse (vl-string->list str))))
       (while (and (setq rtn (substr str 1 1)) (/= rtn ".")
                   (numberp (read rtn))
                   (setq new (strcat rtn new)
                         str (substr str 2)
                         )
                   )
         )
       )
  (if (/= new "") new)
  )  (vl-load-com)

 

Edited by Tharwat
Posted

My attempt. Returns a string if you want an integer returned uncomment the last line and remove the one above

 

(defun last_int ( str / n)
  (vl-every '(lambda (x) (if (< 47 x 58) (setq n (cons x n)))) (reverse (vl-string->list str)))
  (apply 'strcat (mapcar 'chr n))
  ;(atoi (apply 'strcat (mapcar 'chr n)))
);end_defun

 

Posted (edited)

Ahhh... Of course, how could I've missed vl-string->list. That makes things so much easier. Thanks Tharwat and dlanorh, they both works just fine!

 

With that, I would've just then used vl-some and stop on the first non-numeric character. Again, thanks for the functions both!

 

Thanks,

Jonathan Handojo

Edited by Jonathan Handojo
Posted (edited)

You're welcome anytime.

Another without the use of the vl functions. ;) 

(defun end:digits (str / new itm)
  (and (wcmatch str "*#")
       (while (and str (/= str ""))
         (setq new (cons (substr str 1 1) new)
               str (substr str 2)
         )
       )
       (while (and new (setq itm (car new)) (/= itm ".") (numberp (read itm)))
         (setq str (strcat itm str)
               new (cdr new)
         )
       )
  )
  (if new str)
)

 

Edited by Tharwat
Decimal dot avoided.
Posted

Well, whatever simplest and fastest works best. I suppose the more number of parenthesis, the slower it is. In the end, I combined your use of numberp and read, and dlanorh's idea of vl-every to get:

 

(defun last_int (str / final)
  (vl-some
    '(lambda (x)
       (if (numberp (read x))
	 (progn (setq final (cons x final)) nil)
	 (apply 'strcat final)
	 )
       )
    (reverse (mapcar 'chr (vl-string->list str)))
    )
  )

 

Thanks,

Jonathan Handojo

Posted (edited)
2 hours ago, Jonathan Handojo said:

Well, whatever simplest and fastest works best. I suppose the more number of parenthesis, the slower it is. In the end, I combined your use of numberp and read, and dlanorh's idea of vl-every to get:

 


(defun last_int (str / final)
  (vl-some
    '(lambda (x)
       (if (numberp (read x))
	 (progn (setq final (cons x final)) nil)
	 (apply 'strcat final)
	 )
       )
    (reverse (mapcar 'chr (vl-string->list str)))
    )
  )

 

Thanks,

Jonathan Handojo

I know you said there aren't any reals but (wcmatch x "#") would be a safer check in my opinion:

Try: (read ".")

 

Edited by ronjonp
  • Like 1
Posted (edited)

Ahh, I see. (read ".") yields an error, so texts like "Puzzle v12.00" won't work. Thanks for that ronjonp. Looks like I still have miles to learn about this.

Edited by Jonathan Handojo
Posted
27 minutes ago, Jonathan Handojo said:

Looks like I still have miles to learn about this.

So do I :)

Posted

Two more:

(defun LastInt (s / i q r)
  (if (/= "" s)
    (while 
      (and (> (setq i (strlen s)) 0) (wcmatch (setq q (substr s i 1)) "#"))
      (setq r (cons q r))
      (setq s (substr s 1 (1- i)))
    )
  )
  (if r (atoi (apply 'strcat r)))
)
(defun LastInt (s / r)
  (setq r "")
  (vl-catch-all-apply
    (function 
      (lambda nil
        (foreach x (reverse (vl-string->list s))
          (or
            (and (<= 48 x 57) (setq r (strcat (chr x) r)))
            (exit)
          )
        )
      )
    )
  )
  (if (/= "" r) (atoi r))
)

 

Posted (edited)

Sorry guys Hail the king, only 5 years to late

 

;; Parse Numbers  -  Lee Mac
;; Parses a list of numerical values from a supplied string.
;;  Author: Lee Mac, Copyright © 2013 - www.lee-mac.com       ;;


: (lm:parsenumbers "Puzzle #50-23")
(50 23)

: (lm:parsenumbers "Test123")
(123)

: (lm:parsenumbers "ComputerNumber_55")
(55)

: (lm:parsenumbers "Puzzle v12.00")
(12.0)

 

Credit also Tharwat for the MAC would be a solution.

Edited by BIGAL
Posted

I was talking to myself a few minutes a go, how come bigal did not stamp his finger in this thread also yet? 

Posted

🤣🤣 Oh lord... Good to know, but;

 

11 minutes ago, BIGAL said:

(lm:parsenumbers "Puzzle v12.00")
(12.0)

 

I'd actually want this to result in "00" instead. I'd literally only want the last integers in the string, be it any other symbol than numbers from the end. Otherwise yea... that would've been (last (lm:parsenumbers "Puzzle #50-23"))

 

You certainly got me there mate! Haha.

 

Thanks,

Jonathan Handojo

Posted (edited)

Here's another, recursive variation:

(defun lastint ( s )
    (if (wcmatch s "*#")
        (strcat (lastint (substr s 1 (1- (strlen s)))) (substr s (strlen s)))
        ""
    )
)

@Jonathan Handojo Fewer parentheses doesn't necessarily equate to greater efficiency: for example, the above recursive solution will likely be slower than the other iterative solutions posted in this thread...

Edited by Lee Mac
Posted (edited)

 

Quote

Sorry guys Hail the king, only 5 years to late

 

Lol, the king actually showed up 🤣🤣🤣

 

41 minutes ago, Lee Mac said:

Here's another, recursive variation:


(defun lastint ( s )
    (if (wcmatch s "*#")
        (strcat (lastint (substr s 1 (1- (strlen s)))) (substr s (strlen s)))
        ""
    )
)

@Jonathan Handojo Fewer parentheses doesn't necessarily equate to greater efficiency: for example, the above recursive solution will likely be slower than the other iterative solutions posted in this thread...

 

Makes sense though. Definitely much shorter than all the solutions in this thread to date (in terms of length), but if the text is really long, the function will have to keep being called more times. I totally forgot about recursive functions, and to be honest I'm quite weak at it in using it than iterative methods. I've been trying to use it and create other simple functions. For example:

 

The below will return a list of points between p1 and p2 at a specified interval, with a given option for some threshold at the last segment. I had to go through all sorts of hell to finally get it working. I still need to develop my "recursive" thinking better.

(defun pt_intervals (p1 p2 len threshold)	; Returns a list points between p1 and p2 at intervals of 'len' with a given 'threshold' at the last segment.
  (cond						; It simply means that the last segment should have a minimum length of the given 'threshold'
    ((< (distance p1 p2) (+ len threshold)) (list p2))
    ((>= (distance p1 p2) len)
     (cons (polar p1 (angle p1 p2) len)
	  (pt_intervals (polar p1 (angle p1 p2) len) p2 len threshold)))
    )
  )

 

Thanks,

Jonathan Handojo

Edited by Jonathan Handojo
Posted

Recursive functions have their place and, if mastered, are another useful tool in your toolbox, but ultimately it is about selecting the appropriate tool for the job: writing everything recursively may yield elegant and concise code, but ultimately you'll pay in performance and memory overhead; then again, other tasks (such as traversing nested structures) are ill-suited to iterative methods whereas recursion can tackle them with ease.

 

FWIW, you needn't test the distance twice, and hence your cond statement could be reduced to an if, e.g.:

(defun pt_intervals ( p q l f )
    (if (<= (+ l f) (distance p q))
        (cons (polar p (angle p q) l) (pt_intervals (polar p (angle p q) l) q l f))
        (list q)
    )
)

 

Posted
1 hour ago, Jonathan Handojo said:

I totally forgot about recursive functions, and to be honest I'm quite weak at it in using it than iterative methods.

 

Wheres iteration theres also a linear recursion and vice-versa.

 

BTW during my participation in the LISP forums Lee's proven to be the master of recursions (and not just them but also of different insane list manipulations).

 

Posted

Johnathon there is some lisp for pad zeros out there prefix or suffix.

D1 no D01

V12.0000 how many after . 12.0 12.00 12.000

Posted
10 hours ago, BIGAL said:

Johnathon there is some lisp for pad zeros out there prefix or suffix.

D1 no D01

V12.0000 how many after . 12.0 12.00 12.000

 

If you mean in terms of string, I think that's easy to implement. But if it's in the form of a real number, I don't know how to get that. Even Lee Mac's round (LM;:roundto 1.2 4) returns only 1.2, not 1.2000. 

Posted (edited)

1.2 = 1.20 = 1.200 = 1.20000 no matter how  many decimal places. Yes may need to be string just a fact of life, there is some #.## funtions but never used them.

Edited by BIGAL

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Unfortunately, your content contains terms that we do not allow. Please edit your content to remove the highlighted words below.
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...