Menu

Show posts

This section allows you to view all posts made by this member. Note that you can only see posts made in areas you currently have access to.

Show posts Menu

Messages - Jeremy Dunn

#16
Here is a conditional form that I find very useful



(define (<=> x y body1 body2 body3)
  (if (list? x)(setq x (length x)))
  (if (list? y)(setq y (length y)))
  (eval (if (< x y) body1
            (= x y) body2
            body3
        )))


This is a conditional with three bodies. X and Y can either be a list or a number. If X or Y is a list then the length of the list is used for comparison. The conditional looks at the two numbers X and Y. If X<Y body1 is executed elseif X=Y body2 is executed else body3 is executed. This happens a lot and I wish I would have thought of this a long time ago.
#17
Anything else we might add? /
February 24, 2008, 02:44:58 PM
Further fooling around made me realize I forgot to localize my variables, this I hope will be the final word:



(define-macro (nest)
  (local
     (ind funcs vars start)
     (if (setq ind (find : (map eval (args))))
       (begin
         (setq funcs (slice (args) 0 ind)
               vars  (slice (args)(+ ind 1))
               start (eval (append (list (last funcs)) vars))
               funcs (rest (reverse funcs))
         )
         (dolist (op funcs)
            (setq start (eval (list op start))))
         start
       )
       (begin
         (setq start (eval (append (list (args 1))
                                         (slice (args) 2))))
         (eval (list (args 0) start))
       ))))


This version has the added detail of enabling you to leave out the colon if you have only a single nesting such as (abs (ceil -3.4)) becoming

(nest abs ceil -3.4) rather than (nest abs ceil : -3.4). It can be written either way but I wanted to get rid of that colon for the most common nesting of just one level deep. If the colon is missing the first two arguments are considered to be functions with the rest being arguments to the 2nd function, any other case requires the colon to separate the arguments.
#18
Anything else we might add? /
February 04, 2008, 07:23:38 PM
This seems to do it



(define-macro (nest)
    (setq ind   (find : (map eval (args)))
          funcs (slice (args) 0 ind)
          vars  (slice (args)(+ ind 1))
          start (eval (append (list (last funcs)) vars))
          funcs (rest (reverse funcs))
    )
    (dolist (op funcs)
      (setq start (eval (list op  start)))
    )
    start
)
#19
Anything else we might add? /
February 03, 2008, 03:24:33 PM
Here is yet another way that one might do it where I used the function name N for NESTING rather than NOT.



(define-macro (n)
    (setq funcs (filter (fn (x)(ends-with x "&"))
                        (map rest
                            (map string
                               (map quote (filter symbol? (args))))))
          start (eval (slice (args)(length funcs)))
    )
    (dolist (op (reverse funcs))
      (setq start (eval (list (sym (trim op "" "&")) start)))
    )
    start
)


This way we are back to a functional method where instead of



(sin (abs (ceil x)))



it now becomes



(n sin& abs& ceil x)



This now becomes more like Cyril's method except that we don't have to make a pre-declaration. Perhaps this is also a reasonable compromise?
#20
I love it when I find something simple and useful. One of the things we notice with boolean functions is that they invariably are inside an If...then... expression. Now sometimes we want the truth value of the statement but many times we are only using it to decide whether we are going to use the argument that was input or not. For instance,



(if (< x)(setq z x))


In this case we do not need the truth value itself but only x. Now suppose we wrote the following function



(define-macro (rtn)
  (if (eval (args))
      (eval (args 1))))


Now we could write the previous as (setq z (rtn < x)). This eliminates always having to write the conditional statement. RTN can be used with booleans that take more than one argument. For instance,

(if (= x y)(setq z x)) would be (setq z (rtn = x y)). If the boolean test is true then the first argument of the multiple arguments is returned. The name RTN is an abbreviation of RETURN because the function returns the input when true.



Now we may not be setting the value to a variable but just using it directly in a calculation as in



(if (< x y)(* x 4))


But using RTN this becomes simply (* (rtn < x y) 4). I think you'll agree that this happens quite a lot. Hope you find it useful.
#21
Anything else we might add? /
January 31, 2008, 06:45:14 PM
I can appreciate Fanda's criticism  but I am only talking about the case where we are considering functions that take ONE argument only. Now Fanda says that something like (sin abs ceil x) becomes confusing because it all runs together. One way to handle this is simply to capitalize all functions that are not innermost as in (SIN ABS ceil x). This is simple and readable and closer to Cyril's approach which is pretty good too, but I admit that I would like to see the behavior built-in so that one doesn't have to declare anything first.
#22
Anything else we might add? / Redefine DEFINE?
January 30, 2008, 06:40:30 PM
I wanted to revisit the subject of nested expressions, particularly functions that take a single argument only. For example, we could have



(sin (abs (ceil x)))



Fanda wrote a wonderful function he called PASS that enabled us to write this as



(pass (sin abs ceil) x)



