Jump to content
Reu

'if progn' versus 'cond'

Recommended Posts

Reu

Is there a logical reason for using 'if' with 'progn' instead of simply using 'cond'?

Share this post


Link to post
Share on other sites
MSasu

I would say is a matter of code ergonomics; it make the code much readable when need to test a single condition (simple or multiple, that it, combined whith AND and/or OR) and action accordingly.

For sure, COND is a very powerful statement, one that many advanced programming languages lacks.

Share this post


Link to post
Share on other sites
BlackBox

... Certainly.

 

IF is helpful when one must do 'this' (aka 'then') or 'that' (aka 'else') based upon the result of the IF statement's test expression.

 

However, when you need to test for multiple scenarios, COND may be helpful... Just be prepared for the possibility of returning Nil (the last expression in the sublist), if none prior are non-Nil.

 

Given the context of an IF statement, PROGN is only needed when one wants to group two or more expressions within the 'then' or 'else' expressions.

 

HTH

Share this post


Link to post
Share on other sites
Reu
... Certainly.

 

IF is helpful when one must do 'this' (aka 'then') or 'that' (aka 'else') based upon the result of the IF statement's test expression.

 

However, when you need to test for multiple scenarios, COND may be helpful... Just be prepared for the possibility of returning Nil (the last expression in the sublist), if none prior are non-Nil.

 

Given the context of an IF statement, PROGN is only needed when one wants to group two or more expressions within the 'then' or 'else' expressions.

 

HTH

 

So if my understanding of the 'progn' syntax is correct one could set up a code to test for multiple expressions in both the 'then' and 'else' expressions? Kind of like multiple 'and' statements?

 

(if (this is true)
 (progn
   (do these things)
 )
 (progn
   (else do these things)
 )
)

Share this post


Link to post
Share on other sites
BlackBox

So if my understanding of the 'progn' syntax is correct one could set up a code to test for [evaluate] multiple expressions in both the 'then' and 'else' expressions?

 

FTFY :thumbsup:

 

Kind of like multiple 'and' statements?

 

No... The AND function requires all expressions return non-Nil, whereas PROGN does not.

Share this post


Link to post
Share on other sites
Lee Roy

Two ways to skin a cat for If/Then/Else

 

Using "(if )"

(if (<IF this is true>)
 (progn
   (<THEN do this>)
   (<and this>)
   (<and this>)
 )
 (progn
   (<ELSE do this>)
   (<and this>)
   (<and this>)
 )
)

 

Using "(cond (t ))"

(cond ((<IF this is true>)
      (<THEN do this>)
      (<and this>)
      (<and this>)
     )
     (t  
      (<ELSE do this>)
      (<and this>)
      (<and this>)
     )
)

 

I personally prefer to use "(if )" for single expression then/else statements and "(cond (t ))" for anything that requires multiple then/else statements. It's just easier for me to understand for some reason.

Share this post


Link to post
Share on other sites
MSasu

Maybe by testing the below excerpts of code in VLISP Console will help you understand better:

 

The test is evaluated as true:

(if (= 1 1)
"This is true!"
"This isn't true!"
)

The test is evaluated as false:

(if (= 1 0)
"This is true!"
"This isn't true!"
)

 

The test is evaluated as true, so expresions on first branch were evaluated one by one and the result of last evaluated one is returned:

(if (= 1 1)
(progn
 (setq a (+ 1 1))
 (setq a (+ a 1))
)
(progn
 (setq a (- 1 1))
 (setq a (- a 1))
)
)

The test is evaluated as false, so expresions on second branch were evaluated one by one and the result of last evaluated one is returned:

(if (= 1 0)
(progn
 (setq a (+ 1 1))
 (setq a (+ a 1))
)
(progn
 (setq a (- 1 1))
 (setq a (- a 1))
)
)

Share this post


Link to post
Share on other sites
BIGAL

What about repeat v's foreach ? Similar question

Share this post


Link to post
Share on other sites
Lee Mac
What about repeat v's foreach ? Similar question

 

Depends entirely on the application; sometimes repeat is more suitable, sometimes foreach.

When iterating over items of a list, foreach is almost always the most suitable; whereas, when iterating using an index, or when repeating an operation a number of times, repeat would be used. As I say, it depends on the application, there is not a 'one size fits all' answer.

Share this post


Link to post
Share on other sites
BIGAL

Thanks Lee makes sense.

Share this post


Link to post
Share on other sites
irneb

If/Cond

AFAICT if is simply there for readability. As Lee Roy's shown in post #6, the if can be implemented instead through cond. It's simply a matter of semantics. Actually if is a derived function in most programming languages, either through the use of GoTo instructions (in more C-like languages) or through cond structures (in more Lisp-like languages).

 

That's not to say all languages have the same structures though. E.g. you could compare VB's If Then ... ElseIf ... ElseIf ... Else structure to cond, but it's not fully equivalent since Lisp returns the last statement evaluated. This makes cond even more powerful, e.g.:

(setq answer (getint "Enter any integer value: "))
(princ (strcat "\nThat is a "
              (cond ((> answer 0) "positive")
                    ((< answer 0) "negative")
                    (t "ZERO"))
              " integer."))

You'd need at least a temporary variable in VB / C* / Pascal / etc. to perform the same idea, since even VB's If Then ... ElseIf ... Else structure won't return the last value as lisp's cond does.

 

