Jump to content

lambda , function , mapcar , apply , foreach : explained


samifox

Recommended Posts

hi

I can see mapcar, apply and foreach are used to tolorarte a list, but still not sure what is the difference. What mapcar can do that apply or foreach cant?

 

And secondly some codes use lambda and some function(). Why?

 

Thanks

Shay

Link to comment
Share on other sites

  • Replies 22
  • Created
  • Last Reply

Top Posters In This Topic

  • neophoible

    8

  • irneb

    7

  • Lee Mac

    4

  • samifox

    3

Mapcar and foreach are simply loops through a list (or in mapcar's case one or more lists). I've done a description of the main differences between the 2 here: http://www.cadtutor.net/forum/showthread.php?47026-Difference-between-foreach-and-mapcar&p=404821&viewfull=1#post404821

 

Apply, is a way of sending a list as arguments to a function. E.g. The following would give you the same result, as they do the same thing

(+ 1 2 3) ; Returns 6
(apply '+ (list 1 2 3)) ; Returns 6

The reason you'd use apply instead of simply writing the normal call is, sometimes you don't know what contents are in the list or you obtain the list from somewhere else. Note also the single-quote before the +, this is a shorthand way of writing

(apply (quote +) (list 1 2 3))

Quote is a bit difficult to understand at first. A way of thinking about it is that it sends the container and not the value. In lisp (especially AutoLisp) it's impossible to pass something "by-reference" as you can in some other languages. But the quote idea gets around that issue, by passing the container itself. That way you can even modify the container's contents. E.g. say you want to add 2 to the contents of any variable you send to your function:

(defun Add2 (var) (set var (+ (eval var) 2))

(setq n1 5 n2 3)
(Add2 'n1) ;Returns 7, but also sets n1's value to 7
(Add2 'n2) ;Returns 5, but also sets n2's value to 5

Note the use of eval. It's basically the reverse of quote. I.e. extract the value from the container.

 

Lambda, is basically the same thing as defun - except you don't need to give the function a name. I.e. it's an anonymous function. This allows you to place the definition of such function directly where it's used. Function, simply optimizes a lambda so it performs as close as possible to a real defun - since a lambda is stored as a list which needs to be evaluated, but a defun/function is stored as a somewhat compiled piece of program. Also if you're using VLIDE then you can step inside a function-lambda during debugging, but not inside a non-optimized/quoted lambda. Also function already quotes the resulting optimized lambda so it can directly be passed as an argument.

 

E.g. this would add 1 to each item in a list (not modifying the original list) and return the list of results

(setq lst (list 1 2 3)) ;Creates a variable (lst) with its contents set to (1 2 3)
(mapcar '1+ lst) ;Runs the 1+ function on each item in turn and returns (2 3 4)

But if you wanted to add 2 to each, there's no pre-made function for that. So you have to make one:

(defun 2+ (num) (+ num 2))

;; And then you can call it the same way
(mapcar '2+ lst) ; Returns (3 4 5)

But rather than make all sorts of new functions which might clutter the namespace (i.e. all the symbols known to Lisp), you can incorporate the definition of the function directly in the same line as the mapcar by using lambda:

(mapcar '(lambda (num) (+ num 2)) lst) ;Returns (3 4 5)

And then if you want it to run as fast as possible and be able to debug it in VLIDE:

(mapcar (function (lambda (num) (+ num 2))) lst) ;Returns (3 4 5)

Link to comment
Share on other sites

Mapcar and foreach are simply loops through a list (or in mapcar's case one or more lists). I've done a description of the main differences between the 2....

 

Bookmarked!

Link to comment
Share on other sites

  • 3 weeks later...
Mapcar and foreach are simply loops through a list (or in mapcar's case one or more lists). I've done a description of the main

 

Thanks a lot for this detailed explanation, ive learned a lot reading it but still i feel that i missed few things on the way...so i start building my understanding from the beginning

 

Right or wrong....appreciate any input

 

The interpreter

The interpreter reads out the AutoCAD's command line after pressing enter, if the content is wrapped with parenthesis its being delivers to Autolisp evaluation. As for the Autolisp evaluation, the interpreter deliver data for evaluation.

 

Function, arguments and expressions

Autolisp understand only specific structure or formula known as an expression. The content of an expressions are function and arguments. Function is a mean of manipulating data, and arguments are the data to be manipulated.

 

For example, In simple math its common to write a formula like this:

 

(2 +3) + (2*5)

 

What we do is the follow:

 

Calculating 2 + 3

Calculating 2*5

Adding the results

In Autolisp the same logic applies but in slightly different structure:

 

( + (+ 2 3) (*2 5)))

 

The interpreter does the following:

 

evaluate 5 as 5 and 2 as 2

applies those numbers to the * function and evaluate it as 10

evaluate 3 as 3 and 2 as 2

applies those numbers to the + function and evaluate it as 5

applies the evaluated 10 and 5 to the + function and evaluate it as 15

Autolisp evaluation

 

Evaluation is actually the result of an expression or any data within a parenthesis. Note that arguments evaluated first and then applied to the function.

 

Evaluation prevention

Autolisp evaluate everything by its nature, so there is a need to control when we don't want things to be evaluated or skipped by the interpreter.

 

To prevent evaluation we use the Quote function

 

(Quote (a b)) or '(a b)

 

Variables

 

Variables are container to hold data. A name of a variable is always the same but the value can be change by user manipulation only.

 

Data types

Integer

 

An integer is a whole number without a decimal point and its range between 32000 to -32000. If an equation that emulate to a number with a decimal point its being dropped out, for example

 

(/ 15 2) evaluate to 7

 

Integers are evaluated to themselves.

 

Real

 

Is a number with decimal point .

 

(/15 2) 7.5

 

Note that values between 0.0 and 1.0 must start with 0 or 1,

 

Not .425 but 0.425.

 

String

 

A string is a sequence of data types so called characters.

 

Lists

 

Can be treated as a container that holds all kind of data types and even other lists.

 

Assigning values to variables

Assigning values to variables is issued by two functions combination, set and quote which form one function setq .

 

set means set the value of the first argument to be equal to the evaluation of the second argument.

 

Quote means don't evaluate the first argument

 

Setq means set the value of the first argument to be equal to the evaluation of the second argument but don't evaluate the first argument.

 

(setq v 0.734)

 

The argument in the right is evaluated first and sent to the setq function which first preventing the evaluation of v and set the value of v to be equal to 0.734.

 

Another example is when we want to add value to existing vakue of a variable

 

(setq v (+ v 6))

 

The argument is evaluated first and then sent to setq function, which in turn don't evaluate the first argument but assigning the second to the first)

Link to comment
Share on other sites

You're definitely on the correct track. Some notes:

For example, In simple math its common to write a formula like this:

 

(2 +3) + (2*5)

 

What we do is the follow:

 

Calculating 2 + 3

Calculating 2*5

Adding the results

In Autolisp the same logic applies but in slightly different structure:

 

( + (+ 2 3) (*2 5)))

Yes, Lisp uses Polish notation, also referred to as Prefix-notation. Notice in pure Polish Notation you don't need any parentheses to overrule operator precedence as you do in the "normal" infix-notation. E.g. in pure polish your formula is written as:
+ + 2 3 * 2 5
--> + 5 * 2 5
--> + 5 10
--> 15

This is because pure polish means each "operator" is a binary operator and can only be applied to 2 values. Lisp allows multiple values to be passed into the "operator" (zero or more), that's why it needs to group them together and why it need parentheses in all cases.

 

So in the Lisp idea the following happens in your formula:

(+ (+ 2 3) (* 2 5))
--> (+ 5 (* 2 5))
--> (+ 5 10)
--> 15

But notice that most of these functions can receive more than 2 items. E.g. say you want to calculate the following formula:

10 + 40 / 4 / 2 + 2 * 3 * 5 + (30 - 5 - 7 - 2)
--> 10 + 10 / 2 + 6 * 5 + (25 - 7 - 2)
--> 10 + 5 + 30 + (18 - 2)
--> 15 + 30 + 16
--> 45 + 16
--> 61

The same could be written as this in polish:

+ 10 + / / 40 4 2 + * * 2 3 5 - - - 30 5 7 2
--> + 10 + / 10 2 + * 6 5 - - 25 7 2
--> + 10 + 5 + 30 - 18 2
--> + 15 30 + 16
--> + 45 16
--> 61

Similar lisp would be

(+ 10 (+ (/ (/ 40 4) 2) (+ (* (* 2 3) 5) (- (- (- 30 5) 7) 2))))

But notice, that wherever the same function calls again as one of its arguments, these can be combined into multiple argument calls:

(+ 10 (/ 40 4 2) (* 2 3 5) (- 30 5 7 2))

Link to comment
Share on other sites

samifox, you added a whole lot more to the discussion than the title would imply. Is there some reason for adding all this here? Are you trying to develop a book or something? You mention reals, but give the same example as for integers, implying AutoLISP will read your mind. It won't of course. Strings have to be in double quotes, not just a series of adjacent characters. Numbers between -1 & 1 need the lead zero, right? It seems to me that the integer limit is significantly higher than +/-32000, but I don't recall the limit just now. Perhaps someone else can provide that. It looks like its range is -2147483648 to 2147483647 inclusive on my machine.

Link to comment
Share on other sites

irneb, I hadn't really investigated Polish notation, so I didn't know it was more strict in its use of the arithmetic functions. I've always liked that LISP is able to apply them so succinctly.

Link to comment
Share on other sites

It looks like its range is -2147483648 to 2147483647 inclusive on my machine.
Yes, AutoLisp uses 32 bit signed integers. That is 2^31 = 2147483648 with the 1st bit signifying positive / negative, 0 taking one space from the positive side, thus you get -2147483648 through 2147483647.

 

Note that in general Lisp acts on integers and reals transparently. By converting the result into the most accurate / largest scoped operand E.g.

(/ 1 2) ; Returns 0 integer
(/ 1 2.0) ; Returns 0.5 real

This is a bit different from other Lisps. E.g. using some implementations of Common Lisp you would get a result which uses a fraction data type:

(/ 1 2) ;Returns (1 . 2)
(/ 4 2) ;Returns 2
(/ 32 12) ;Returns (8 . 3)

 

Also in AutoLisp an integer never grows beyond its limits. Rather you get an overflow, e.g.

(+ 2147483647 1) ;Returns -2147483648

In other Lisps you generally get a larger sized integer. E.g. the above might be converted to a 64bit signed integer result, or even a BigInt.

 

But you are correct about data types. They're not all interoperable. You can't simply add a string to a real, you need to convert either one into a compatible type. This has become a heated debate over several decades: dynamic / static variable typing. Dynamic means a variable can be assigned any type at any time throughout it's life, while static means a variable is declared to only contain a specific type and is not allowed to receive anything else.

 

The argument goes: dynamic (as in Lisp) saves programmer's time by needing less verbosity and eliminating the need to declare all variables with their types explicitly specified. Static allows for compile time type checks and makes things like IntelliSense (in Visual Studio) a lot easier. Some languages (like F#) goes a middle ground by using inferred typing - i.e. the first time a variable is used it is stuck with that type assigned to it and will error if you try to assign a different type to it later.

 

With Lisp you can assign anything to any variable, but it might error if you try to use an incompatible type in a function which expects something else. This error (due to dynamic typing) only happens once the code runs, you don't get a warning about it like you do in C# (even before you've compiled). So in Lisp you sometimes need to write some run-time type checks, e.g. using the type function, or even listp / atom / numberp.

 

irneb, I hadn't really investigated Polish notation, so I didn't know it was more strict in its use of the arithmetic functions. I've always liked that LISP is able to apply them so succinctly.
True. Even using non-strict Polish means you need to group multiple operands by using parentheses - which is what Lisp does. The only difference would be where you place these. The normal extended Polish notation is like other languages would do:
SumAll(1, 2, 3, 4)

While in lisp the parentheses surround both the function and the operands:

(SumAll 1 2 3 4)

Link to comment
Share on other sites

Evaluation prevention
While the usual use of quote is to prevent evaluation (or rather defer it until later), it's not the only use. A quoted symbol does not even need to have a value at all. E.g. the types returned by the type function is quoted symbols with no value
(setq test 123 test2 "A string")
(type test) ;Returns 'INT, but ...
(princ INT) ;Prints nil
(princ 'INT) ;Prints INT
(type test2) ;Returns 'STR, but ...
(princ STR) ;Prints nil
(princ 'STR) ;Prints STR, BUT ...
(= 'STR 'INT) ;Returns nil
(= STR INT) ;Returns T

This can be used to make much more efficient comparisons. You can use a quoted symbol as a piece of data, this means it uses less RAM but also when compared to another uses less CPU. A normal value would need to be checked for its symbol, then from that the RAM position needs to be extracted and finally the value stored in that position in RAM needs to be extracted - only then can it be compared to something else. Comparing 2 quoted symbols means 2 of those steps are eliminated.

Link to comment
Share on other sites

The argument goes: dynamic (as in Lisp) saves programmer's time by needing less verbosity and eliminating the need to declare all variables with their types explicitly specified. Static allows for compile time type checks and makes things like IntelliSense (in Visual Studio) a lot easier. Some languages (like F#) goes a middle ground by using inferred typing - i.e. the first time a variable is used it is stuck with that type assigned to it and will error if you try to assign a different type to it later.

 

With Lisp you can assign anything to any variable, but it might error if you try to use an incompatible type in a function which expects something else. This error (due to dynamic typing) only happens once the code runs, you don't get a warning about it like you do in C# (even before you've compiled). So in Lisp you sometimes need to write some run-time type checks, e.g. using the type function, or even listp / atom / numberp.

Does the argument also include a reference to getting input? In AutoLISP, nil is treated as an empty list, though its type is not LIST (inconsistency detected?). Indication of the default selection is returned as nil for everything except getstring. I would consider that a pretty simple approach, making for easy handling. Explicit input, on the other hand would most often (but not always) be of a different type than LIST, that is, user input is often of variable type for the same query, and it can even include keyword responses, all easily handled in AutoLISP.
Link to comment
Share on other sites

In AutoLISP, nil is treated as an empty list, though its type is not LIST (inconsistency detected?).
That does seem to be an inconsistency, especially since:
(listp nil) ; Returns T

But note, nil is a special case. In Lisp it's considered the same as false, an empty list and a "nothing" item. So you get

(length nil) ;Returns 0
(not nil) ;Returns T
(atom nil) ;Returns T

So IMO type is doing it correctly. Nil can be any of the types, so rather than indicate yet another type which means all it simply returns nil (as in not any type).

Link to comment
Share on other sites

So IMO type is doing it correctly. Nil can be any of the types, so rather than indicate yet another type which means all it simply returns nil (as in not any type).

 

I agree.

 

For completeness, we also have:

_$ (vl-consp nil)
nil

 

As a test for a non-empty list.

Link to comment
Share on other sites

So IMO type is doing it correctly. Nil can be any of the types, so rather than indicate yet another type which means all it simply returns nil (as in not any type).
Very interesting--I mean saying that nil can be any of the types. I'm not sure what that's supposed to mean, but if left at that, it sounds very misleading. I think the reason is obvious, as you cannot treat it as just any of the types. If you do, you will soon find out that it is not an Integer, nor a Real. Thus, numberp also does it right. And if you use nil as an argument just whenever, you will certainly generate errors. What say ye?
Link to comment
Share on other sites

Very interesting--I mean saying that nil can be any of the types. I'm not sure what that's supposed to mean, but if left at that, it sounds very misleading.

 

By 'any of the types' I think Irné meant that nil can be viewed either as an atom (as in a boolean false value), or as an empty list - i.e. any of these two types. The fact that an empty list is equivalent to a boolean false value is incredibly useful in conditional statements testing whether a list has been processed, e.g. when recursively processing a list.

Edited by Lee Mac
Link to comment
Share on other sites

Very interesting--I mean saying that nil can be any of the types. I'm not sure what that's supposed to mean, but if left at that, it sounds very misleading. I think the reason is obvious, as you cannot treat it as just any of the types. If you do, you will soon find out that it is not an Integer, nor a Real. Thus, numberp also does it right. And if you use nil as an argument just whenever, you will certainly generate errors. What say ye?
Yep, as used it's both an atom and a list.

 

I've been working at extending AutoLisp for some time now. Mostly in the research phase, but I've been investigating redoing the entire engine. And what most Lisp implementations seem to have in common is that nil is the base class type, while atom and list are derived from that. Then the other types are derivatives of those. So it actually does seem that nil is the "daddy" of all other types.

 

If you look at it from an object oriented perspective:

NIL
 - Atom
   - Number
     - Integer
     - Real
   - EName
   - etc.
 - Sequence
   - Cons
   - String
   - etc.

So while everything is a nil (like in both cars and buses are vehicles), nil is not everything (like in a vehicle is not necessarily a bus).

Link to comment
Share on other sites

Lee Mac & irneb, thanks for the explanations. :) Interesting how such a seemingly negative little guy could be the basis for the entire DNA of the LISP universe I've been enjoying so much all these years. Perhaps not too surprising, though.:thumbsup:

Link to comment
Share on other sites

Interesting how such a seemingly negative little guy could be the basis for the entire DNA of the LISP universe
8) I see nil in a similar light as zero, it makes the programming a lot simpler - in the same way as 0 did for maths after the Greek & Roman number systems were superseded by the Arabic numbers we use these days.

 

Nil is not exclusive to Lisp, though Lisp uses it much more prevalently. You do get Null in other languages like C/C++/C#/VB/Pascal, but more often than not it's only used to distinguish uninitialized variables / pointers to objects. Very seldom is it equivalent to a false, an empty collection as well as a blank value, like it is in Lisp.

Link to comment
Share on other sites

8) I see nil in a similar light as zero, it makes the programming a lot simpler - in the same way as 0 did for maths after the Greek & Roman number systems were superseded by the Arabic numbers we use these days.
Or maybe more like the Origin of origins of coordinate systems--what everything else must be referenced to.8)

 

Nil is not exclusive to Lisp, though Lisp uses it much more prevalently. You do get Null in other languages like C/C++/C#/VB/Pascal, but more often than not it's only used to distinguish uninitialized variables / pointers to objects. Very seldom is it equivalent to a false, an empty collection as well as a blank value, like it is in Lisp.
Yeah, that. In our LISP it looks more like the beginning on which to build--the primordial (of) list.;)
Link to comment
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
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...