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

27/03/2006

Thinking differently

While it's fairly easy to write code in most programming languages, it's much harder to 'think' in a language - to use the essential phrases and forms that characterise and exploit that language more than another. I bet I'm not the only person who has a deplorable tendency to start writing BASIC-style programs in every language I have to write in! Someone recently observed that some of my code isn't very newLISP-y. It's true - I'm still at that point in the learning process when my first thoughts are more likely to be the type of standard construction that you'd see in a BASIC or Java program than in a Lisp-inspired solution.

Here's a good example. I wanted a function that compared two lists of the same length and returned the number of elements that were equal and in the same position. For example:

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

would score 3, because there are 3 elements that are the same and in the same position in both lists, whereas:

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

would score 0 - the elements are the same, but they're all in different places. I didn't see an existing function in newLISP that could do this job. My first attempt at a function, therefore, was this:

(define (get-score)
    (set 'total 0)
    (for (s 0 (- (length list1) 1))
        (if (= (nth s list1) (nth s list2))
            (inc 'total)))
    total)

which worked fine. I don't think there's anything particularly wrong with this definition, but, looking at it again, I would agree that it's not very newLISP-y - I've probably written something like this before, in AppleScript or BASIC. But next time I consider writing a for loop to work with lists, I should stop and ask myself whether map or apply could do a better job.

map can work through two lists and compare pairs of elements by applying a function (such as =) to each pair:

(set 'list1 '(1 2 3 4 5) 'list2 '(5 2 3 4 1))
(map = list1 list2)
;-> (nil true true true nil)

Now we can count the number of trues in the list, remembering to put the objects to count in a list:

(count '(true) (map = list1 list2))
;-> (3)

and we're almost there. To convert (3) to 3, I tried this:

(apply integer (count '(true) (map = list1 list2))

but then I switched to a simple first, and it's just as good, and a bit shorter too:

(first (count '(true) (map = list1 list2)))

This is shorter and faster than my first attempt, and looks more newLISP-y too!

(I don't know what the adjective to describe idiomatic newLISP code is: newLISPian? newLISPish? newLISP-y? newLISP-esque? Perhaps it's better to say "that's good newLISP"...)

3 Comments:

At 22:06, Anonymous Anonymous said...

Just keep your code 'your code ;-)

Though the addictive part in newlisp is..the code can always be smaler the day after...

If you like to refurnish with map,
try to rebuild this one, it contains a function called 'dirty? and i would like to see a second version of it..(I was unable to comply sofar..but who know what tomorrow brings.. ;-)

(setq depth 5)

(seed (nth 6 (now)) )
(define (dirty?) (dolist (y b) (if (index true? (map = x y)) (throw 'true)) ))
(push (setq x (randomize (sequence 1 depth))) b)

(println "\r\n--- Running!\r\n")
(setq T (time
(while (> depth (length b))
(setq x (randomize (sequence 1 depth) true))
(if (not (catch (dirty?))) (println (push x b))))
)
)
(println (last b))
(println "\r\n--- " T " milliseconds timed!\r\n")
(exit)

 
At 22:54, Blogger newlisper said...

Hi! Thanks for this. It looks like it would be hard to make it shorter or smaller!

Do you need to use index? Not ref? I'm not sure.

 
At 09:46, Anonymous Anonymous said...

Well i tried all options, seems this is it (for me ;-)
Keep up the good blogging!

 

Post a Comment

Links to this post:

Create a Link

<< Home