That's pretty good but could we do better? I came to thinking about this when I was writing a redefinition of the NOT function as



(define-macro (n)
 (if (true? (args 0)) nil
     (nil? (args 0)) true
     (not (eval (if (= 1 (length (args)))) (args 0) (args))))
 ))


(n x) is equivalent to (not x). The difference occurs when you write an expression like (not (= x y)). Using the N function you can now rewrite this as (n = x y). Gets rid of parentheses. After doing this it occured to me that the same principle can be applied to ANY single argument function. This leads me to suggest the following: Let us have a slight redefinition of the DEFINE function so that if we are defining a function with a single argument that it behaves in this manner if it receives more than one argument. The first argument will be assumed to be the name of a function and all further arguments are considered to be its arguments. By doing this all single argument functions that exist and that will ever be written will have the property of nesting with each other. If this is so then we can simply write



(sin (abs (ceil x)))



as the more natural



(sin abs ceil x)



without the need for any PASS type of function at all. I think this solution is better than PASS because in this case when using PASS you eliminate 2 parentheses but must type PASS for a net gain of two characters. You would need a nesting at least 4 levels deep to finally get a net gain in typing. Doing it my way results in a gain for typing at any level of nesting since the behavior is part of the functions to begin with. If DEFINE cannot be changed then perhaps we could have a DEFINE-SINGLE command to deal with single argument functions.
#23
Anything else we might add? / Suggestions for DOLIST
January 12, 2008, 02:59:53 PM
In thinking about the DOLIST command I had some ideas that might be useful. The protected symbol $idx stores the current loop index. I would suggest having two more protected symbols that would be called $length and $last. $length stores the length of the list and $last stores the last index or $length - 1. This way if I want to find out if I'm on the last index I can just write (= $idx $last).



Another possible change would be adding to the general structure. The current structure is



(dolist (sym list break) body)



I would like this changed to



(dolist (sym list break body-1st body-last body-nth) body)



This would would work as follows: The extra bodies can either be single expressions or groups of statements thrown together using BEGIN. On the 1st element of the loop the statements in body-1st are executed, on the last element of the loop the statements in body-last are excecuted and for all other elements body-nth is executed. Any operations that occur for all elements are exectued in body as normal.



Now if we have the form



(dolist (sym list break body-1st/last body-nth) body)



where we only have two bodies then the 1st body is executed for the 1st and last elements while the 2nd occurs for all others. And naturally if there are no extra bodies then just body is executed and the loop behaves as it traditionally has. I think doing things this way would get rid of a lot of repetition as it seems the user is often having to test if they are on the 1st or last elements.
#24
Anything else we might add? /
December 29, 2007, 10:31:55 AM
Just to raise a further can of worms, why should indexing begin with 0 in the positive direction and -1 in the negative? Wouldn't it be more proper to have 1,-1 or 0,-0 to be consistent? About the only thing I like about the VB language is that it gives you the option to set your indexing to 0 or 1 as you please. Almost every language seems to want to preserve this 0,-1 nonsense simply because it is tradition to have bad human factors design. I would like to be able to write something like



(set-indexing 1)



to do like VB and have the choice of indexing. As for the original issue of whether an error should be thrown if the index exceeds the list count I think it should because it does forgive error a little bit too much. In a small program it is not such a big deal but in a large one it can be devil to find sometimes. It is a nice thing to have a function that needs a positive integer perform absolute value on the input but that does not seem to be as critical as knowing where you really are in a list.
#25
newLISP newS /
December 25, 2007, 10:48:32 AM
Suggestion for a function enhancement. The example in the documentation for the APPLY function shows that



