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

Topics - rickyboy

#21
newLISP newS / Shell Games (Substitute)
May 16, 2008, 05:38:07 PM
Since m i c h a e l was on vacation, I thought a substitute for Shell Games would be appropriate.



I call this one http://rickhanson.org/vids/tail-call-juice.mov">Tail Call Juice.



Wait for it to load -- it's not as fast as m i c h a e l's vids.  ("Wait for it ...")



And please don't shoot spitwads at the new substitute!  :-)
#22
Well, it's over now.  It was held at the Barnes and Noble coffee shop in Boca Raton, Florida, USA.  In attendance were rickyboy and lutz.  The conference went from 1030 to 1330, and many topics were covered: travel in South America, how to make a great espresso, oh and we talked about newlisp.  :-)



The door prize of one official newlisp t-shirt was won by rickyboy.  Sorry we missed you.  We hope to see you at the next conference!



(Seriously, it was my great pleasure to meet the man himself, Lutz.  We remarked at how many people might be able to make a *real* newlisp conference near Lutz's place in Florida.  Holler if you are interested.  I myself may not be prepared with talk in hand, but I always relish the good excuse for beer or coffee and good banter.)
#23
Recently I had to write an escaping throw in a newlisp program, like this:
(dolist x xs
  (catch
    (let
      ...something...
      (if condition-holds (throw 'escape))
      ...something...)))

So if the condition holds the second something won't get evaluated.



Well, the throw argument doesn't get used by the catch, in this case; so it could be any value.  The argument 'escape after a while sounded kind of boring, and since this is a whimsical crowd (after my own heart), I thought a brainstorm of useless throw arguments was in order.  The whole expression must be either as funny as possible or as much of a groaner as possible.



I'm shooting for the latter for my entries:
(throw 'tantrum)
(throw 'curveball)

You get the idea.  This one is a particular groaner:
(throw 22)

because of what ostensibly happens after a throw of 22.  (* rimshot *)  :-)



Your turn!
#24
newLISP newS / newlisp.org is down again
November 10, 2007, 01:53:07 PM
Arrggghhh!!!  So frustrating! :-(



Idea: host newlisp.org on same server as this forum (which doesn't seem to experience much down time) or one like it ...
#25
Hi there everyone!



What is The Way, or your preferred way, of querying the file system for information about the accessibility of a file?  I was looking for a portable way to do this also.



For instance, a function file-readable? which given a filename returns true or false depending on if the current process can read the file or not.  I was also looking for a file-writable? function which would be defined similarly.



Has anyone made any progress on this?  Sorry if we've talked about this before.  Thanks for any help.



--Rick
#26
I think this is a Lutz question: why does index.cgi in newLISP-wiki expand the template line by line?  In this way, it can't handle tags which are written over multiple lines (but browsers can):

<img src="Really-long-image-resource-name-that-goes-on-and-on"
     height="100" width="150" border="1"/>

I know there must be a reason, e.g. to handle very large line-delimited text.  Curious if we should change the template expansion processing or not.  Thanks!  --Rick
#27
http://www.w3.org/People/Raggett/tidy/">Tidy is a nice program which cleans up malformed (or just plain ugly) HTML.  I've always wanted access to it in newLISP without having my newLISP script run it as a command line program.  Well, today I finally saw that there was a library version of tidy (called TidyLib).



Here is a first cut of a tidy interface module for newLISP.  The interface code is based on the example C code given in http://tidy.sourceforge.net/libintro.html">http://tidy.sourceforge.net/libintro.html.  


;;;; tidy.lsp -- A module to interface TidyLib
;;;; Author: Rick Hanson
;;;; Date: 17 June 2007

(context 'tidy)

;;;---------------------------------------------------------
;;;       U S E R    C O N F I G U R A T I O N
;;;
;;; Read the desciptions of the following two variables,
;;; and change as appropriate for your needs.

;; This is the location of your TidyLib shared library
;; On Macs it's called libtidy.dylib, on Win32 machines
;; it's called libtidy.dll, on the Penguin and Unices it's
;; called libtidy.so.

(define libtidy "/usr/lib/libtidy.dylib")

;; According to Lutz, you probably don't need to change this.
;; Change it to 64, ONLY IF you know your TidyLib (and probably
;; the rest of your system + newLISP) is LP64.

(define machine-address-size-in-bits 32)

;;;---------------------------------------------------------
;;;    B O I L E R P L A T E   C O D E   F O L L O W S
;;;
;;; (meaning that, if you're
;;;     (a) just a user of this module AND
;;;     (b) you're lucky,
;;; then you won't need to change the code below this line.)
;;; :-)

(import libtidy "tidyCreate")
(import libtidy "tidyOptSetBool")
(import libtidy "tidySetErrorBuffer")
(import libtidy "tidyParseString")
(import libtidy "tidyCleanAndRepair")
(import libtidy "tidyRunDiagnostics")
(import libtidy "tidySaveBuffer")
(import libtidy "tidyBufFree")
(import libtidy "tidyRelease")
(import libtidy "tidyReleaseDate")

(define machine-address-size-in-bytes
  (/ machine-address-size-in-bits 8))
(define size-of-u_int machine-address-size-in-bytes)
(define size-of-address-pointer machine-address-size-in-bytes)

(define tidy-release-date
  (let ((pd (parse (get-string (tidy:tidyReleaseDate))))
        (months '("Month0" "January" "February" "March" "April"
                  "May" "June" "July" "August" "September"
                  "October" "November" "December")))
    (if (= (length pd) 4)
        (date-value (int (pd 3)) (find (pd 2) months) (int (pd 0)))
      (date-value (int (pd 2)) (find (pd 1) months) (int (pd 0))))))

;;; Since TidyBuffer (in buffio.h) changed on 2006-12-29, this code
;;; checks to see if your TidyLib's release date is before or
;;; on-or-after this date, and tries to do the right thing.  This
;;; would all be easier if the Tidy developers used version numbers.
;;;
;;; The right thing is the setup of the following two variables:
;;;
;;;    empty-TidyBuffer: an allocation of enough space to account
;;;    for the size of a TidyBuffer.
;;;
;;;    bp-offset: the offset from the start of the TidyBuffer
;;;    struct to struct member `bp', where the TidyLib text output
;;;    is stored.

(let ((TidyBuffer-change-date (date-value 2006 12 29)))
  (cond
    ((< tidy-release-date TidyBuffer-change-date)
     ;; struct _TidyBuffer
     ;; {
     ;;     byte* bp;           /**< Pointer to bytes */
     ;;     uint  size;         /**< # bytes currently in use */
     ;;     uint  allocated;    /**< # bytes allocated */
     ;;     uint  next;         /**< Offset of current input position */
     ;; };
     (define empty-TidyBuffer
       (dup "00" (+ size-of-address-pointer
                      (* 3 size-of-u_int))))
     (define bp-offset 0))
    (true
     ;; struct _TidyBuffer
     ;; {
     ;;     TidyAllocator* allocator;  /**< Memory allocator */
     ;;     byte* bp;           /**< Pointer to bytes */
     ;;     uint  size;         /**< # bytes currently in use */
     ;;     uint  allocated;    /**< # bytes allocated */
     ;;     uint  next;         /**< Offset of current input position */
     ;; };
     (define empty-TidyBuffer
       (dup "00" (+ (* 2 size-of-address-pointer)
                      (* 3 size-of-u_int))))
     (define bp-offset size-of-address-pointer))))

;;; The following flags are recovered from tidyenum.h of
;;; TidyLib. (Fortunately, the developers did not change the enums
;;; -- the old ones should stay the same from version to version.)

(define TidyXmlOut 22)      ; Output XML.
(define TidyXhtmlOut 23)    ; Output extensible HTML.
(define TidyHtmlOut 24)     ; Output plain HTML, even for XHTML input.
(define TidyForceOutput 64) ; Output document even if errors were found.
(define no 0)
(define yes 1)

(define (tidy:tidy output-type input)
  (let ((output empty-TidyBuffer)
        (output-contents nil)
        (errbuf empty-TidyBuffer)
        (rc -1)
        (ok nil)
        (tdoc (tidyCreate)))
    (setq ok (tidyOptSetBool tdoc output-type yes))
    (if ok (setq rc (tidySetErrorBuffer tdoc errbuf)))
    (if (>= rc 0)
        (setq rc (tidyParseString tdoc input)))
    (if (>= rc 0)
        (setq rc (tidyCleanAndRepair tdoc)))
    (if (>= rc 0)
        (setq rc (tidyRunDiagnostics tdoc)))
    (if (> rc 1)
        (setq rc (if (not (= 0 (tidyOptSetBool tdoc
                                               TidyForceOutput
                                               yes)))
                     rc -1)))
    (if (>= rc 0)
        (setq rc (tidySaveBuffer tdoc output)))
    (if (>= rc 0)
        (setq output-contents
              (get-string
               (first
                (unpack "lu"
                  (bp-offset size-of-address-pointer output)))))
      (println (format "A severe error (%d) occurred.n" rc)))
    (tidyBufFree output)
    (tidyBufFree errbuf)
    (tidyRelease tdoc)
    output-contents))

(define xml<- (curry tidy TidyXmlOut))
(define xhtml<- (curry tidy TidyXhtmlOut))
(define html<- (curry tidy TidyHtmlOut))

(context MAIN)


Here's an example (assume that the above code is in file "tidy.lsp"):
> (load "tidy.lsp")
MAIN
> (print (tidy:xhtml<- "<title>Foo</title><p>Foo!"))
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta name="generator" content=
"HTML Tidy for Mac OS X (vers 1st December 2004), see www.w3.org" />
<title>Foo</title>
</head>
<body>
<p>Foo!</p>
</body>
</html>

Enjoy! (as Norman would say)

--Rick
#28
Anything else we might add? / Mixed Radix Numbers
June 09, 2007, 03:30:11 PM
Sorry that this is not anything about guiserver.  :-)  But have you heard of this: http://en.wikipedia.org/wiki/Mixed_radix">Wikipedia:Mixed Radix?



It turns out that it is not uncommon that I use these, so I wrote a newLISP module which helps the newLISP programmer to make and use them.  Here are usage examples.


> (load "mixed-radix.lsp")
MAIN
> (mixed-radix:new HHMMSS (hours minutes seconds) (1 60 60))
HHMMSS
> (HHMMSS:to-minutes '(3 34 42))
214.7
> (HHMMSS:to-seconds '(3 34 42))
12882
> (HHMMSS:from-minutes 214.7)
(3 34 42)
> (HHMMSS:from-seconds 12882)
(3 34 42)
> (HHMMSS:+ '(3 34 42) '(1 54 59))
(5 29 41)

Recently, my real estate agent wanted to know the square footage of the finished portions of my basement, so I thought "Perfect job for newLISP!"  :-)



When the following is loaded in newLISP
;;--------------------------------------
;; Application: Floor plan lengths

(load "mixed-radix.lsp")

(mixed-radix:new ftin (feet inches) (1 12))

(define rawdims '((laundry ((5 5) (2 11) (4 6.5))
                           ((7 1.5)))
                  (bath ((4 10.5))
                        ((7 1)))
                  (family-room ((10 4.5) (8 11.5) (0 1))
                               ((12 1) (10 0) (0 1)))))

(define (dim<-rawdim rawdim)
  (list (rawdim 0)
        (apply ftin:add (rawdim 1))
        (apply ftin:add (rawdim 2))))

(define dims (map dim<-rawdim rawdims))

(define (sqft<-dim dim)
  (list (dim 0)
        (mul (ftin:to-feet (dim 1))
             (ftin:to-feet (dim 2)))))

;; Show and tell.
(println "Dimensions:")
(println dims)
(define sqftages (map sqft<-dim dims))
(println "SQ FTages:")
(println sqftages)
(println "Total SQ FT = " (apply add (map last sqftages)))

it yields
Dimensions:
((laundry (12 10.5) (7 1.5)) (bath (4 10.5) (7 1)) (family-room (
   19 5)
  (22 2)))
SQ FTages:
((laundry 91.734375) (bath 34.53125) (family-room 430.4027778))
Total SQ FT = 556.6684028

Hmm ...  Not a large area, but nonetheless correct.  Incidently, the reason why there are multiple ftins for the width or breadth dimensions of a room was because I couldn't measure the room straight across in one shot (either the length was too long for the tape measure or I had to measure around obstacles like cases or door frames) -- I figured to let newLISP due the adding.  :-)



Here is the module's code from mixed-radix.lsp.  Enjoy (as Norman would say)!  --Ricky


;;;; mixed-radix.lsp -- Mixed radix numbers for newLISP
;;;; Author: Rick Hanson
;;;; Date: 9 June 2007

(context 'mixed-radix)

;;;-------------------------------------
;;; Slots and Constructor

(define labels '())
(define bases '())

(define-macro (mixed-radix:new mrn-symbol mrn-labels mrn-bases)
  (letex (mrn-labels mrn-labels mrn-bases mrn-bases)
    (MAIN:new 'mixed-radix mrn-symbol)
    (let (ctx (eval mrn-symbol)
          unqualify (lambda (symb) (replace ".*:" (string symb) "" 0)))
      (setq ctx:labels (quote mrn-labels))
      (setq ctx:bases (quote mrn-bases))

      ;; Setup the conversion functions for new instances.
      (dolist (label ctx:labels)
        (set (sym (append "to-" (unqualify label)) ctx)
             (letex ($$idx $idx fsym (sym 'mid-units<-mixrad ctx))
               (curry fsym $$idx)))
        (set (sym (append "from-" (unqualify label)) ctx)
             (letex ($$idx $idx fsym (sym 'mixrad<-mid-units ctx))
               (curry fsym $$idx))))
      mrn-symbol)))

;;;-------------------------------------
;;; Utilities used in this context.

(define (compose)
  (apply (lambda (f g) (expand (lambda () (f (apply g (args)))) 'f 'g))
         (args) 2))

(define-macro (kurry f)
  (letex ($f (eval f)
          $cargs (map eval (args)))
    (lambda () (apply $f (append (quote $cargs) (args))))))

(define (butlast xs) (0 (- (length xs) 1) xs))

;; This version of `unfold' uses `while' and `setq' (for reasons of
;; time and space efficiency) -- "don't pay any attention to the man
;; behind the curtain!" :-)
(define (unfold p f g s post)
  (let (acc '())
    (while (not (p s))
      (push (f s) acc -1)
      (setq s (g s)))
    (post p f g s acc)))

;;;-------------------------------------
;;; Method Definitions

(define (low-units<-mixrad M (bases bases))
  "Convert a mixrad `M' to a scalar in low-order units with respect
to the list of bases `bases'."
  (rotate bases -1)
  (apply MAIN:add
    (map (lambda (i) (mul (M i) (apply mul (i bases))))
         (sequence 0 (- (length M) 1)))))

(define (high-units<-mixrad M (bases bases))
  "Convert a mixrad `M' to a scalar in high-order units with
respect to the list of bases `bases'."
  (div (low-units<-mixrad M) (apply mul bases)))

(define (mid-units<-mixrad mid M)
  "Convert a mixrad `M' to a scalar in `mid'-order units with
respect to the list of bases `bases'.  `mid' is zero-based.  This
function acts as if the radix point of `M' were after the
`mid'-th digit (from the left).  For instance to convert 3 hours,
34 minutes, 42 seconds into minutes, say
    (mixed-radix:new HHMMSS (hours minutes seconds) (1 60 60))
    (HHMMSS:mid-units<-mixrad 1 '(3 34 42))
which yields 214.7, as expected."
  (letn (mid+1 (+ mid 1)
         basesL (0 mid+1 bases)
         digitsL (0 mid+1 M)
         basesR (cons 1 (mid+1 bases))
         digitsR (cons 0 (mid+1 M)))
    (MAIN:add (low-units<-mixrad digitsL basesL)
              (high-units<-mixrad digitsR basesR))))

(define (mixrad<-low-units N (bases bases))
  "Convert `N', which is a scalar in low-order units, to a mixrad,
with respect to the list of bases 'bases'."
  (rotate bases -1)
  (unfold (lambda (s) (>= (s 1) (length bases)))
          (lambda (s) (/ (s 0) (apply mul ((s 1) bases))))
          (lambda (s) (list (mod (s 0) (apply mul ((s 1) bases))) (+ (s 1) 1)))
          ;; In the seed, keep track of the latest remainder AND an
          ;; incrementing index with which we use to slice `bases':
          (list N 0)
          ;; In the post-processor, add the last remainder into the
          ;; last entry in the accumulated list:
          (lambda (p f g s res0)
            (append (butlast res0) (list (MAIN:add (s 0) (last res0)))))))

;; This is not used by any method or instance conversion function, but
;; is here for completion sake.
(define (mixrad<-high-units N (bases bases))
  "Convert `N', which is a scalar in high-order units, to a mixrad,
with respect to the list of bases 'bases'."
  (mixrad<-low-units (apply mul (cons N bases))))

(define (mixrad<-mid-units mid N)
  "Convert `N', which is a scalar in mid-order units, to a mixrad,
with respect to the list of bases 'bases'."
  (mixrad<-low-units (apply mul (cons N ((+ mid 1) bases)))))

;; Now it's easy to define a normalization function.
(define (normalize M)
  "Return the canonical representation of mixrad `M' with respect to
the list of bases `bases'."
  (mixrad<-low-units (low-units<-mixrad M)))

(define (component-wise-operator op) (compose normalize (kurry map op)))
(define mixed-radix:add (component-wise-operator MAIN:add))
(define mixed-radix:sub (component-wise-operator MAIN:sub))
(define mixed-radix:+ mixed-radix:add)
(define mixed-radix:- mixed-radix:sub)

(context MAIN)
#29
newLISP newS / Wishlist Item: Improve curry
May 22, 2007, 07:56:48 PM
When I saw recently that Lutz had added curry as a new intrinsic procedure, I thought "way cool!"  (I still think that, BTW.)



But apparently, you can't curry a function using more than one (curry) argument:
> (define (f x y z) (* x (+ y z)))
> (define f-2-3 (curry f 2 3))
> (f-2-3 4)
value expected in function + : z
called from user defined function f
called from user defined function f-2-3

However, you can define, as a macro, your own curry functor which fixes this (you've seen this before, e.g. from John Small):
(define-macro (currie f)
  (letex ($f (eval f)
          $cargs (map eval (args)))
    (lambda () (apply $f (append (quote $cargs) (args))))))

> (define f-2-3 (curry f 2 3))
> (f-2-3 4)
14

So, I'd like for Lutz to consider changing the intrinsic curry to handle this situation.  Then I can get rid of currie! :-)
#30
Among the Code Snippets, "Add with alternating signs" can be improved by replacing the principal procedure, given currently by the following macro definition:
(define-macro (+-)
  (let (signs (cons 1 (series 1 -1 (- (length (args)) 1))))
    (apply 'add (map 'mul (map eval (args)) signs))))

by a function which does the same thing:
(define (+-)
  (let (signs (cons 1 (series 1 -1 (- (length (args)) 1))))
    (apply add (map mul signs (args)))))

In addition to shortening the punchline, the function version will execute faster.  Please let me know if I missed anything.  Thanks!  --Rick
#31
Known newLISP hater (from c.l.l.) Stephan Scholl takes a dig at newLISP in a recent blog entry (and the entry is about Arc, not newLISP):



http://www.no-spoon.de/articles/2007/04/26/everybody-talks-about-arc-again">//http://www.no-spoon.de/articles/2007/04/26/everybody-talks-about-arc-again



complete with a comment by 'ken', the one-man echo chamber.  What you should notice is that it is a "drive by" dig and no reason is given for their apparent displeasure with newLISP.  For that reason, they qualify in my book as "haters", that is people who say that something is wrong but do not have the ability, lack the courage, or are too intellectually lazy to explain their position.  (Personally, and until I see otherwise, I vote for all three reasons.)



Unsubstantiated claims like this are typically not motivated by the subject at hand (in this case newLISP), but rather childish people's attempt to demonstrate the "righteousness" of their particular view, by way of making snide and public marginalizations, in order to mask a deep-seated insecurity or lack of confidence about their own position.  Add some sad "bandwagoning" by the likes of 'ken', and you have the familiar formulae we see on bad TV programming.



Let's analyze the entry as regards newLISP (it's all in the last paragraph), shall we?



According to the Great Programming Language Genius Stephan (*genuflection*), we are "misguided people" and by implication "language zealots";  although I don't see this -- I mean after all I found his blog entry on planet lisp; why would I be reading that, as a newLISP programmer?  And get this: I know more languages than newLISP AND read about them (and still use newLISP); however, I did not check the list of approved languages issued by the Great Programming Language Genius Stephan (*genuflection*), so once again I find myself part of the "misguided people."



BUT, after all newLISP is not (*whisper*) really a Lisp (nudge, nudge, wink, wink) according to the subtle Jedi mind tricks of the Great Programming Language Genius Stephan (*genuflection*).  BUT ... maybe he is trying to tell me I'm really *not* a newLISP programmer.  Wow!  His genius knows no bounds and is so clever and deep, he is causing my brain to explode!



And Lutz, by the way, the Great Programming Language Genius Stephan (*genuflection*), by implication, compares you to Paul Graham; I'm sure the Great Programming Language Genius Stephan (*genuflection*) wants us lowly newLISP programmers to be concerned (very, very concerned, and with deeply furrowed brows at that) that you have not found the same financial success as Paul Graham (at least not so publicly).  Yet another reason not to use newLISP!  (*Pulling hair and gnashing teeth*)



OK you poor, poor "misguided" newLISP programmers: GIVE UP!!!  The Great Programming Language Genius Stephan (*genuflection*) has spoken!!



We infidels cannot deny THE TRUTH any longer!  We have transgressed and must pay!  I will set the example and get some rusty razor blades and slit my wrists now!!!  Aaaaahhhhhhh!!!!!  ;-)
#32
Hello all,



A warning if you don't already now.  If you build newLISP and then install it in a non-standard directory (e.g. install via make install_home), you will *not* be able to get the init.lsp (the one sitting in your non-standard share directory) read in at startup.  That is, you won't until you go back to the build directory and edit newlisp.c, changing the value of the C pre-processor #define macro INIT_FILE to point to the appropriate (in this case, your non-standard) directory.  After this fix and subsequent re-build and re-install, my newLISP now reads the init.lsp file.



Hope this helps and doesn't confuse the issue.  Apologies if this has already been covered.



Cheers, --Ricky
#33
newLISP in the real world / Load path?
April 20, 2007, 07:24:20 AM
Lutz?  Any one?  Is there a load path for newLISP?



I need my newLISP library routines to be loaded on demand (as opposed to all the time, e.g. init.lsp in the share directory).  And I'd like to avoid saying


(load "/path/to/rickyboys/libs/mylib.lsp")
but rather say


(load "mylib.lsp")
and have /path/to/rickyboys/libs in the "load path."  Thinking of emacs here -- so what is the newLISP way?



Thanks for any help!  --Ricky
#34
Hello everyone!



I took a shot at writing an unwind-protect macro for newLISP and I have hit the wall.  Here is the code (which doesn't work BTW):
(define-macro (unwind-protect EXPR POSTLUDE)
  (letex ($expr EXPR $postlude POSTLUDE)
    (let (result nil)
      (let (no-throw? (catch (let () $expr) 'result))
        $postlude
        (if no-throw? result (throw result))))))

I thought that I could use the return value from catch to sense if EXPR threw an exception, which I would then need to throw up the stack (after running POSTLUDE).  But it seems as if I can't control the exception  handling to this degree.



Am I missing something in my knowledge of newLISP exception handling or is an unwind-protect not possible in newLISP?  Curious.



BTW, I'd like to have an unwind-protect for various reasons; however a frequent use would be via a macro which handles file IO housekeeping, like CL's with-open-file, e.g.
(with-open-file (in "myfile" "read') bla bla bla ...)
where the file "myfile" gets automatically closed, even when a non-local exit is triggered in the blas.  (The symbol in would hold the file handle for  "myfile".)  And of course, my definition of with-open-file relies on unwind-protect.
(define-macro (with-open-file FLIST)
  (letex ($fhandle-id (FLIST 0)
          $path-file (FLIST 1)
          $access-mode (FLIST 2)
          $option (FLIST 3)
          $body (cons 'begin (args)))
    (let ($fhandle-id (open $path-file $access-mode $option))
      (unwind-protect
          $body
        (if (peek $fhandle-id) (close $fhandle-id))))))

Thanks for any help.  --Ricky
#35
newLISP newS / Help with logic
November 26, 2006, 08:09:19 PM
I've never noticed this before -- look at these two calls to 'or':
> (or nil 42)
42
> (or nil '())
nil


Oh no, I can't have this -- it screws up my code.  I need the second 'or' call to return a '(), not nil.  Although, '() and nil are both boolean false, they are not equivalent in any other sense (cf. section 8 of the manual).



Lutz, can you change the behavior of the 'or' (and the 'and' since it behaves similarly) or show me what I am doing wrong?



Thanks!  --Rick
#36
Anything else we might add? / Macro fun!
May 05, 2006, 11:59:40 AM
Don't you just love syntactic sugar?  I don't know about you but I like it so much, I've already rotted out my syntactic teeth.  (* rimshot *)



Here's a case for your consideration.  Recently, I wanted to write a macro which looked like 'dolist' but behaved like 'map'.  I decided to call it 'collect', and I wanted its usage to be such that I could do the following, for instance.


> (collect (x '(1 2 3) y '(4 5 6)) (list y x))
((4 1) (5 2) (6 3))

> (collect (x '(1 2 3) y '(4 5 6)) (println y) x)
4
5
6
(1 2 3)


Then I thought, wouldn't it be nice for the macro definition to expand in the manner that backquote does in Common Lisp?  The most important facility being the list splicing by way of the ',@' syntax.  That is, I'd like for the definition of 'collect' to have this as the punchline:


`(map (fn ,vars ,@body) ,@lists)

Then I could nix all the code I had to write, at every instance, to do the work of the splicing of 'body' and 'lists' into the 'map' expression.



The following definition is as close as I could get.


(define-macro (collect)
  (letn ((parms (args 0))
         (plen (length parms))
         (vars (list-nth (sequence 0 (- plen 1) 2) parms))
         (lists (list-nth (sequence 1 (- plen 1) 2) parms))
         (body (1 (args))))
    (comma-expand (map (fn ,vars ,@body) ,@lists))))

; where 'list-nth' is defined as:
(define (list-nth indices lisst)
  (map (fn (n) (nth n lisst)) indices))


where 'comma-expand' is like newLISP's 'expand' but instead of taking symbol arguments, it just scans the given expression for symbols preceded by ',' and ',@' and expands them accordingly.



My definition of 'comma-expand' is:


(define-macro (comma-expand form)
  (catch
   (cond ((quote? form)
          (comma-expand-func (eval form) '()))
         ((list? form)
          (eval (comma-expand-func form '())))
         (true form))))

(define (comma-expand-func form acc)
  (cond
    ((not (list? form)) form)
    ((empty? form) (reverse acc))
    ((lambda? form)
     (let ((fn-tail (map (fn (x) x) form))) ; dirty trick.
       (append (lambda) (comma-expand-func fn-tail '()))))
    ((quote? (form 0))
     (comma-expand-func
      (1 form)
      (cons (append '(quote)
                    (list (comma-expand-func (eval (form 0)) '())))
            acc)))
    ((list? (form 0))
     (comma-expand-func (1 form)
                        (cons (comma-expand-func (form 0) '())
                              acc)))
    ((= ', (form 0))
     (if (not (symbol? (form 1))) (throw 'CAN-ONLY-EXPAND-SYMBOLS))
     (let ((sym-name (name (form 1))))
       (if (= "@" (sym-name 0)) ; this means splice is required.
           (letn ((var (symbol (1 sym-name)))
                  (val (eval var)))
             (if (not (list? val)) (throw 'CAN-ONLY-SPLICE-LISTS))
             (comma-expand-func (2 form) (append (reverse val)
                                                 acc)))
         (comma-expand-func (2 form) (cons (eval (form 1)) acc)))))
    (true
     (comma-expand-func (1 form) (cons (form 0) acc)))))


Personally, I like the clarity of the definition of 'collect' (i.e. with the backquote syntax).  However, the only problem I've had is that 'collect' is now sort of slow, due to the call overhead of the "housekeeping" code.  Oh well.  Maybe if Lutz likes it we will get the present of an intrinsic (read "faster") backquoting mechanism.  I almost hate to mention it though, since it seems that lately, every time I write something to the forum, it ends up being a request for Lutz to put something on his todo list.  Sorry Lutz.
#37
If I load the contents of http://newlisp.org/index.cgi?roman_numbers_generator">//http://newlisp.org/index.cgi?roman_numbers_generator in newlisp, I can time it like this:
> (time (roman 2006) 1000)
470

A version with less call overhead, but still recursive, will yield:
(define (roman* n val rep vrlist acc)
  (if (not val)
      (roman* n ((*ROMAN* 0) 0) ((*ROMAN* 0) 1) (1 *ROMAN*) "")
      (= n 0)
      acc
      (< n val)
      (roman* n ((vrlist 0) 0) ((vrlist 0) 1) (1 vrlist) acc)
      (roman* (- n val) val rep vrlist (string acc rep))))

> (time (roman* 2006) 1000)
266

Of course, as you know, the best performing version is the one with no extra call overhead:
(define (roman** n)
  (let ((val ((*ROMAN* 0) 0))
        (rep ((*ROMAN* 0) 1))
        (vrlist (1 *ROMAN*))
        (acc ""))
    (until (= n 0)
      (cond ((< n val)
             (set 'val ((vrlist 0) 0) 'rep ((vrlist 0) 1))
             (pop vrlist))
            (true
             (set 'acc (string acc rep) 'n (- n val)))))
    acc))

> (time (roman** 2006) 1000)
94

My only problem is that the recursive ones are easier to program, read, and understand.  And it would be nice if, also, both 'roman' and 'roman*' could perform about as well as 'roman**'.  That is, Lutz, will tail recursive call optimization ever be forthcoming in newLISP?  Curious.
#38
Recall the function 'expr2xml' from http://newlisp.org/index.cgi?page=S-expressions_to_XML">//http://newlisp.org/index.cgi?page=S-expressions_to_XML:
;; translate s-expr to XML
;;
(define (expr2xml expr level)
 (cond
   ((or (atom? expr) (quote? expr))
       (print (dup "  " level))
       (println expr))
   ((list? (first expr))
       (expr2xml (first expr) (+ level 1))
       (dolist (s (rest expr)) (expr2xml s (+ level 1))))
   ((symbol? (first expr))
       (print (dup "  " level))
       (println "<" (first expr) ">")
       (dolist (s (rest expr)) (expr2xml s (+ level 1)))
       (print (dup "  " level))
       (println "</" (first expr) ">"))
   (true
      (print (dup "  " level)
      (println "<error>" (string expr) "<error>")))
 ))

It's a beautiful exposition of how you can do some powerful programming in (new)Lisp.  But we need an "ugly" version :-) that handles element attributes and childless elements.  I propose the following.
(context 'SXML)

(define (element? maybe-element)
  (and (list? maybe-element)
       (> (length maybe-element) 0)
       (symbol? (maybe-element 0))))

(define (has-attrs? maybe-element)
  (and (SXML:element? maybe-element)
       (> (length maybe-element) 1)
       (list? (maybe-element 1))
       (= '@ (maybe-element 1 0))))

(define (get-attr-string maybe-element)
  (if (SXML:has-attrs? maybe-element)
      (let ((attr-alist (1 (maybe-element 1))))
        (join (map (lambda (attr-pair)
                     (string (attr-pair 0) "="
                             """ (attr-pair 1) """))
                   attr-alist)
              " "))
    ""))

(define (return-sans-attrs maybe-element)
  (if (SXML:has-attrs? maybe-element)
      (pop maybe-element 1))
  maybe-element)

(define (childless? maybe-element)
  (= 1 (length (SXML:return-sans-attrs maybe-element))))

(define (get-children element)
  (if (SXML:has-attrs? element) (2 element) (1 element)))

(define (name-in-MAIN symbul)
  (if (starts-with (string symbul) "MAIN:")
      (name symbul)
    (string symbul)))

;; The following function is modified from 'expr2xml' in:
;;   http://newlisp.org/index.cgi?page=S-expressions_to_XML
(define (print-xml sxml level)
  (let ((level (or level 0)))
    (cond ((or (atom? sxml) (quote? sxml))
           (print (dup "  " level))
           (println sxml))
          ((list? (first sxml))
           (dolist (s sxml) (print-xml s (+ level 1))))
          ((symbol? (first sxml))
           (let ((attr-string (SXML:get-attr-string sxml))
                 (tag-name (SXML:name-in-MAIN (sxml 0))))
             (print (dup "  " level))
             (println "<" tag-name
                      (if (= attr-string "") "" " ")
                      attr-string
                      (if (SXML:childless? sxml) "/" "")
                      ">")
             (unless (SXML:childless? sxml)
               (let ((kids (SXML:get-children sxml)))
                 (dolist (k kids) (print-xml k (+ level 1)))
                 (print (dup "  " level))
                 (println "</" tag-name ">")))))
          (true
           (print (dup "  " level))
           (println "<error>" (string sxml) "<error>")))))

(context MAIN)

And the usage is something like:
(SXML:print-xml sxml)
where 'sxml' is some SXML expression, e.g. '(html (body (p "Hello, World!")))'.



Please let me know what you think.  I sometimes get "wrapped around the axle" during coding time, so I may have done things in a less than stellar way.
#39
newLISP newS / newLISP mention on c.l.l.
April 18, 2006, 05:47:42 AM
FYI, on this c.l.l. thread today/yesterday



http://groups.google.com/group/comp.lang.lisp/browse_thread/thread/18cc7a823112559c/">http://groups.google.com/group/comp.lan ... 23112559c/">http://groups.google.com/group/comp.lang.lisp/browse_thread/thread/18cc7a823112559c/



Pascal Costanza mentions newLISP.
#40
newLISP and the O.S. / IRIX port discussion
May 27, 2005, 07:23:04 AM
OK.  Taking a leaf from Peter's book, I open this discussion in the "Lisp in general" forum.



OK, first problem.  I tried a `make solaris' and, lo and behold, the call to `mmap()' in `nl-filesys.c' fails because IRIX doesn't have a `MAP_ANON' flag in its `<sys/mman.h>'.



A `man mmap' says that these are the flags it has:
MAP_SHARED       Share changes
MAP_PRIVATE      Changes are private
MAP_FIXED        Interpret addr exactly
MAP_AUTOGROW     Implicitly grow object
MAP_LOCAL        Do not share with share group
MAP_AUTORESRV    Reserve logical swap on demand
MAP_SGI_ANYADDR  Use reserved area for mappings

Any suggestions?  (I don't know how important this flag is.)  I could have deleted the flag and pressed on, but I didn't want to "spin my wheels" on something I am sort of unfamiliar with, and I assumed you had wanted me to proceed deliberately so that we could have a solid port.  Your thoughts?



Regards,  --Rick