Author Topic: Function design (thoughts)  (Read 7096 times)

0 Members and 1 Guest are viewing this topic.

Mark

  • Custom Title
  • Seagull
  • Posts: 28762
Function design (thoughts)
« on: March 30, 2005, 07:56:47 AM »
When defining a function, specifically a utility function;

How do you decide on what arguments to accept?

What about output?

Do you put a lot of emphasis on error checking?

Do you try a recursive approach first?

Is creating a separate function a last resort when designing an application?

You thoughts on any/all of the above would be very much appreciated.

Some of these questions are very broad, but I'm trying to get inside your thought process on function design.
TheSwamp.org  (serving the CAD community since 2003)

David Hall

  • Automatic Duh Generator
  • King Gator
  • Posts: 4075
Function design (thoughts)
« Reply #1 on: March 30, 2005, 08:17:51 AM »
Most of my Utility functions apply to the whole drawing. Like resetting ALL the text in a drawing back to the correct style, or going thru the entire drawing and resetting the ByLayer settings for color and linetype etc.
Everyone has a photographic memory, Some just don't have film.
They say money can't buy happiness, but it can buy Bacon and that's a close second.
Sometimes the question is more important than the answer. (Thanks Kerry for reminding me)

MP

  • Seagull
  • Posts: 17750
  • Have thousands of dwgs to process? Contact me.
Re: Function design (thoughts)
« Reply #2 on: March 30, 2005, 08:29:36 AM »
I've been using the same basic technique for awhile in both lisp and vb and it's very simple in my mind. Other than the container that hosts all the subs and functions, let's call that the application, each function or sub does precisely one thing; no more, no less. This makes it easy to understand, easy to maintain, easy to debug and yields a lot of functions / subs that can be reused.

Having said that, distilling an app down to it's constituent functions yields functions whose arguments are usually very apparent, in fact, I don't recall struggling over argument determination. I would say that I normally avoid any globals (though constants do qualify as globals), so if a function needs some value not defined by constants it will usually become an argument.

At the function level I try to minimize error checking, that's the job of higher level programming. If every function is idiot proofed overall performance of the application really suffers. So ... if a function expects a string it will crash if passed an integer, not elegantly exist saying "Dood, wrong value passed as parameter, have a nice day."

Recursion is fun and the resulting code is usually very elegant, but there's usually a performance penalty to be paid. Also, recursion has (stack) limits, so one has to be careful to remove superfluous information from being pushed onto the stack for each call, lest the limits be reached unnecessarily quickly (read optimization is important). So ... normally I won't use recursion, but sometimes it's the right route, for example -- scanning directories for files.

<cough>

That's all I have for now. What are ya doing posting a question like this at 6am and why on earth am I trying to answer it before I've even had a coffee??? Medic: Can I get a jumbo coffee, triple caffiene special over here?

Engineering Technologist • CAD Automation Practitioner
Automation ▸ Design ▸ Drafting ▸ Document Control ▸ Client
cadanalyst@gmail.comhttp://cadanalyst.slack.comhttp://linkedin.com/in/cadanalyst

JohnK

  • Administrator
  • Seagull
  • Posts: 10648
Function design (thoughts)
« Reply #3 on: March 30, 2005, 09:17:20 AM »
I agree with MP 150%.
TheSwamp.org (serving the CAD community since 2003)
Member location map - Add yourself

Donate to TheSwamp.org

MP

  • Seagull
  • Posts: 17750
  • Have thousands of dwgs to process? Contact me.
Function design (thoughts)
« Reply #4 on: March 30, 2005, 10:02:14 AM »
I think what John's saying is that it would be real nice if someone bought him a real big coffee.

:)
Engineering Technologist • CAD Automation Practitioner
Automation ▸ Design ▸ Drafting ▸ Document Control ▸ Client
cadanalyst@gmail.comhttp://cadanalyst.slack.comhttp://linkedin.com/in/cadanalyst

JohnK

  • Administrator
  • Seagull
  • Posts: 10648
Function design (thoughts)
« Reply #5 on: March 30, 2005, 10:04:14 AM »
Amen brother!
TheSwamp.org (serving the CAD community since 2003)
Member location map - Add yourself

Donate to TheSwamp.org

CAB

  • Global Moderator
  • Seagull
  • Posts: 10401