There is one thing which is "missing" in AutoLisp, something equivalent to VB's select-case statement. It works a lot like cond, but due to how it works can be optimized a lot better. The reason behind this is because select operates on a specific value and then associates that value to a group of instructions. Therefore it can do the test once and immediately "know" which portion of the code to run - making for less testing if possible. It's not impossible to roll your own through lisp though. E.g.

(defun Select (value operations else / opr)
 (eval (if (setq opr (assoc value operations))
         (cadr opr)
         else)))

(setq answer (getint "Enter a integer [0/1/2/3]: "))
(princ (strcat "\nThat integer is "
              (Select answer
                      '((0 "Zero")
                        (1 "One")
                        (2 "Two")
                        (3 "Three"))
                      "not implemented in this code")
              "."))

Not as elegant as the VB select, but if Autolisp had macros / fexpr like other lisps have - it could have been made to look exactly the same or even better. No need for quotes.

 

Repeat/ForEach/MapCar/While

As Lee's pointed out it's a matter what the scenario calls for. I generally use foreach if I have to use each item in turn from a single list. MapCar works on more than one list at a time - so sometimes that's preferable to foreach, though its main purpose is to modify one or more lists into a new list containing calculated items. Repeat is simply a loop of a specified number of times, what you do inside is up to you. Similar with while, only now the condition is tested each time the loop iterates - instead of only once like in repeat (can be good if what you're doing inside is altering the condition, or bad if the condition could have been calculated beforehand - thus making it less efficient than it could have been).

 

You can perform the same thing in mapcar as you can in foreach, and in some cases as in repeat / while. You'd generally use mapcar/foreach to iterate over one or more lists. But you'd use repeat / while to do something a number of times or until some condition fails. E.g. say you want to make a list which is 10 items long each item numbered 0 to 9:

(setq lst nil n 10)
(repeat n (setq lst (cons (setq n (1- n)) lst)))

You could've done the same using while instead:

(setq lst nil n 10)
(while (>= (setq n (1- n)) 0) (setq lst (cons n lst)))

But you'd be hard pressed to use foreach/mapcar in this case unless you already had a list which was 10 items long, e.g.

;; Using foreach
(setq source '(A B C D F G H I J K) lst nil n 10)
(foreach item source (setq lst (cons (setq n (1- n)) lst)))

;; Using mapcar
(setq source '(A B C D F G H I J K) n -1)
(setq lst (mapcar '(lambda (item) (setq n (1+ n))) source))

See how convoluted it gets when you try to force the use of something where it's not meant to be used? Even worse when you try to go the other way round. Say you want to square each integer in a list, this would be the simplest using mapcar

;; Using mapcar
(setq source '(1 2 3 4 5))
(setq lst (mapcar '(lambda (i) (* i i)) source))

;; Using foreach
(setq source '(1 2 3 4 5) lst nil)
(foreach i (reverse source) (setq lst (cons (* i i) lst)))

;; Using repeat
(setq source '(1 2 3 4 5) lst nil n (length source))
(repeat n (setq lst (cons (* (nth (setq n (1- n)) source) (nth n source)) lst)))

;; Using while
(setq source '(1 2 3 4 5) lst nil n (length source))
(while (>= (setq n (1- n)) 0) (setq lst (cons (* (nth n source) (nth n source)) lst)))

Notice how foreach now requires a second list variable, and repeat / while requires a further index variable? Not to mention repeat/while could become very inefficient on long lists since the nth function would have to step through the list each time to get to the nth item.

Share this post


Link to post
Share on other sites
Lee Mac

Great examples & explanations Irneb, well done. :thumbsup:

 

P.S. This:

;; Using mapcar
(setq source '(1 2 3 4 5))
(setq lst (mapcar '(lambda (i) (* i i)) source))

Could be:

;; Using mapcar
(setq source '(1 2 3 4 5))
(setq lst (mapcar '* source source))

;)

Share this post


Link to post
Share on other sites
irneb
Great examples & explanations Irneb, well done. :thumbsup:

 

P.S. This:

;; Using mapcar
(setq source '(1 2 3 4 5))
(setq lst (mapcar '(lambda (i) (* i i)) source))

Could be:

;; Using mapcar
(setq source '(1 2 3 4 5))
(setq lst (mapcar '* source source))

;)

Thanks for the compliment, and thanks for showing the idea behind mapcar even better - indicates that you can use 2 lists, even if they're the same list :thumbsup:

Share this post


Link to post
Share on other sites
hmsilva

@irneb

@Lee Mac

 

Great explanations, many thanks to both!

 

:thumbsup::thumbsup:

 

Henrique

Share this post


Link to post
Share on other sites
irneb
@irneb

@Lee Mac

 

Great explanations, many thanks to both!

 

:thumbsup::thumbsup:

 

Henrique

You're more than welcome!

 

 

if Autolisp had macros / fexpr like other lisps have - it could have been made to look exactly the same or even better. No need for quotes.
Actually it seems there's already a standard implementation in Common Lisp called case: http://www.lispworks.com/documentation/HyperSpec/Body/m_case_.htm

http://en.wikipedia.org/wiki/Switch_statement#Common_Lisp

 

Much less verbose than the VB Select-Case idea ... more reminiscent of Pascal's case statement: http://en.wikipedia.org/wiki/Switch_statement#Pascal

 

Definitely less convoluted than C's fall-through switch-case idea: http://en.wikipedia.org/wiki/Switch_statement#C.2C_C.2B.2B.2C_D.2C_Java.2C_PHP.2C_ActionScript.2C_JavaScript

Share this post


Link to post
Share on other sites

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
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  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...