Author Topic: 'ReplaceText' lisp errors on first go ...  (Read 6233 times)

0 Members and 1 Guest are viewing this topic.

Hangman

  • Swamp Rat
  • Posts: 566
'ReplaceText' lisp errors on first go ...
« on: July 22, 2010, 04:59:16 PM »
Afternoon everyone.

I am wondering if you gent's here would be good enough to help me troubleshoot a piece of code.
I have been working on this (troubleshooting it) for quite some time, learning VLIDE as I go.  But I can't find my problem.
I know I'm not using VLIDE correctly, so I have to go restudy the help files and such.

But the code I have is interesting in it's error.
The first time it runs, it errors with:
Quote
Application Error: 0 :- bad argument type: numberp: nil
The second time, or if I edit the code with something simple like a comment, save it and reload it, then it works without a hitch.
Here's the code:
Code: [Select]
(defun C:ReplaceText (/ *error* NewTLen             ; Local
                        OldText NewText OldTLen SearchStr NewTLen AllText) ; Global
  (defun *error* (msg /)
    (on-error msg)
    (princ)
  ) ; _EoF
  (setvar "cmdecho" 0)
  (prompt "\nUse quotes for multiple words.")
  (setq OldText (strcase (getstring "\nEnter text being replaced (Press 'Enter' for entire string): "))
        NewText (strcase (getstring "\nEnter new text to replace existing: ")))
  (if (or (= OldText nil) (= OldText ""))           ; if OldText is empty
    (setq OldText "String")                         ; Then replace entire string
    (setq OldTLen (strlen OldText)                  ; Else get Length of OldText
          SearchStr (strcat "*" OldText "*"))       ; create search string
  ) ; _if
  (if (or (= NewText nil) (= NewText ""))           ; if NewText IS empty
    (quit)
    (setq NewTLen (strlen NewText))                 ; Else get Length of NewText
  )
  (if (= OldText "String")                          ; if replacing entire string
    (SelectEachString)                              ; select strings to be replaced
    (SelectAllStrings)                              ; Else optional to select ALL text
  ) ; _if
  (setvar "cmdecho" 1)
  (princ)
) ; _EoF
;
(defun SelectEachString (/ )
  (while (= AllText nil)
    (prompt "\nSelect text to be replaced: ")
    (setq AllText (ssget '((0 . "TEXT,MTEXT"))))    ; ,MULTILEADER
  )
  (ProcessText)
)
;
(defun SelectAllStrings (/ )
  (prompt "\nSelect text to be replaced (Press 'Enter' for all text in drawing): ")
  (setq AllText (ssget '((0 . "TEXT,MTEXT"))))
  (if (or (= AllText nil) (= AllText ""))
    (setq AllText (ssget "_X" '((0 . "TEXT,MTEXT")))); ,MULTILEADER
  )
  (ProcessText)
)
;
(defun ProcessText (/ i TextObj TextStr eList Posi TextChar PreChar PostChar UPDeList)
  (setq i -1)
;  (prompt "\n... 1 ...")
  (while (setq TextObj (ssname AllText (setq i (1+ i))))
    (if (setq TextStr (cdr (assoc 1 (setq eList (entget TextObj)))))
      (progn
;        (prompt "\n... 2 ...")
        (if (= OldText "String")                    ; if replacing entire string
          (setq UPDeList 1 TextStr NewText)         ;
          (progn                                    ; Else
;            (prompt "\nrunning Else ...")
            (if (wcmatch TextStr SearchStr)         ; if SearchStr is found in TextStr
              (progn
                (setq TextLen (strlen TextStr)      ; get length of TextStr
                      Posi 1
                      TextChar (substr TextStr Posi OldTLen) ; set TextChar to first SearchStr
                )                                            ; characters of TextStr
                (while (and (/= TextChar OldText) (>= TextLen Posi)) ; while TextChar does not
                  (setq Posi (1+ Posi)                          ; match, move position up 1
                        TextChar (substr TextStr Posi OldTLen)) ; and try again
                )                                           ; when characters match, continue
                (if (> Posi 1)
                  (setq PreChar (substr TextStr 1 (1- Posi))) ; get everything before SearchStr
                  (setq PreChar "")                           ; Else there is nothing
                )
                (setq UPDeList 1
                      PostChar (substr TextStr (+ Posi OldTLen)) ; get everything after SearchStr
                      TextStr (strcat PreChar NewText PostChar)  ; create new string
                )
              ) ; _progn
            ) ; _if
          ) ; _progn
        ) ; _if
      ) ; _progn
    ) ; _if
    (if (= UPDeList 1)                              ; if there's a new string to be updated,
      (progn                                        ; Then update eList
        (entmod (subst (cons 1 TextStr) (assoc 1 eList) eList))
        (setq UPDeList 0)
      ) ; _progn
    ) ; _if
  ) ; _while
); _EoF
 < ... error catch ... >

I have a few (setq ... functions that are multiples, and I've checked each one and they have a value to them when the program finishes.
Also, I believe it is erroring in the main function area, before it goes to any other function (I have several prompt's that are commented out (I thought I had found the problem but alas, it still errors) that I have used to narrow down where the error is occuring).

Thanks.
Hangman  8)

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Drafting Board, Mechanical Arm, KOH-I-NOOR 0.7mm
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Lee Mac

  • Seagull
  • Posts: 12914
  • London, England
Re: 'ReplaceText' lisp errors on first go ...
« Reply #1 on: July 22, 2010, 05:03:17 PM »
Haven't looked at the code as yet, but Replace Text is a nasty subject area to get into as you run into all sorts of problems (mostly with MText formatting codes)...

Not trying to discourage you, but believe me, I've been through it..

Lee Mac

  • Seagull
  • Posts: 12914
  • London, England
Re: 'ReplaceText' lisp errors on first go ...
« Reply #2 on: July 22, 2010, 05:31:17 PM »
Talking of replacing text, perhaps one of the simplest ways would be something like this:

Code: [Select]
(defun StringSubst ( new old str )
  ;; © Lee Mac 2010
  (
    (lambda ( i / nl ) (setq nl (strlen new))
      (while
        (and (< i (strlen str))
          (setq i (vl-string-search old str i))
          (setq str (vl-string-subst new old str i) i (+ i nl))
        )
      )
      str
    )
    0
  )
)

JohnK

  • Administrator
  • Seagull
  • Posts: 10637
Re: 'ReplaceText' lisp errors on first go ...
« Reply #3 on: July 22, 2010, 05:49:57 PM »
Please do not take offence to this but I have at least 8 comments on the first function alone and i only glanced at it (didnt even break the skin of the functions and their use so-to-speak). But in an effort to help teach, here is my first pass at the function.

Again, please, no offence. Just trying to help give direction.

Code: [Select]
(defun C:ReplaceText (/ *error* NewTLen             ; Local
                        OldText NewText OldTLen SearchStr NewTLen AllText) ; Global
[color=red]                 ;; 7: there isnt anything GLOBAL about the localization "( / <blah> )" process so remove that comment.
[/color]  (defun *error* (msg /)
    (on-error msg)
    (princ)
  ) ; _EoF
               [color=red] ;; 7: what is "on-error"? ...either use "*error*" or "on-error"
                ;; 7: this is not the END OF FILE (that tag is typical used to mean END OF FILE not END OF FUNCTION)
                ;;    use the typical syntax instead.  Like: "...);_ end defun"[/color]
  (setvar "cmdecho" 0)
[color=red]                ;; 7: you're not even going to save the users current settings?
[/color]
  (prompt "\nUse quotes for multiple words.")
  (setq OldText (strcase (getstring "\nEnter text being replaced (Press 'Enter' for entire string): "))
        NewText (strcase (getstring "\nEnter new text to replace existing: ")))

  (if (or (= OldText nil) (= OldText ""))           ; if OldText is empty
[color=red]                ;; 7: (or (null Oldtext) ...?
[/color]    (setq OldText "String")                         ; Then replace entire string
    (setq OldTLen (strlen OldText)                  ; Else get Length of OldText
          SearchStr (strcat "*" OldText "*"))       ; create search string
  ) ; _if

  (if (or (= NewText nil) (= NewText ""))           ; if NewText IS empty
[color=red]                ;; 7: (or (null NewText) ...?
[/color]    (quit)
[color=red]                ;; 7: You have already altered the users CMDECHO var, are you setting it back?
                ;;    try and think of a way to NOT use this method.[/color]

    (setq NewTLen (strlen NewText))                 ; Else get Length of NewText
  )
  (if (= OldText "String")                          ; if replacing entire string
    (SelectEachString)                              ; select strings to be replaced
    (SelectAllStrings)                              ; Else optional to select ALL text
  ) ; _if
  (setvar "cmdecho" 1)
  (princ)
) ; _EoF
[color=red]                ;; 7: see above[/color].

TheSwamp.org (serving the CAD community since 2003)
Member location map - Add yourself

Donate to TheSwamp.org

CAB

  • Global Moderator
  • Seagull
  • Posts: 10401
Re: 'ReplaceText' lisp errors on first go ...
« Reply #4 on: July 22, 2010, 06:40:42 PM »
Hangman,
FYI, First thing you do when debugging is to disable the local error handler.

I ran the routine with out error but it did not replace the text. :-o
« Last Edit: July 22, 2010, 06:52:44 PM by CAB »
I've reached the age where the happy hour is a nap. (°¿°)
Windows 10 core i7 4790k 4Ghz 32GB GTX 970
Please support this web site.

CAB

  • Global Moderator
  • Seagull
  • Posts: 10401
Re: 'ReplaceText' lisp errors on first go ...
« Reply #5 on: July 22, 2010, 06:58:50 PM »
Looking at your code I did not see a clear picture of what you are trying to do with (if (= OldText "")

What does your pseudo code look like? 8-)
I've reached the age where the happy hour is a nap. (°¿°)
Windows 10 core i7 4790k 4Ghz 32GB GTX 970
Please support this web site.

Hangman

  • Swamp Rat
  • Posts: 566
Re: 'ReplaceText' lisp errors on first go ...
« Reply #6 on: July 23, 2010, 11:10:54 AM »
Please do not take offence to this < ... >.  But in an effort to help teach, here is my first pass at the function.

Again, please, no offence. Just trying to help give direction.

No offense taken Se7en, I really appreciate the constructive feedback.  This is where I can really learn, I thank you.  If you don't mind, I'll try to explain my actions & thoughts here as well.  Please correct me where I may be incorrect as I would really like to get some good programming practices in my work instead of the bad habits we can easily inherit.

Quote from: Se7en
Code: [Select]
(defun C:ReplaceText (/ *error* NewTLen             ; Local
                        OldText NewText OldTLen SearchStr NewTLen AllText) ; Global
[color=red]                 ;; 7: there isnt anything GLOBAL about the localization "( / <blah> )" process so remove that comment.
[/color]
Perhaps I am not yet understanding the difference between the Local and Global variables yet.  These variables, although they are used by this main function, are also used in the other functions.  As this is where the program begins, I figured this is where it would be best to declare the variables.  I know they can't be declared in the other functions as they will be rendered useless in this function.  That is what I was understanding Global to be.  I know declaring them here makes them Local, but they are (to my limited understanding) Global in that they are accessed by the other functions in this routine.

Quote from: Se7en
Code: [Select]
  (defun *error* (msg /)
    (on-error msg)
    (princ)
  ) ; _EoF
               [color=red] ;; 7: what is "on-error"? ...either use "*error*" or "on-error"
                ;; 7: this is not the END OF FILE (that tag is typical used to mean END OF FILE not END OF FUNCTION)
                ;;    use the typical syntax instead.  Like: "...);_ end defun"[/color]
Oops.  I apologize, I forgot I needed the 'on-error' to fully understand it all.  Here it is.
Code: [Select]
(defun on-error (msg / )                            ; borrowed from Kerry - theSwamp
  (while
    (> (getvar "cmdactive") 0)
    (command)
  )
  (setvar "cmdecho" 1)
  (cond ( (not msg))                                ; no error, do nothing
        ( (member
            (strcase msg t)                         ; invoke a cancel
             '("console break" "function cancelled" "quit / exit abort")
          )
          (prompt "\nFunction Cancelled\nFunction Cancelled")
        )
        ( (princ (strcat "\nApplication Error: "    ; else, an error message
                         (itoa (getvar "errno"))
                         " :- "
                         msg "\n"
                 )
          )
        )
  )
  (setq *error* nil)
)
... ; EoF = End of File ...  Got it, thanks.

Quote from: Se7en
Code: [Select]
  (setvar "cmdecho" 0)
[color=red]                ;; 7: you're not even going to save the users current settings?
[/color]
Well umm, ... no I wasn't.  Most of the users don't even read their command line yet, I'm hoping to get them trained to look at it.  I want to force the echo on at the end but I don't see a reason for the echo to be on during the function, so I'm hard coding it in.  I did forget about my little (quit) if there's no text though.   :oops:  I need to fix that.

Quote from: Se7en
Code: [Select]
  (prompt "\nUse quotes for multiple words.")
  (setq OldText (strcase (getstring "\nEnter text being replaced (Press 'Enter' for entire string): "))
        NewText (strcase (getstring "\nEnter new text to replace existing: ")))

  (if (or (= OldText nil) (= OldText ""))           ; if OldText is empty
[color=red]                ;; 7: (or (null Oldtext) ...?
[/color]    (setq OldText "String")                         ; Then replace entire string
    (setq OldTLen (strlen OldText)                  ; Else get Length of OldText
          SearchStr (strcat "*" OldText "*"))       ; create search string
  ) ; _if

  (if (or (= NewText nil) (= NewText ""))           ; if NewText IS empty
[color=red]                ;; 7: (or (null NewText) ...?
[/color]    (quit)
[color=red]                ;; 7: You have already altered the users CMDECHO var, are you setting it back?
                ;;    try and think of a way to NOT use this method.[/color]
Null ...  I forgot about that.  Null does help, I don't like the way I currently have it.
You are correct, (quit) does not work like this.  Instead of using (quit), I can simply send it to the 'on-error' to clean everything up, yes?.

Thank you again Se7en for your eye's & constructive feedback.


Hangman,
FYI, First thing you do when debugging is to disable the local error handler.

I ran the routine with out error but it did not replace the text. :-o
Correct CAB, that's why I came here.  I had it disabled early on.  I also had all the declared variables commented out so I could go through them and see if any one was not being used as they should.  As it does run without the error handler enabled, I re-enabled it and put a bunch of prompts in there to see how far it was going before erroring as well.  Like I mentioned earlier, I was using VLIDE too, but I don't think I was using it correctly.  I need to go re-read the help files on it.

Looking at your code I did not see a clear picture of what you are trying to do with (if (= OldText "")

What does your pseudo code look like? 8-)
Well, ...  I don't actually have any pseudo code for that part.  It was a random thought as I was putting this together and I just ... threw it in there.  Basically all it does is;  If there is no specific text to be replaced, it replaces every piece of text string selected.  So the function forces a specific selection rather than the global (ssget "_X" ... ) selection.


I thank you guys for your help and your instruction.  I love coming to the Swamp just because of people like you guys here.  Thank you.
Hangman  8)

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Drafting Board, Mechanical Arm, KOH-I-NOOR 0.7mm
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

psuchewinner

  • Guest
Re: 'ReplaceText' lisp errors on first go ...
« Reply #7 on: July 23, 2010, 11:21:02 AM »
Speaking of MText...Is there a "Change Text" routine for MText entities?

JohnK

  • Administrator
  • Seagull
  • Posts: 10637
Re: 'ReplaceText' lisp errors on first go ...
« Reply #8 on: July 23, 2010, 12:02:15 PM »
Lets tackle this one at a time.

First lets clean up some your use of terms.

As far as variables go there are two things you can do (Im speaking in general programing terms here, not just AutoLisp).

In most programming languages like C/C++ you can declare and define variables you want to use in two steps:
step 1. declare
step 2. define

Declare and Define:

You can declare a variable, that is that you can tell the computer you are going to use a variable with this name.

you can define a variable this is where you tell the computer what to "put into" that variable.

C/C++ variable declare/define example:
Code: [Select]
// declare variables

int myVar;
int myOtherVar;

// define variables

myVar = 1;
myOtherVar = 2;


Now AutoLisp being a high level language doesn't actually let you separate the declaration from the definition so when you see a variable being used in AutoLisp you often see this:
Code: [Select]
(setq myVar 1)that would be Declaration and Definition all in one statement.

I know what your thinking: If we were to try and separate the declaration and definition process in AutoLisp (i.e. be more like C/C++) we could try something like this:

Code: [Select]
(setq myVar '())
(setq myVar 1)

but since the first statement actually "destroys" the variable called myVar it only causes confusion to other readers when you do this.

Now we will touch on the global/local variable problem.

In Lisp we have this syntax to use: ( / ) {open paren, slash, close paren} ...left side and right side if you will.

Suff on the left side is called "formal parameters" or even often called "arguments".

Stuff on the right side is called "localization". This area just does one simple thing; it destroys variables. Remember the bad example of variable declaration I gave above? (setq myVar '()) ...thats essentially whats happening. The variable is just being destroyed.


Localization example:
Code: [Select]
(defun foo ( / var1 var2 var3 )
        (setq var1 1)
        (setq var2 2)
        (setq var3 3)
 )

All vars are localized (destroyed).

Now where does global come into play? Simple, just dont destroy it.

Global example
Code: [Select]
(defun foo ( / var2 var3 )
        (setq var1 1)
        (setq var2 2)
        (setq var3 3)
 )

Now var1 is global. Get it?


OK, now this write up was very broad in nature and loose with explanations. As you get better and have a better understanding of programming in general you will need to expand on these explanations more. -e.g. when I said that a value gets "put into" a variable, that isnt actually what happens. When I said that a variable gets "destroyed"..."variable destruction" doesnt really happen the way that those words imply.
TheSwamp.org (serving the CAD community since 2003)
Member location map - Add yourself

Donate to TheSwamp.org

JohnK

  • Administrator
  • Seagull
  • Posts: 10637
Re: 'ReplaceText' lisp errors on first go ...
« Reply #9 on: July 23, 2010, 12:12:46 PM »
Quote from: Se7en
Code: [Select]
 (setvar "cmdecho" 0)
[color=red]                ;; 7: you're not even going to save the users current settings?
[/color]
Well umm, ... no I wasn't.  Most of the users don't even read their command line yet, I'm hoping to get them trained to look at it.  I want to force the echo on at the end but I don't see a reason for the echo to be on during the function, so I'm hard coding it in.  I did forget about my little (quit) if there's no text though.   :oops:  I need to fix that.


I dont care. You WILL save the users setting(s) before you set it to what YOU want (i'll show you how, its real easy).

Code: [Select]
(defun foo ( / users-old-cmdecho-value ) [color=green];;<-- we are doing what here?[/color]
        (setq users-old-cmdecho-value (getvar 'CMDECHO)) [color=green];; <-- what is this referred to as again?[/color]
        ;; store the users cmdecho variable so that we can
        ;; set it to something else for now. we will set it
        ;; back later.

        (setvar 'CMDECHO 0)

        ;; do my fancy lisp processing here
        ;; on hundreds of lines
        ;; of this file, resulting in me getting
        ;; a raise and sending Se7en
        ;; money because I think he is the
        ;; greatest person in the whole, wide
        ;; world!
        ;;
        ;;*phew!* that was a good lisp.

        ;; lets reset the users CMDECHO value
        ;; back to what they had before.
        (setvar 'CMDECHO users-old-cmdecho-value)

 )
TheSwamp.org (serving the CAD community since 2003)
Member location map - Add yourself

Donate to TheSwamp.org

Lee Mac

  • Seagull
  • Posts: 12914
  • London, England
Re: 'ReplaceText' lisp errors on first go ...
« Reply #10 on: July 23, 2010, 01:09:31 PM »
Localization example:
Code: [Select]
(defun foo ( / var1 var2 var3 )
        (setq var1 1)
        (setq var2 2)
        (setq var3 3)
 )

All vars are localized (destroyed).

Now where does global come into play? Simple, just dont destroy it.

Global example
Code: [Select]
(defun foo ( / var2 var3 )
        (setq var1 1)
        (setq var2 2)
        (setq var3 3)
 )

Now var1 is global. Get it?

Just to elaborate on this slightly, let us say we have two (or more) functions, and we wish for variables to be accessible in both, eg;

Code: [Select]
(defun foo ( / var1 )
  (setq var1 "Hangman")
  (bar)

  (princ)
)

(defun bar nil
  (princ var1)
)

Here we define (and declare) the variable 'var1' in function foo and reference it in function bar. Notice that the variable is not localised in bar, as this would render it's value null upon calling bar from foo.

Effectively, we can look at it like this, replacing (bar) with all the expressions contained in that definition.

Code: [Select]
(defun foo ( / var1 )
  (setq var1 "Hangman")

  ;; Now evaluate a bunch of statements in 'bar'

  (princ var1)

  ;; Now get back to foo
  (princ)
)

This method of referencing variables between functions is called 'lexical scoping' if I remember from Se7en correctly...

So you can effectively view subfunctions as elegant 'space-savers', instead of duplicating code over and over.

IMO (for what its worth), I prefer to pass arguments where I can, and avoid referencing variables across multiple functions unless I absolutely have to - I feel this makes the intention clearer and easier to understand. Not to mention it makes the subfunction useful as a stand-alone function (or library function if you will).

Lee

JohnK

  • Administrator
  • Seagull
  • Posts: 10637
Re: 'ReplaceText' lisp errors on first go ...
« Reply #11 on: July 23, 2010, 01:16:15 PM »
Be very careful of that phrase: "render it's value null" because thats not what REALLY happens. NULL means zero'ed out, that isnt what happens.
TheSwamp.org (serving the CAD community since 2003)
Member location map - Add yourself

Donate to TheSwamp.org

JohnK

  • Administrator
  • Seagull
  • Posts: 10637
Re: 'ReplaceText' lisp errors on first go ...
« Reply #12 on: July 23, 2010, 01:18:33 PM »
<snip>
Quote from: Se7en
Code: [Select]
(defun *error* (msg /)
    (on-error msg)
    (princ)
  ) ; _EoF
               [color=red] ;; 7: what is "on-error"? ...either use "*error*" or "on-error"[/color]
Oops.  I apologize, I forgot I needed the 'on-error' to fully understand it all.  Here it is.
<snip>

Here is where things get a bit fuzzy but try and hold on. -i.e. the reason why high level languages are refered to as "high" is because they hide the inner workings from you. When I told you how to declair and define a variable you were actually assigning a NAME to some space in memory which held a THING.

So when you create a function ("abstraction") you are really defining a NAME to a series of process' (hence the term: abstraction. Its abstract, its removed from you. Its a black box of stuff).

Let me demonstrate this by declaring and defining a NAME in several ways (Think of this exercise as: Filling a "variable" with a "function".)

Code: [Select]
(setq a (lambda (n) (+ n 1))) ;; choosen to be first because you are used to seeing SETQ

;; Isn't any different than

(set 'a (lambda (n) (+ n 1)))

;; Isn't any different than

(defun a (n) (+ n 1))

;; give it a try.

(a 1)

Now dont worry if you dont grasp this right away. just let it mull around for a bit.

We didnt actually `fill a ``variable'' with a ``function''" at all. A more acurate description of what we did with either one of those three steps is "DEFINE A NAME". That is UBER important! Think about that phrase.

To use our own error handling routine all we have to do is to "RE-define a name"

(setq the-old-error-function *error*)
;; declare and define a variable (NAME).

(setq *error* my-new-fancy-error-function)
;; now "*ERROR*" "refers to" something else.

Now whenever and/or how ever long "*error*" "refers to" "my-new-fancy-error-function" the STUFF found in "my-new-fancy-error-function" will be executed.

In other words: An execution of "(QUIT)" will then call "*ERROR*" which now refers to "MY-NEW-FANCY-ERROR-FUNCTION".

Does that make sense?

Im giving you a lot of information here that should by all rights take you years to learn. it may not seem like it but these explinations are very general and incomplete only offered up to help you understand how to READ and UNDERSTAND AutoLisp (programming in general) better.
TheSwamp.org (serving the CAD community since 2003)
Member location map - Add yourself

Donate to TheSwamp.org

JohnK

  • Administrator
  • Seagull
  • Posts: 10637
Re: 'ReplaceText' lisp errors on first go ...
« Reply #13 on: July 23, 2010, 01:23:06 PM »
K, I think thats all the pieces you need so-far. Read what i gave you several times, i was very literal in my wording.

BTW, How are we doing?

TheSwamp.org (serving the CAD community since 2003)
Member location map - Add yourself

Donate to TheSwamp.org

hermanm

  • Guest
Re: 'ReplaceText' lisp errors on first go ...
« Reply #14 on: July 23, 2010, 04:50:05 PM »
(quit) is an error condition, as John has told you.

You should structure your program to avoid an abnormal end under most conditions.

Example: (picked at random from my own code)
Code: [Select]
(cond
    ((= -2 strdata) (princ "\nUnable to Find Datafile"))
    ((= -1 strdata) (princ (strcat "\nShape not Found: " shapename)))
    ( T (setq realdata (mapcar 'atof (cdr(strtok strdata " ")))))
);cond
(if realdata
  (progn
  <do stuff here>
  );end progn
);end if
(vla-endundomark *THISDWG*)
(princ)
);end defun

Hangman

  • Swamp Rat
  • Posts: 566
Re: 'ReplaceText' lisp errors on first go ...
« Reply #15 on: August 02, 2010, 01:45:49 PM »
Sorry I haven't commented recently, I've been pulled away for a few days.
Should be back on top of things once I get my head wrapped around this again.

K, I think thats all the pieces you need so-far. Read what i gave you several times, i was very literal in my wording.

BTW, How are we doing?

Phew, there's a lot of info there.  I do understand I think regarding the use of the variables now.  This has been a good description and learning experience.

I have a couple questions regarding what Lee mentioned:
Just to elaborate on this slightly, let us say we have two (or more) functions, and we wish for variables to be accessible in both, eg;

< ... > we define (and declare) the variable 'var1' in function foo and reference it in function bar. Notice that the variable is not localised in bar, as this would render it's value null upon calling bar from foo.

This is in essence, what I have going on in my code above.

This method of referencing variables between functions is called 'lexical scoping' if I remember from Se7en correctly...

So you can effectively view subfunctions as elegant 'space-savers', instead of duplicating code over and over.

IMO (for what its worth), I prefer to pass arguments where I can, and avoid referencing variables across multiple functions unless I absolutely have to - I feel this makes the intention clearer and easier to understand. Not to mention it makes the subfunction useful as a stand-alone function (or library function if you will).

Lee

Now I have read from other posts in the past that cutting and pasting code to put together a new routine is fine, as long as it all works out in the end.  I've found that errors can be horrendous by doing this unless you keep very careful track of everything.  But if you are cutting and pasting code from other routines, you will have a lot of these "Lexical scoping" issues.  By Lee's opinion, you don't like to do this, correct ??  You'd rather have one definition and the code inside to run from that definition.  Would you consider this a good practice though for it's purposes ??  I mean, I like 'lexical scoping' for several reasons; it can be used in other definitions without rewriting the code.  It makes the routine as a whole, easier to follow and troubleshoot as you are working with sections rather than the whole.  But there are the drawbacks, like as was mentioned, variables are referenced across multiple functions.

There was a lot of information given here, and there are still some things I am confused over, but I can't put my finger on them yet to post a question.  Or in other words, I haven't yet thought through it from beginning to end, to post a question that hasn't already been answered.

Now looking back at my code, I can see that the piece here:
Code: [Select]
(if (/= OldText "String")                         ; if NOT replacing entire string
    (SelectAllStrings)                              ; Then select ALL text
    (SelectEachString)                              ; Else select strings to be replaced
  ) ; _if
referencing these:
Code: [Select]
(defun SelectEachString (/ )
  (while (= AllText nil)
    (prompt "\nSelect text to be replaced: ")
    (setq AllText (ssget '((0 . "TEXT,MTEXT"))))    ; ,MULTILEADER
  )
  (ProcessText)
)
;
(defun SelectAllStrings (/ )
  (prompt "\nSelect text to be replaced (Press 'Enter' for all text in drawing): ")
  (setq AllText (ssget '((0 . "TEXT,MTEXT"))))
  (if (or (= AllText nil) (= AllText ""))
    (setq AllText (ssget "_X" '((0 . "TEXT,MTEXT")))); ,MULTILEADER
  )
  (ProcessText)
)
Could be all-inclusive to the main segment of code and probably save a few lines.  Although I don't see why it would matter (for this small piece of code) as there is no variables in either that are being referenced somewhere else.  From the main segment to these definitions, it is a simple open & close process.
The third section is different however, as it does go back and forth from the main section.

Can I assume you are seeing something in my code that is prompting you to go in this direction, that is, toward this instruction of variables ??
As CAB mentioned:
Hangman,
FYI, First thing you do when debugging is to disable the local error handler.

I ran the routine with out error but it did not replace the text. :-o

Which is what is happening to me as well.  What I am not understanding is I can edit the code with something simple like adding a comma to a comment line and saving the code and reloading it, and it works fine.  It is just the initial time it is loaded it will not run, it errors.
I know the error occurs in the main segment of code and not in any of the 'lexical scoping' sections (or 'sub-functions' perhaps?).  But there's another drawback to lexical scoping I guess, you can have a variable referenced in one of the sub-functions and it could cause an error in the main section.  But that doesn't make sense here (again, trying to think this through) as, by stepping through the code, it would have to get toward the end of the main section before transfering to the third definition (processtext) to find a variable NOT being initialized to cause an error.  And so far, it is not doing this.  My code is erroring before getting to the third definition, ... it is erroring before getting to the second definitions.

I am thinking if I rewrite this, I can probably make it work as I want it to, eliminating the error as I go.  But I want to find this error so I can avoid making the same mistake again later with some other similar piece of code I may put together.

Thank you for your continued help.
Hangman  8)

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Drafting Board, Mechanical Arm, KOH-I-NOOR 0.7mm
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Hangman

  • Swamp Rat
  • Posts: 566
Re: 'ReplaceText' lisp errors on first go ...
« Reply #16 on: August 02, 2010, 02:21:55 PM »
Lee,
< ... >  But then what do I know lol  :wink:

A lot more than me.   ^-^


Ok, so isn't your '( code eList )' considered a Lexical scope ??  Or am I missing the definition of Lexical scope ... being, in order to have a Lexical scope, the code has to be dependent upon something more.  Otherwise you would simply have a sub-function.
If this be the case, then according to my code;  the first and second sub-functions are just that, sub-functions.  The third 'sub-function' (ProcessText) is my lexical scope, because it IS dependent on other definitions or variables that are crossing.
Hangman  8)

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Drafting Board, Mechanical Arm, KOH-I-NOOR 0.7mm
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Lee Mac

  • Seagull
  • Posts: 12914
  • London, England
Re: 'ReplaceText' lisp errors on first go ...
« Reply #17 on: August 02, 2010, 02:30:55 PM »
Ok, so isn't your '( code eList )' considered a Lexical scope ??

No, as it is completely self-contained, i.e. it doesn't depend on variables from another function.

Consider this:

Code: [Select]
(defun f ( / a b c )

  (setq a 1 b 2 c 3)

  (g)
)

(defun g ( / )
  (* b 2)
)

Notice that 'g' depends on 'f' in that it ASSUMES that there will be a variable 'b' holding numerical data to work with.

Now consider this:

Code: [Select]
(defun f ( / a b c )

  (setq a 1 b 2 c 3)

  (g b)
)

(defun g ( x )
  (* x 2)
)

Now, 'g' does not depend on 'f' and makes no assumptions. When called, data for the argument 'x' must be supplied, and hence we are guaranteed that 'x' will contain data.


Hangman

  • Swamp Rat
  • Posts: 566
Re: 'ReplaceText' lisp errors on first go ...
« Reply #18 on: August 02, 2010, 02:41:08 PM »
Ok, let me paint a picture and see if I'm correct in my assessment.  I'm kind of stuck on this 'Lexical Scope' thing, as I think this is where my error is occuring, but I'm not positive.

Lexical scoping would be like this:

Say you are walking down the sidewalk.  You left foot is independent of your right foot.  They both work together to make the motion of moving forward, but they are each independent.  The same thing would apply if you were using crutches and your left foot was in a cast.  The left foot would be held above the sidewalk, not ever touching.  But the crutches would be working independent of the right foot, and assisting the right foot in making the forward motion.
This is what you are refering to with this:
Code: [Select]
(defun dxf ( code elist ) (cdr (assoc code elist)))
Now, if you had a cane rather than crutches, you would be dependent on the stability of the cane AND your left foot to cause the forward motion.  The right foot is independent of the left, but the left would be dependent on the cane.  So in order to get forward motion, the left foot would dependent on the variable 'cane'.
Code: [Select]
(defun dxf ( code ) (cdr (assoc code elist)))
Am I understanding this (Lexical scope) correctly ??


Doht !!  You posted just before I could get this out.  But yes, I now understand.  Your code explanation is better than my crutches/cane explanation too.   :-D

Again, Thank you for your time.  I'm going to go re-write my code now and see if I can't eliminate my Lexical scope issue and see if this will fix my error.
Hangman  8)

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Drafting Board, Mechanical Arm, KOH-I-NOOR 0.7mm
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Lee Mac

  • Seagull
  • Posts: 12914
  • London, England
Re: 'ReplaceText' lisp errors on first go ...
« Reply #19 on: August 02, 2010, 02:45:17 PM »
You're very welcome, I'm glad I could help you understand :-)

JohnK

  • Administrator
  • Seagull
  • Posts: 10637
Re: 'ReplaceText' lisp errors on first go ...
« Reply #20 on: August 02, 2010, 03:36:52 PM »
whoa?! NOTE: I havent read all the posts on the second page but i was hit by the phrase "lexical scope"...the example that LeeMac gave on the first page is NOT lexical scoping. That example is just an example of Global Variables. 

Im sorry Lee if i ever confused you but the example you gave is way off. Briefly: Lexical is "how" and scope is kinda like "what can i do with it". The whole topic is very complicated and i only introduced it in my technical paper "Building named abstractions" because of spaghetti code.

I have to read this thread now. Sorry for any confusion.
TheSwamp.org (serving the CAD community since 2003)
Member location map - Add yourself

Donate to TheSwamp.org

JohnK

  • Administrator
  • Seagull
  • Posts: 10637
Re: 'ReplaceText' lisp errors on first go ...
« Reply #21 on: August 02, 2010, 03:55:59 PM »
Okay, forget Lexical scoping! ...i have a fire here. i will be back.
TheSwamp.org (serving the CAD community since 2003)
Member location map - Add yourself

Donate to TheSwamp.org

pkohut

  • Guest
Re: 'ReplaceText' lisp errors on first go ...
« Reply #22 on: August 02, 2010, 04:32:25 PM »
You've basically got 2 scoping choices for variables. Local Scope and Global Scope. To reduce side effects in your code, strive for local scoping and pass needed arguments to the function. Local scoping allows functions to be self contained modules, that shouldn't break when changes are made to code outside of the function.

In your case Hangman, you've talked about cutting and pasting code and such. Upfront you have no idea which variable names are used or their type. For example, suppose you wrote code that set global A to a string and did string type work. In another chuck of code, global variable A is set to a float is called. Now your string work code needs to do more work but crashes because global A isn't a string any more.

Another side effect (related to the above), is you've set a global variable in some function and written code in another function that uses the variable name. At some point you rename the variable in the first function, and everything appears to works great until a new drawing or session of Acad is started, where the expected variable name isn't set and the second function crashes.

Global variables have their place, just try and limit their use.

JohnK

  • Administrator
  • Seagull
  • Posts: 10637
Re: 'ReplaceText' lisp errors on first go ...
« Reply #23 on: August 02, 2010, 04:39:41 PM »
Code: [Select]
(defun f ( / a b c g )

        (defun g ( ) (* b 2) )
        ;; local function
        ;;
        ;; this function will "look up" the `b' variable.


  (setq a 1
        b 2
        c 3 )

  (g)
)

In an effort to clean up this little scope mess, this is a better example of Lexical Scoping in action but that phrase is just facy speak for: how the AutoLisp interpreter can look up a variable. Please, do NOT be concerned with how the `b' var is being enacted or what the interpreter can or cant do with it. The only thing you should take from this whole mess is that you can look at this code in a way that will allow you to improve the readablity of your main program later once you grasp the concept of local and global variables.

For example, using this model allows you to build procedures that can be constructed and read this way:

Code: [Select]
(defun ( / )
  ;; local functions...
  ;;     ...
  ;; end local functions...

  (ask-for-payment)
  (count-payment)
  (make-change)
  (return-to-user)

  (princ)
 )

Okay, this concept is not pertant to what you need to know now. Focus on the localization of variables, Global variables, and simple NAMES like (setq myVar...).
TheSwamp.org (serving the CAD community since 2003)
Member location map - Add yourself

Donate to TheSwamp.org

JohnK

  • Administrator
  • Seagull
  • Posts: 10637
Re: 'ReplaceText' lisp errors on first go ...
« Reply #24 on: August 02, 2010, 05:07:00 PM »
<snip>
Global variables have their place, just try and limit their use.

Let me repeat that for you Hangman.

"Global variables have their place, just try and limit their use."

TheSwamp.org (serving the CAD community since 2003)
Member location map - Add yourself

Donate to TheSwamp.org

Lee Mac

  • Seagull
  • Posts: 12914
  • London, England
Re: 'ReplaceText' lisp errors on first go ...
« Reply #25 on: August 02, 2010, 06:07:47 PM »
Quote from: Lee Mac
But then what do I know lol :P

Hangman

  • Swamp Rat
  • Posts: 566
Re: 'ReplaceText' lisp errors on first go ...
« Reply #26 on: August 03, 2010, 02:49:15 PM »
Okay, forget Lexical scoping! ...i have a fire here. i will be back.

Heh, sounds like you've got a fire here !!  :-D
Hangman  8)

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Drafting Board, Mechanical Arm, KOH-I-NOOR 0.7mm
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~