Function design (thoughts)
« Reply #6 on: March 30, 2005, 11:54:44 AM »
????  How do you decide on what arguments to accept?
You may gain some versatility by accepting different types like ename, elist or object.
Those are easy to test and have the subroutine convert to what it needs. Else the overhead
of converting is on the calling routine. If you have a large application and the routine is
called many times from many places it may be more efficient to convert the variable in the subroutine.
That said I usually don't as all my routines are small. :)

????  What about output?
I assume you are referring to the returned value(s).
Seems to me that the function of the subroutine would dictate the return value(s)

????  Do you put a lot of emphasis on error checking?
Ah the 'What IF' game.
i think the user input need a lot of error checking but not so much on the subroutine level.
I think you have to ask what are the consequences of the error. If the error just dumps the user
and no harm to the drawing then a simple error trap with a message is fine. If a lot of user input is
going to have to be re-entered it would be a good thing to trap & correct the error else the user
will be miffed at the program.

????  Do you try a recursive approach first?
No, last. For me it's hard to program, hard to debug, hard to understand when i come back.
But it's a fun exercise at times.:)

????  Is creating a separate function a last resort when designing an application?
I usually look at how many times the subroutine is used and how large the subroutine is and how
if affects readability of the routine.
If the routine will be used 3 or more times
Or if the routine is larger than 10 lines of code, my code not MPs
(sorry I have this condense idous disease and can't seem to shake it.)
Or if the chunk of code when removed to a subroutine makes the Main routine read more clearly
I sometimes will move it to a subroutine.


That said, I always break my own rules.
I look back at some of my older code, last month is older, and see it need some cleanup but
who has the time? If it ain't broke.

I only wish I could write like MP, and Mark and Stig & John and others but I don't think it's in me.
I guess we have our own basic style & it's hard to change that.
So I guess the answerers are subjective to some degree.

My 2 cents.


Wow that got long winded :)
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.

Mark

  • Custom Title
  • Seagull
  • Posts: 28762
Function design (thoughts)
« Reply #7 on: March 30, 2005, 12:42:19 PM »
I have found myself lately using a subroutine just to make the code more readable, at least to me it is. *g*

Example. I wrote a little clipboard app and I used this;
Code: [Select]

(defun is-text (ent)
    (cond
      ((= (cdr (assoc 0 (entget ent))) "MTEXT") T)
      ((= (cdr (assoc 0 (entget ent))) "TEXT") T)
      (T nil)
      )
    )


and then in the body of the app I simply used;

Code: [Select]
(if (is-text ent)

makes it easier to follow to me.
TheSwamp.org  (serving the CAD community since 2003)

MP

  • Seagull
  • Posts: 17750
  • Have thousands of dwgs to process? Contact me.
Function design (thoughts)
« Reply #8 on: March 30, 2005, 01:03:41 PM »
Good idea Mark. The only thing I'd probably do different is with regards to naming. Essentially you are writing a predicate function (return t or nil), e.g. listp, numberp, zerop so ...

Code: [Select]
(defun textp ( ename )
    (and
        (member
            (cdr (assoc 0 (entget ename)))
           '("TEXT" "MTEXT")
        )
    )    
)

Or of you do predominantly activex stufff ...

Code: [Select]
(defun textp ( object )
    (and
        (member
            (vla-get-objectname object)
           '("AcDbText" "AcDbMText")
        )
    )    
)
Engineering Technologist • CAD Automation Practitioner
Automation ▸ Design ▸ Drafting ▸ Document Control ▸ Client
cadanalyst@gmail.comhttp://cadanalyst.slack.comhttp://linkedin.com/in/cadanalyst

Kerry

  • Mesozoic relic
  • Seagull
  • Posts: 11654
  • class keyThumper<T>:ILazy<T>
Function design (thoughts)
« Reply #9 on: March 30, 2005, 01:10:41 PM »
Quote from: Mark Thomas
I have found myself lately using a subroutine just to make the code more readable, at least to me it is. *g*

.. makes it easier to follow to me.


Mark, I do the same, for the same reason. In fact, I forgo thought of 'optimum speed freak efficiency' , preferring readability and 'scanability'.

Sometimes it seems  more difficult to find a name for the routine than writing the routine itself .... decisions, decisions ..

I find modularizing code this way fits my thought process and translates easily from a design outline [ pseudocode].

The ugly side to this can be the 'indiscriminate wrapping' I see sometimes.

It's my opinion that programming is a confidence game [ in the nicest sense], that is, we build on what we know is correct, confident in the foundation we have established. Anything I can do to reduce the potential for errors, or at least isolates 'thoughts' so they can be tested,  increases my confidence.
kdub, kdub_nz in other timelines.
Perfection is not optional.
Everything will work just as you expect it to, unless your expectations are incorrect.
Discipline: None at all.

Kerry

  • Mesozoic relic
  • Seagull
  • Posts: 11654
  • class keyThumper<T>:ILazy<T>
Function design (thoughts)
« Reply #10 on: March 30, 2005, 01:23:51 PM »
One thing I have gotten into the habit of doing is listing the user defined dependancy functions in the header for routines.

functionName < Parameters > :: fileName

.. though this may just be 'cause my memory isn't what it used to be .. :)
kdub, kdub_nz in other timelines.
Perfection is not optional.
Everything will work just as you expect it to, unless your expectations are incorrect.
Discipline: None at all.

