You should be automatically redirected . If not, visit
http://newlisper.wordpress.com
and update your bookmarks.

15/08/2009

Creative functions

There's a small family of newLISP functions that can make new lists and fill them with data. These could be described as creative functions. Let's have a quick look at some of them.

dup is the plain but useful replicator function. It can produce a number of identical copies of an element, and it's handy in a wide variety of situations.

(dup {.} 20)
;-> "...................."

Use an extra true flag to make a list of short strings, rather than a single longer string:

(dup {.} 20 true)
;-> ("." "." "." "." "." "." "." "." "." "." "." "." "." "." "." "." "." "." "." ".")

You can use dup when formatting text:

(define (space (n 4)) (dup { } n))
(space 20)
;-> "                    "

normal and random are different in action yet - in a way - they both do similar jobs. random creates a list of random numbers, and normal does the same. However, the numbers returned by normal are chosen to match a pre-determined mean and standard deviation.

(normal 4 1.2 10) ; mean 4, and standard deviation 1.2
;-> (1.87421875 4.626953125 4.88359375 1.476953125 1.573046875 2.34765625 
;  5.321875 3.84765625 4.019921875 4.10078125)

(random 0 10 10) 
;-> (1.031711884 1.260753391 4.954440666 7.604752284 9.84751665 
; 9.350039866 6.844450169 3.831883312 7.497708824 3.686635417)

The ten numbers in the first list have a mean value of 4, and a standard deviation of 1.2. These numbers have a normal or Gaussian distribution. The ten numbers in the second list are randomly chosen.

sequence is a simple but smart numerical sequence generator:

(sequence 1 10)
;-> (1 2 3 4 5 6 7 8 9 10)

(sequence 1 10 1.6)
;-> (1 2.6 4.2 5.8 7.4 9)

It's also a good way of generating alphabetic sequences:

(map char (sequence (char "a") (char "z")))
;-> ("a" "b" "c" "d" "e" "f" "g" "h" "i" "j" "k" "l" "m" "n" "o" "p" "q" 
; "r" "s" "t"  "u" "v" "w" "x" "y" "z")

But probably the most powerful and flexible member of this creative family is series. This has two forms, a simple numerical form that generates a geometric series, and a more general functional form.

(series 1 2 10) ; start at  1, factor 2
;-> (1 2 4 8 16 32 64 128 256 512)

The functional form takes a starting expression, a function that generates the next element from the current one, and the number of elements required.

(series 1 inc 10)
;-> (1 2 3 4 5 6 7 8 9 10)

Here, the function inc is applied repeatedly, first to the starting element, and then to the result of each application, until the required number of elements has been generated. The result here is the same as the sequence call above.

The starting element doesn't have to be a number, it could be any expression. All that's required is for the function to be able to generate the 'next' element in the series given the current element.

(series "123456789" chop 10)
;-> ("123456789" "12345678" "1234567" "123456" "12345" "1234" "123" "12" "1" "")

Our code for generating the alphabet can also be written as follows. An inline anonymous function calculates the next letter given the current one:

(series "a" (fn (n) (char (inc (char n)))) 26) ; alphabet
;-> ("a" "b" "c" "d" "e" "f" "g" "h" "i" "j" "k" "l" "m" "n" "o" 
; "p" "q" "r" "s" "t" "u" "v" "w" "x" "y" "z")

The next element is easily found by incrementing the ASCII code of the current element.

As with many of newLISP's iteration and mapping functions, you can access a counter to find out how far you've got:

(series 1 (fn (f) (* f (+ 2 $idx))) 20)  ; factorials
;-> (1 2 6 24 120 720 5040 40320 362880 3628800 39916800 479001600 
; 6227020800 87178291200 1307674368000 20922789888000 355687428096000 
; 6402373705728000 121645100408832000 2432902008176640000)

$idx returns the 'number' of the current element being calculated. It starts at 0, though, so you might want to move it up a bit, as here.

To add a day - a day's worth of seconds - to a date:

(series (date-value) (curry + 86400) 10)
;-> (1250112127 1250198527 1250284927 1250371327 1250457727 
; 1250544127 1250630527 1250716927 1250803327 1250889727)

Notice that we can't simply say (+ 86400) here, because that evaluates to 86400, which makes newLISP evaluate the expression as a simple numerical series rather than a functional one. By saying (curry + 86400), we're making a new function that adds 86400 to its argument, and it's this function which is applied to each value.

(map date (series (date-value) (curry + 86400) 10))
;-> ("Wed Aug 12 22:22:24 2009" "Thu Aug 13 22:22:24 2009" 
; "Fri Aug 14 22:22:24 2009" "Sat Aug 15 22:22:24 2009" 
; "Sun Aug 16 22:22:24 2009" "Mon Aug 17 22:22:24 2009"
; "Tue Aug 18 22:22:24 2009" "Wed Aug 19 22:22:24 2009" 
; "Thu Aug 20 22:22:24 2009" "Fri Aug 21 22:22:24 2009")

series is versatile enough that it can do the jobs of some of the other creative functions, such as dup:

(dup "fred" 6 true)
;-> ("fred" "fred" "fred" "fred" "fred" "fred")

(series "fred" copy 6)
;-> ("fred" "fred" "fred" "fred" "fred" "fred")

That copy looks at home here, but in fact any function that returns the same element when applied would work just as well:

(series "fred" begin 6)
;-> ("fred" "fred" "fred" "fred" "fred" "fred")

because:

(begin "fred")
;=> "fred"

Also:

(series "fred" string 6)
;-> ("fred" "fred" "fred" "fred" "fred" "fred")

And what about that most famous of numeric sequences, the Fibonacci series? Can series do it? With a little thought, yes:

(set 'p 0)
(series 1 (fn (n) (set 'q (+ n p)) (set 'p n) q) 20)
;-> (1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597 2584 4181 6765)

We've had to use temporary variables to store the previous values. In the inline function, notice that the finishing value, fed to the next iteration, is q.

0 Comments:

Post a Comment

Links to this post:

Create a Link

<< Home