(apply op '(1 2 3 4 5) 2)



is the same as



(op (op (op (op 1 2) 3) 4) 5)



Could we enhance this to take lists of function names so that



(apply '(op2 op1) '(1 2 3 4 5) 2)



is the same as



(op2 (op1 (op2 (op1 1 2) 3) 4) 5)?



By the way, Froliche Weinachten Lutz!
#26
newLISP newS /
December 16, 2007, 11:44:20 AM
I had three small changes as suggestions for the next release, they are:



1. I wrote a function to mimic the NOT function but differ in one important respect, it could take on multiple operators. it looks like this



(define-macro (n)
 (if (= true (args 0)) nil
     (nil? (args 0)) true
     (not (eval (if (= 1 (length (args))) (args 0) (args))))
 ))

Doing it this way enables you to write (n (= 2 3)) also in the form (n = 2 3) where the inner parentheses can now be eliminated. If there are multiple arguments the function assumes that the first argument is a boolean function that is to be applied to the rest of the arguments. That is a pretty safe assumption. This change wouldn't break code for the NOT function and enhance it slightly. This might seem trivial but the addition of a lot of small things often adds up and LISP is always about stretching an abstraction to its greatest limits wherever possible.



2. Include norming within the POW function. POW currently squares a single number as input. Let POW take a single list of numbers (a vector) and return the norm of the vector. So

(pow (1 2 3)) would return (sqrt (apply add (map mul L L))) or 3.741... This would combine all of the common squaring and powering operations under one roof.



3. The DIV function currently returns the inverse 1/X of a number X as in (div 3) -> 0.333 . The second most common division operation is to divide by 2. I would suggest handing this task over to the / operator so that (/ X) becomes the same as (div X 2). I know that this is not exactly ideal since the / operator is supposed to deal with integers only but DIV is already used up for defaults.
#27
I have a situation where I want to determine if my function received +2 vs just 2 from the user. If I use "integer?" as my test both versions return true. If I try using "string" both values return "2". Is there any way to identify a positive signed integer versus a positive unsigned integer? If not, could "string" be modified to return the input literally?
#28
Anything else we might add? / Useful Math Function
November 23, 2007, 01:31:07 PM
This function is basically a counterpart to the SQRT function and serves a useful purpose. I originally had two separate functions but then realized that I could combine them both.



;; This function takes one or more numbers as arguments. If there is a single
;; number the number is squared. If there is more than one number then the square
;; root of the sum of the squares (a^2 + b^2 + c^2 + ...)^1/2 is returned.
;; Example: (sq 3) -> 9
;;          (sq 2 3) -> 3.605551275
(define-macro (sq)
  (if (= (length (args)) 1)
      (mul (args 0)(args 0))
      (sqrt (apply add (map mul (args)(args))))))
#29
Anything else we might add? /
October 12, 2007, 01:33:22 PM
Thanks all for the comments. Perhaps I should make certain things clearer about my approach. I love LISP and don't have a particular hatred of parentheses, its just that I am a minimalist at heart and am interested in the notion of how simple can code actually be. I can't believe that there is any disadvantage in reducing visual clutter wherever possible. I am a visual oriented person and to me the addition of context coloring into code editors was a godsend. Our visual systems cry out for color and 3D just to relieve boredom if nothing else. I felt that LISP of all languages was the ideal one to try this 3D approach on. Perhaps I will discover that some patches of code will block the view of other parts when written in 3D or that it is more trouble than it is worth but I have a gut feeling that seeing actual depth will provide more immediate visual feedback into overall structure than careful indenting would. HPW, there is one respect in which this approach is better for AutoCAD programmers in that you can draw instructive pictures off to the side to go along with your code. Trying to decipher a CAD program that is doing geometric operations merely from looking at the code can often be very difficult, but if you can draw a diagram in your program to show what all your variables are refering to can be an enormous help. My particular implementation is probably not the best that can be done but will be good enough for me to test the concept out and see if it has potential. Like the rest of you I have a day job and get most of my time on the weekends to devote to this. When I have something working I will give you all an update.
#30
Anything else we might add? / A 3D Text Editor?
October 10, 2007, 07:29:06 PM
I was thinking about the nature of LISP syntax and its nesting parentheses that programmers from other languages seem to go into fits over. What if we could get rid of the parentheses? I gave some considerable thought to this problem and I realized that LISP lends itself to a novel solution. What if our code writing text editor had a third axis that we could move the cursor up and down in? Imagine your normal text editor but with the main XY plane displayed in an isometric view so that it was like you were looking down on it at an angle. Imagine further that you could hit SHIFT-DOWN ARROW or SHIFT-UP ARROW to move the cursor up or down a level from the main text plane. Now imagine that every time that you type the name of a function you then move the cursor DOWN into the plane and type the rest of the arguments going up or down as needed to indicate the nesting. Comments could be written on the +1 level (above the home plane). This way we could eliminate the display of the parentheses entirely by using visual depth as a substitute. I am currently starting to write such an interface but I am doing so in AutoCAD to write AutoLISP code in a DWG file. The DWG file is read by a translator and the drawing entities in it are converted to AutoLISP code. The 3D display environment already exists so I don't have to write it. I intend to display spaces as light grey filled rectangles to add visual clues to help define the current plane that the text is on. I also intend to use this interface to write NEWLISP code as well but it would only benefit NEWLISP users that have copy of AutoCAD on their computer. A good project for you adept GUI writers out there?



What do the rest of you think of this concept? Profound? Nutty?



Short of that what can we do in boring old 2D land? One idea I had was to make LISP case sensitive so that the function names can be written in a format that makes them unique. For instance, a function name would be upper-case with at least one letter at the beginning. This way the interpreter could identify a function name and automatically assume a left parenthesis in front of it. You would still have to type the right parentheses but it would get rid of 50% of them.



My final idea was to allow the use of square brackets [] as well as parentheses. A square bracket would be equivalent to two parentheses. So an expression like (f1 (f2 (f3 (f4 x)))) could be written as

(f1 (f2 (f3 (f4 x]] . At least we could shorten some of those more massive collections of parentheses that sometimes occur at the end of a long complicated expression.



Any other ideas out there? All comments welcome.