Mark

  • Custom Title
  • Seagull
  • Posts: 28762
Function design (thoughts)
« Reply #11 on: March 30, 2005, 01:25:15 PM »
Quote from: MP
Good idea Mark. The only thing I'd probably do different is with regards to naming. Essentially you are writing a predicate function (return t or nil), e.g. listp, numberp, zerop so ...

Oh yea............ great tip MP!!

why didn't I think of that in the first place...
TheSwamp.org  (serving the CAD community since 2003)

Mark

  • Custom Title
  • Seagull
  • Posts: 28762
Function design (thoughts)
« Reply #12 on: March 30, 2005, 01:28:58 PM »
Quote from: Kerry Brown
The ugly side to this can be the 'indiscriminate wrapping' I see sometimes.

Sorry but I don't understand.  :oops:

Quote from: Kerry Brown
It's my opinion that programming is a confidence game [ in the nicest sense], that is, we build on what we know is correct, confident in the foundation we have established. Anything I can do to reduce the potential for errors, or at least isolates 'thoughts' so they can be tested,  increases my confidence.

Well said...........
TheSwamp.org  (serving the CAD community since 2003)

MP

  • Seagull
  • Posts: 17750
  • Have thousands of dwgs to process? Contact me.
Function design (thoughts)
« Reply #13 on: March 30, 2005, 01:29:08 PM »
Quote from: Mark Thomas
why didn't I think of that in the first place...

You kind of did, you're just following VB(A) naming conventions: IsArray, IsDate, IsEmpty ...

:)
Engineering Technologist • CAD Automation Practitioner
Automation ▸ Design ▸ Drafting ▸ Document Control ▸ Client
cadanalyst@gmail.comhttp://cadanalyst.slack.comhttp://linkedin.com/in/cadanalyst

MP

  • Seagull
  • Posts: 17750
  • Have thousands of dwgs to process? Contact me.
Function design (thoughts)
« Reply #14 on: March 30, 2005, 01:31:30 PM »
Good stuff Kerry, thanks for sharing. I agree, esp. superfluous wrapping, even though I may be guilty as charged.

:)
Engineering Technologist • CAD Automation Practitioner
Automation ▸ Design ▸ Drafting ▸ Document Control ▸ Client
cadanalyst@gmail.comhttp://cadanalyst.slack.comhttp://linkedin.com/in/cadanalyst

Mark

  • Custom Title
  • Seagull
  • Posts: 28762
Function design (thoughts)
« Reply #15 on: March 30, 2005, 01:38:26 PM »
Quote from: MP
You kind of did, you're just following VB(A) naming conventions: IsArray, IsDate, IsEmpty ...

Maybe it's to much Python and C++ and PHP and PERL ...... *grin*

funny fact:
When I write something in C++ I compile it right from Vim using the builtin command "make". Well the other day I wrote a little autolisp routine and then ran "make" on it. "oop's that didn't work" :oops:
TheSwamp.org  (serving the CAD community since 2003)

MP

  • Seagull
  • Posts: 17750
  • Have thousands of dwgs to process? Contact me.
Function design (thoughts)
« Reply #16 on: March 30, 2005, 01:45:01 PM »
I hear ya -- can't tell you how many times I've been coding basic and typed "(defun ..."

Engineering Technologist • CAD Automation Practitioner
Automation ▸ Design ▸ Drafting ▸ Document Control ▸ Client
cadanalyst@gmail.comhttp://cadanalyst.slack.comhttp://linkedin.com/in/cadanalyst

JohnK

  • Administrator
  • Seagull
  • Posts: 10648
Function design (thoughts)
« Reply #17 on: March 30, 2005, 01:48:13 PM »
As long as we are on the subject of args/vars:

