list of functions to a subroutine

Discussion in 'AutoCAD' started by TCEBob, Apr 11, 2004.

  1. TCEBob

    TCEBob Guest

    I'm trying to write a routine called "for" that will emulate similar
    subs in other languages, eg (for 1 3 ... ) where the ... are a sequence
    of lisp functions. Naturally this will be built on (while), but maybe
    make coding a bit more streamlined. I *think* I need to encapsulate the
    function list in a quoted list, like '((fn1) (fn2) etc.) but the waters
    are getting murky. Should I be looking at (apply) or (progn) or maybe
    (lambda)?

    rs
     
    TCEBob, Apr 11, 2004
    #1
  2. Please identify a coding scenario where such a beast would be
    required. In almost twenty years of lisping I can't say I've run
    into a coding situation that wasn't solvable by a while, foreach,
    repeat, mapcar, apply, lambda, vlax-for, vlax-map-collection ...

    While I'm definitely open to the idea, and would find an efficient
    implementation interesting, I'm suggestion a different angle might
    solve your immediate need using existing functions.

    In other words: Post (some of) yer code, or provide more info :)
     
    michael puckett, Apr 11, 2004
    #2
  3. TCEBob

    TCEBob Guest

    Well, sure, (while) is my best and sometimes only friend. But, having
    been raised in the shadows of Kemeny & Kurtz, I sometimes miss my old
    buddies. Just to see if it can be done is my only defense. And if you'd
    prefer not to answer the question I will understand.

    Actually, there is no immediate need excepting my own laziness and
    keying in "(setq counter 0) . . ." for the umpteenth time. If we view
    Vlisp out of the box as a minimal set of functions and vow to stay
    within them we have taken, as it were, an oath of fealty to the purity
    of the function set. I'm cool with that. But I do like to tinker.
    Besides, the basic question is "how can one present to a function a list
    of statements to be executed?" I bet the answer is simple. Just not
    simple enough for me.

    like:

    (defun foo(a b flist / )
    (statement ...
    (statement ...
    (execute the statements in flist)
    (statement ...
    )

    rs


     
    TCEBob, Apr 11, 2004
    #3
  4. There's little utility to genericizing basic contstructs
    like for() or foreach(), because you end up with nothing
    but highly convoluted, difficult to read code.

    This is kind of like inventing your own proprietary language
    extension, which is generally ill-advised (there may be some
    here that don't place much importance on the ability of others
    to easily read what they seem to regard as 'viable' code, I
    would beg to differ with them).

    But there are plenty of good uses for more specialized
    iterators that are designed to iterate very specific
    sets or collections of data. You cannot design them to
    work in the same way that functions that take a list of
    expressions (the 'body') work (such as while or repeat),
    however. So you must encapsulate your equivalent of a
    'body' in a function (or lambda), and your specialized
    iterator must accept the body as a functional argument,
    like (mapcar) requires.

    Here is a specialized iterator that can be used to
    process each file in a directory:

    (defun foreach-file (folder pattern func)
    (foreach file (vl-directory-files folder pattern)
    (apply func (list folder file))
    )
    )

    You can for example, use this to print out a list of
    DWG files in a folder:

    (foreach-file "D:\\Drawings" "*.dwg"
    '(lambda (folder file)
    (write-line "\nFile: " file)
    )
    )

    This method of iterating is the same as that used by
    the (mapcar) function (in contrast to foreach which
    takes an explicit list of expressions as its 'body').
    The lambda list is a function that takes two arguments,
    one is the name of the folder, and the other the name
    of each file.

    Disregarding the uselessness of the (for)-like function
    you envision, that iterates a range of integers, it
    might go something like this:

    (defun for (low high func / i)
    (setq i (1- (min low high)))
    (repeat (1+ (abs (- high low)))
    (apply func (list (setq i (1+ i))))
    )
    )

    The 'func' argument must be a function or lambda list
    that takes one argument, which is the index counter.

    Then:

    Command: (for 0 10 'print)

    0
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10 10

    Or, using a lambda:

    Command: (for 0 10 '(lambda (val) (print (* val 100))))

    0
    100
    200
    300
    400
    500
    600
    700
    800
    900
    1000 1000

    That just gives you the basic idea, and of course you
    could get fancy and add arguments that specify how to
    increment the counter variable too, to for example
    count down, or increment/decrement by something other
    than 1. However, I wouldn't bother with something like
    this, because it is akin to creating your own proprietary
    language extensions that only serve to make the the code
    less readable.
     
    Tony Tanzillo, Apr 11, 2004
    #4
  5. Whoops:

    (foreach-file "D:\\Drawings" "*.dwg"
    '(lambda (folder file)
    (write-line "\nFile: " file)
    )
    )

    Should be:

    (foreach-file "D:\\Drawings" "*.dwg"
    '(lambda (folder file)
    (write-line (strcat "\nFile: " file))
    )
    )
     
    Tony Tanzillo, Apr 11, 2004
    #5
  6. Is the K & K reference w/respect to BASIC?

    I'm not suggesting inventiveness should go out the window;
    yikes, might as well give up programming, as that's the very
    reason many of us push bits -- we get to invent every day.

    Anyway ... leaving the (setq counter 0) alone, and based soley
    on the direct question "How can one present to a function a
    list of statements to be executed?" I'd sat you already have
    it: pass a list of statements.

    In the simplest terms (w/out error trapping) ...

    (defun Evaluator ( QuotedStatementList )
    (foreach Statement QuotedStatementList
    (eval Statement)
    )
    )

    eg

    (Evaluator
    '( (princ "This is ")
    (princ "a very crude ")
    (princ "example.\n")
    (princ)
    )
    )

    Or if you need to return the result of the whole mess en masse:

    (defun Evaluator ( QuotedStatementList )
    (mapcar 'eval QuotedStatementList)
    )

    You could construct the list of statements to be passed and executed
    dynamically at run time; just a little more work.

    While this is all acedemically interesting, I still can't see why you'd
    need this, but I will admit not being able to see past my nose sometimes.

    Without a specific example it's a little difficult to provide a more
    specific response. However, Tony, who has written more parametric,
    dynamically evaluated, self modifying code than anyone in this sector
    of the universe, may stop in and illuminate.
     
    michael puckett, Apr 11, 2004
    #6
  7. Very weird timing. I sent a post, making reference to your
    expertise in this area, you post at the time mine should
    appear, and mine vanishes / never shows up. Spooky weird.

    Anyway, thanks for jumping in Tony, always enlightening.

    <snippage>
     
    michael puckett, Apr 11, 2004
    #7
  8. < this is a copy of what I sent >

    Is the K & K reference w/respect to BASIC?

    I'm not suggesting inventiveness should go out the window;
    yikes, might as well give up programming, as that's the very
    reason many of us push bits -- we get to invent every day.

    Anyway ... leaving the (setq counter 0) alone, and based soley
    on the direct question "How can one present to a function a
    list of statements to be executed?" I'd sat you already have
    it: pass a list of statements.

    In the simplest terms (w/out error trapping) ...

    (defun Evaluator ( QuotedStatementList )
    (foreach Statement QuotedStatementList
    (eval Statement)
    )
    )

    eg

    (Evaluator
    '( (princ "This is ")
    (princ "a very crude ")
    (princ "example.\n")
    (princ)
    )
    )

    Or if you need to return the result of the whole mess en masse:

    (defun Evaluator ( QuotedStatementList )
    (mapcar 'eval QuotedStatementList)
    )

    You could construct the list of statements to be passed and executed
    dynamically at run time; just a little more work.

    While this is all acedemically interesting, I still can't see why you'd
    need this, but I will admit not being able to see past my nose sometimes.

    Without a specific example it's a little difficult to provide a more
    specific response. However, Tony, who has written more parametric,
    dynamically evaluated, self modifying code than anyone in this sector
    of the universe, may stop in and illuminate.
     
    michael puckett, Apr 11, 2004
    #8
  9. TCEBob

    TCEBob Guest

    Tony, thanks for your thoughtful response. Clearly I have a lot to learn
    and it's nice to have the likes of you and Michael slip me a tip from
    time to time. I probably shouldn't have even mentioned the "for" thing;
    that was tangential, but it did confront me with the problem of
    executing a sequence of statements in a subroutine.

    Your last example,
    (for 0 10 '(lambda (val) (print (* val 100))))
    gets me right up to the edge of where I want to be. Now, suppose instead
    of (* val 100) we had a whole list of statements. Would that be enclosed
    in a (progn)? Like, (for 0 10 '(lambda (val) (setq zed (progn
    'list_of_statements))))?

    Again, the "for" issue is minor. I agree that using lisp to rewrite lisp
    is not a real good idea.

    rs
     
    TCEBob, Apr 11, 2004
    #9
  10. TCEBob

    TCEBob Guest

    Tony, Michael, I'm gonna reflect on your responses for a few hours, with
    my eyes closed.

    Hasta mañana

    rs
     
    TCEBob, Apr 11, 2004
    #10
  11. TCEBob

    Doug Broad Guest

    In addition to what Tony and Michael have said, I would suggest that
    each programming language has certain constructs that don't map
    map well to others. Yet each language can perform the same basic tasks
    well in its own way.

    With that said, it sounds like the following would be of some use. Since
    the for statement in basic takes an optional step argument and since the
    direction of the step could be down or up, this function could generate
    the list without those worrisome setq's in the calling function. ;-)
    The resulting list could be then used with either foreach or mapcar.

    (defun index (start end step / i result)
    ;;D. C. Broad, Jr.
    ;;Generates a stepped list of numbers
    (setq i start)
    (repeat (1+ (fix (abs (/ (- end start) step))))
    (setq result (cons i result))
    (setq i (+ i step))
    )
    (reverse result))

    Some sample results:
    _$ (index 10 0 -1)
    (10 9 8 7 6 5 4 3 2 1 0)
    _$ (index 10 0 -2)
    (10 8 6 4 2 0)
    _$ (index 0 5 0.5)
    (0 0.5 1.0 1.5 2.0 2.5 3.0 3.5 4.0 4.5 5.0)


    To emulate the for function with the above:

    _$ (foreach n (index 0 5 1) (print n))

    0
    1
    2
    3
    4
    5 5

    or

    _$ (mapcar '* (index 10 0 -1) (index 0 10 1))
    (0 9 16 21 24 25 24 21 16 9 0)

    The possiblities are endless. Use mapcar if the
    list should be returned. In that case the quoted function
    can be as complex as you desire and can take any number
    of arguments.

    If you do not need the result list, then use foreach, which
    has a simpler, more straightforward structure IMO.


    Regards,
    Doug
     
    Doug Broad, Apr 11, 2004
    #11
  12. Your last example,
    You're assuming that the lambda can contain only one
    expression. Not true. A lambda is just a function
    definition without a name, which appears where it is
    used. IOW, lambda allows you to define functions on
    the fly. Just like any function, lambda lists can
    contain any number of expressions, and therefore, the
    (progn) is implied:

    (for 0 10
    '(lambda (val)
    (princ (strcat "\nVal + 10 = " (itoa (+ val 10))))
    (princ (strcat "\nVal * 10 = " (itoa (* val 10))))
    (princ (strcat "\nVal * pi = " (rtos (* val pi) 2 8)))
    )
    )
     
    Tony Tanzillo, Apr 11, 2004
    #12
  13. TCEBob

    TCEBob Guest

    Good routine, Thanks.

    And now, the slats.

    rs
     
    TCEBob, Apr 11, 2004
    #13
  14. TCEBob

    TCEBob Guest

    Michael, I hope you will forgive my slow response. Thank you very much,
    the Evaluator is exactly what I was looking for. I tried a lot of
    combinations, but until now have never had a use for eval.

    rs
     
    TCEBob, Apr 13, 2004
    #14
  15. You're very welcome, I'm happy if I can help, even
    if only in small ways, many small steps ...

    Revisit Tony's code when it all starts to really gel.
    As usual, there are real GEMS in his stuff.
     
    michael puckett, Apr 14, 2004
    #15
Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments (here). After that, you can post your question and our members will help you out.