If im actualy building true "suport procedures" like Marks "istext" example i usualy rely on scoping to handle my args/vars.

Forinstance:
Code: [Select]
(defun c:MyApp ( / ename textp)
  (while (not (setq ename (entsel "\nselect object: "))))
 ;; suport procedure
  (defun textp ( )
    (and
      (member
        (cdr (assoc 0 (entget (car ename))))
        '("TEXT" "MTEXT")
        )
      )  
    )

  (if (textp)
    (alert "Congrats! You selected some Text.")
    (alert "Nope!")
    )
  )


This makes it that much easier for me to read. (Look at my "if" vs this:
 (if (textp (car ename))
   ...
   )
 )

Using lexical scoping in your procedure design, i believe, is much easier to follow.
TheSwamp.org (serving the CAD community since 2003)
Member location map - Add yourself

Donate to TheSwamp.org

MP

  • Seagull
  • Posts: 17750
  • Have thousands of dwgs to process? Contact me.
Function design (thoughts)
« Reply #18 on: March 30, 2005, 01:52:15 PM »
To each their own, but I wouldn't code it like that myself, though I am a proponent of lexical scoping.
Engineering Technologist • CAD Automation Practitioner
Automation ▸ Design ▸ Drafting ▸ Document Control ▸ Client
cadanalyst@gmail.comhttp://cadanalyst.slack.comhttp://linkedin.com/in/cadanalyst

JohnK

  • Administrator
  • Seagull
  • Posts: 10648
Function design (thoughts)
« Reply #19 on: March 30, 2005, 01:55:51 PM »
Why?
TheSwamp.org (serving the CAD community since 2003)
Member location map - Add yourself

Donate to TheSwamp.org

MP

  • Seagull
  • Posts: 17750
  • Have thousands of dwgs to process? Contact me.
Function design (thoughts)
« Reply #20 on: March 30, 2005, 01:57:23 PM »
Primarilly code clarity and re-use.
Engineering Technologist • CAD Automation Practitioner
Automation ▸ Design ▸ Drafting ▸ Document Control ▸ Client
cadanalyst@gmail.comhttp://cadanalyst.slack.comhttp://linkedin.com/in/cadanalyst

JohnK

  • Administrator
  • Seagull
  • Posts: 10648
Function design (thoughts)
« Reply #21 on: March 30, 2005, 02:03:34 PM »
Re-use is the reason i use it.

Pulling that procedure out and trying to hack together an app is hard.  (Forces me to "Build" a procdeure and not "cut 'n paste" one.) I realise that my "worker procedures" could get a bit bloated this way (-ie. building built-in support procedures for each) but its worth the overall robust-ness i achieve.
TheSwamp.org (serving the CAD community since 2003)
Member location map - Add yourself

Donate to TheSwamp.org

Kerry

  • Mesozoic relic
  • Seagull
  • Posts: 11654
  • class keyThumper<T>:ILazy<T>
Function design (thoughts)
« Reply #22 on: March 30, 2005, 02:11:33 PM »
Quote from: Mark Thomas
Quote from: Kerry Brown
The ugly side to this can be the 'indiscriminate wrapping' I see sometimes.

Sorry but I don't understand.  :oops:
 <<<<


I have seen [ read as,  debugged others code ] that has taken this concept a little to far. Its a different issue if the wrapped routine is used more than once or if it has a significant purpose.  ... sort of negates the 'readability' aspect. .. but admittedly that was for code that I didn't originate, so a reasonable amount of mental gymnastics is expected. :)

 .... on the other hand, a 3 page function that has no white space for thought delineation, and no header comments [ I prefer those to in line comments, the code should speak to me ] and everything inline and questionable variable naming regimen is a completely different can of worms.
kdub, kdub_nz in other timelines.
Perfection is not optional.
Everything will work just as you expect it to, unless your expectations are incorrect.
Discipline: None at all.

Kerry

  • Mesozoic relic
  • Seagull
  • Posts: 11654
  • class keyThumper<T>:ILazy<T>
Function design (thoughts)
« Reply #23 on: March 30, 2005, 02:19:21 PM »
... while [ sort of ] on the topic of debugging ..

I frequently will write a named routine just for testing, then include its code in line to reduce the necessity of passing variables to a helper.
kdub, kdub_nz in other timelines.
Perfection is not optional.
Everything will work just as you expect it to, unless your expectations are incorrect.
Discipline: None at all.