dolist but getting more values at the same time

Q&A's, tips, howto's
Locked
ale870
Posts: 297
Joined: Mon Nov 26, 2007 8:01 pm
Location: Italy

dolist but getting more values at the same time

Post by ale870 »

Hello,

I need to read a list, but I want to get more than one value per loop cycle. This is an "abstract example":

(dolist ( (i j k) '(1 2 3 4 5 6 7 8 9) ) (println (i "-" j "-" k)))

Well, I could get something like:
1-2-3
4-5-6
7-8-9

In fact, at every loop I could "load" the variables with three values.
Is it possible with a specific function?

Thank you for your help!

(Hello to everyone, since it is some time I do not frequent this forum!)
--

johu
Posts: 143
Joined: Mon Feb 08, 2010 8:47 am

Re: dolist but getting more values at the same time

Post by johu »

A code example :

Code: Select all

(define-macro (mvdolist)
  (letex (_vars (args 0 0)
          _vals (args 0 1)
          _body (cons 'begin (1 (args))))
    (local _vars
      (dolist (_x (explode _vals (length '_vars)))
        (bind (transpose (list '_vars _x)))
        _body))))
And operation examples :
> (mvdolist ( (i j k) '(1 2 3 4 5 6 7 8 9)) (println i "-" j "-" k))
1-2-3
4-5-6
7-8-9
9
> (mvdolist ( (i j k) (sequence 1 9) ) (println i "-" j "-" k))
1-2-3
4-5-6
7-8-9
9
> (mvdolist ( (i j k) (sequence 1 5) ) (println i "-" j "-" k))
1-2-3
4-5-nil
nil
>
Unless the optional break expression as the dolist function, [exp-break].

Sorry, I did not think of a break condition.

But, if the optional break expression is not necessary,
the following function could be used :

Code: Select all

(define-macro (mvdolist)
  (letex (_varlst (map list (args 0 0))
          _vars (args 0 0)
          _vals (args 0 1)
          _flag (and (= 3 (length (args 0))) (args 0 2))
          _body (cons 'begin (1 (args))))
    (let _varlst
      (dolist (_x (if _flag _vals (explode _vals (length '_vars))))
        (bind (transpose (list '_vars _x)))
        _body))))
And examples :
> (mvdolist ( (i j k) '((1 2 3)(4 5 6)(7 8 9)) ) (println i "-" j "-" k))
(1 2 3)-(4 5 6)-(7 8 9)
(7 8 9)
> (mvdolist ( (i j k) '((1 2 3)(4 5 6)(7 8 9)) true) (println i "-" j "-" k))
1-2-3
4-5-6
7-8-9
9
> (mvdolist ( (i j k) (explode (sequence 1 9) 3) true) (println i "-" j "-" k))
1-2-3
4-5-6
7-8-9
9
> (mvdolist ( (i j k) (explode (sequence 1 9) 4) true) (println i "-" j "-" k))
1-2-3
5-6-7
9-nil-nil
nil
> (mvdolist ( (i j k) (explode (sequence 1 9) 2) true) (println i "-" j "-" k))
1-2-nil
3-4-nil
5-6-nil
7-8-nil
9-nil-nil
nil
> (mvdolist ( (i j k) '(1 2 3) true) (println i "-" j "-" k))
1-1-1
2-2-2
3-3-3
3
>

ale870
Posts: 297
Joined: Mon Nov 26, 2007 8:01 pm
Location: Italy

Re: dolist but getting more values at the same time

Post by ale870 »

good solution, thanks!
--

cormullion
Posts: 2038
Joined: Tue Nov 29, 2005 8:28 pm
Location: latiitude 50N longitude 3W
Contact:

Re: dolist but getting more values at the same time

Post by cormullion »

If one of Lutz' Laws is "Don't underrate an iterate...", then Cormullion's Corollary is "You don't always have to do lists with dolist."

Code: Select all

(set 'l (series 1 2 12))
;-> (1 2 4 8 16 32 64 128 256 512 1024 2048)

(map (fn (n) (apply add n)) (explode l 3))
;-> (7 56 448 3584)

(dolist (n (explode l 3))
  (println (apply add n)))
;->
 7
56
448
3584

; the old banger...
(do-while l 
     (println (apply add (list (pop l) (pop l) (pop l)))))

;-> 7
56
448
3584

johu
Posts: 143
Joined: Mon Feb 08, 2010 8:47 am

Re: dolist but getting more values at the same time

Post by johu »

If one of Lutz' Laws is "Don't underrate an iterate...", then Cormullion's Corollary is "You don't always have to do lists with dolist."
This is interesting.
I hope to read all words of Lutz' Laws.
Please teach me this sentence or url(?).
What is the subject that means this sentence ?
For instance, readability or speed, or philosophy , etc.

By the way,

Code: Select all

> (map (fn (x) (println (x 0) "-" (x 1) "-" (x 2))) (explode (sequence 1 9) 3))
1-2-3
4-5-6
7-8-9
(3 6 9)
> (map (curry apply (fn (i j k) (println i "-" j "-" k))) (explode (sequence 1 9) 3))
1-2-3
4-5-6
7-8-9
(3 6 9)
> 
Are these examples suitable for Cormullion's Corollary ?

I like the second example.
Then, I wrote the following macro.

Code: Select all

(define-macro (map-mv)
;(map-mv exp-functor nested-list)
   (letex (_func (args 0)
           _vals (args 1))
     (map (curry apply _func) _vals)))

> (map-mv (fn (i j k) (println i "-" j "-" k)) (explode (sequence 1 9) 3))
1-2-3
4-5-6
7-8-9
(3 6 9)
> (map-mv pow '((2 1) (2 2) (2 3)))
(2 4 8)
> 
I will add this macro to my utilities.

Thank you for advice, cormullion.

cormullion
Posts: 2038
Joined: Tue Nov 29, 2005 8:28 pm
Location: latiitude 50N longitude 3W
Contact:

Re: dolist but getting more values at the same time

Post by cormullion »

Don't worry I'm just being silly. What I'm saying is that there are always alternative ways of doing things. Lutz has sometimes observed that iteration is sometimes as good or better than recursion... And every time you type dolist you can ask yourself whether there are alternatives... That's all! :)

ale870
Posts: 297
Joined: Mon Nov 26, 2007 8:01 pm
Location: Italy

Re: dolist but getting more values at the same time

Post by ale870 »

Nice example guys! Thank you!
Everyday you teach me something more!

I know very well imperative languages, but everyday I discover how "big" (I can say "deep") are functional languages.

Thank you.
--

Kirill
Posts: 90
Joined: Wed Oct 31, 2007 1:21 pm

Re: dolist but getting more values at the same time

Post by Kirill »

Just today I was needing a multi valued dolist and it was great to find a pointer towards a more elegant solution, that I currently have.

In my case I'd need another variant of dolist with values being supplied in context, so it would return something like

Code: Select all

nil 1 2
  1 2 3
  2 3 4
  3 4 5
  4 5 6
  6 7 8
  7 8 9
  8 9 nil
I've come up with this "solution"

Code: Select all


(define-macro (mvdolist)
              (letex (_vars (args 0 0)
                            _vals (args 0 1)
                            _body (cons 'begin (1 (args))))
                     (local _vars
                            (for (_x 0 (- (length _vals) 1))
                                 (bind (transpose (list '_vars
                                                        (list
                                                          (if (= _x 0)
                                                            nil
                                                            (_vals (- _x 1)))
                                                          (_vals _x)
                                                          (if (= _x (- (length _vals) 1))
                                                            nil
                                                            (_vals (+ _x 1)))))))
                                 _body))))
I'm sure it can be improved, e.g. by storing length of _vals somewhere.

cormullion
Posts: 2038
Joined: Tue Nov 29, 2005 8:28 pm
Location: latiitude 50N longitude 3W
Contact:

Re: dolist but getting more values at the same time

Post by cormullion »

Hi Kirill, your solution is impressive!

I confess that I would probably try to avoid such a solution, because I find it a bit too advanced. I would go for a more basic approach, starting with trying to generate suitable list indices on the fly:

Code: Select all

(define (C:C)
   (if (nil? a)
       (set 'a 0)
       (inc a))
   (list a (+ a 1) (+ a 2)))

(set 'L  (sequence 100 120))
(select L (C))
;-> (100 101 102)
(select L (C))
;-> (101 102 103)
(select L (C))
;-> (102 103 104)
Your solution is much more Lispy.

Kirill
Posts: 90
Joined: Wed Oct 31, 2007 1:21 pm

Re: dolist but getting more values at the same time

Post by Kirill »

The issue appeared as I wanted to automatically create the menu on my updated web page http://km.krot.org/. The menu is defined in terms of a nested list data structure and in order to expand one item I need to know if the previous item in the list is the page user is currently viewing, as you may see by going to http://km.krot.org/english.

And the page structure is defined like this:

Code: Select all

(context 'pages)
(setq pages:pages '(
                    ("/index" "Velkommen")
                     ("/archive2010" "Arkiv")
;                    ("/archive" "Arkiv")
;                    (
;                     ("/archive2010" "Arkiv 2006–2010")
;                     )
                    ("/code" "Programkode")
                    ("/english" "<strong>English</strong>")
                    (
                     ("http://www.linkedin.com/in/kmiazin/en" "LinkedIn profile")
                     )
                    ("mailto:km@krot.org" "&#9993; km@krot.org")
;                    ("http://validator.w3.org/check?uri=referer" ".")
                    ))

rickyboy
Posts: 607
Joined: Fri Apr 08, 2005 7:13 pm
Location: Front Royal, Virginia

Re: dolist but getting more values at the same time

Post by rickyboy »

I'm with cormullion. I don't know if macros are necessary.

Code: Select all

newLISP v.10.3.3 on Win32 IPv4/6, execute 'newlisp -h' for more info.

> [cmd]
(define (make-sandwich bread meat)
  (cons bread (push bread meat -1)))

(define (generate-moving-window size xs)
  (map (lambda (i) (slice xs i size)) (sequence 0 (- (length xs) size))))

[/cmd]

> (generate-moving-window 3 (make-sandwich nil (sequence 1 9)))
((nil 1 2) (1 2 3) (2 3 4) (3 4 5) (4 5 6) (5 6 7) (6 7 8) (7 8 9) (8 9 nil))
(λx. x x) (λx. x x)

rickyboy
Posts: 607
Joined: Fri Apr 08, 2005 7:13 pm
Location: Front Royal, Virginia

Re: dolist but getting more values at the same time

Post by rickyboy »

Addendum. Change in code for readability.

Before:

Code: Select all

(define (generate-moving-window size xs)
  (map (lambda (i) (slice xs i size)) (sequence 0 (- (length xs) size))))
After (and more "newlispier", hehehe -- see Lutz, I'm learning):

Code: Select all

(define (generate-moving-window size xs)
  (map (fn (i) (i size xs)) (sequence 0 (- (length xs) size))))
Another note also -- and this is for the newer newLISP users -- making sandwiches is not ultimately destructive. :-)

Code: Select all

newLISP v.10.3.3 on Win32 IPv4/6, execute 'newlisp -h' for more info.

> (define (make-sandwich bread meat) (cons bread (push bread meat -1)))
> (define L '(1 2 3))
> (make-sandwich nil L)
(nil 1 2 3 nil)
> L
(1 2 3)
> ;; Yup.  L is still unchanged.
(λx. x x) (λx. x x)

Lutz
Posts: 5289
Joined: Thu Sep 26, 2002 4:45 pm
Location: Pasadena, California
Contact:

Re: dolist but getting more values at the same time

Post by Lutz »

Some thoughts:

- Since 10.3.0 you don't need the [cmd], [/cmd] tags anymore. Just hit enter at the prompt, then enter you multiple code lines. When entering any empty line, you get out of multiline mode and the whole thing gets evaluated.

See here: http://www.newlisp.org/downloads/newlis ... .html#REPL

- for the moving window have you tried using explode?

Code: Select all

> (dolist (item (explode (sequence 1 9) 3)) (println "-> " item))
-> (1 2 3)
-> (4 5 6)
-> (7 8 9)



Ps: regarding your question in your other post here:

http://newlispfanclub.alh.net/forum/vie ... 608#p19608

I am not lmf :), and I don't think there is one on this board. Perhaps that person is on this forum, but with a different handle. Also the link is

http://lmf-ramblings.blogspot.com
not
http://lmf-ramlbings.blogspot.com <- switched "bl"

rickyboy
Posts: 607
Joined: Fri Apr 08, 2005 7:13 pm
Location: Front Royal, Virginia

Re: dolist but getting more values at the same time

Post by rickyboy »

Lutz wrote:Come on, rickyboy! [cmd] is so yesterday! Here's how you do it:

RTFM: http://www.newlisp.org/downloads/newlis ... .html#REPL
Ah, very cool! Thanks!
Lutz wrote: - for the moving window have you tried using explode?

Code: Select all

> (dolist (item (explode (sequence 1 9) 3)) (println "-> " item))
-> (1 2 3)
-> (4 5 6)
-> (7 8 9)
Well, when the "window size" is, say, 3, can explode shift by 1, instead of 3 as in your example above? The application generate-moving-window is shifting by 1 with a "window size" of 3.
Lutz wrote:I am not lmf :), I never was lmf, and I'm not planning to be lmf. I don't where you get these crazy ideas, rickyboy.
Yes, I knew that it was not you. :) I wonder who it is though. I'd vote we torture Kazimir until he tells us who it is, but from the looks of him, I think he could throttle us all at once.

Image

I know I would lose in a fight with him, so I'm not going there. :)
(λx. x x) (λx. x x)

Kirill
Posts: 90
Joined: Wed Oct 31, 2007 1:21 pm

Re: dolist but getting more values at the same time

Post by Kirill »

Thanks all! I liked make-sandwich and generate-moving-window very much. The solution was really to add a nil to the start and end of the list. :) But to me it also was very helpful to "explore" the language by experimenting with macros.

TedWalther
Posts: 608
Joined: Mon Feb 05, 2007 1:04 am
Location: Abbotsford, BC
Contact:

Re: dolist but getting more values at the same time

Post by TedWalther »

Here is my solution:

Code: Select all

(setq lst '(a b c d e f g 1 2 3 4 5))
(dotimes (i (- (length lst) 2))
  (do-something (i 3 lst)))
The code gives you your moving window. It also lets you access the original list, in case your code needs to modify the original list. And it doesn't make duplicates of the original list, so should be more efficient.
Cavemen in bearskins invaded the ivory towers of Artificial Intelligence. Nine months later, they left with a baby named newLISP. The women of the ivory towers wept and wailed. "Abomination!" they cried.

Lutz
Posts: 5289
Joined: Thu Sep 26, 2002 4:45 pm
Location: Pasadena, California
Contact:

Re: dolist but getting more values at the same time

Post by Lutz »

This is how you could build generators for different window sizes and shifts. The generator sees the list as a circle and can go around if required.

Code: Select all

(define (make-window-generator size shift thelist)
   (expand (fn () (0 size (rotate 'thelist shift))) 'size 'shift 'thelist))

; or alternative syntax of expand uppercasing the vars to expand

(define (make-window-generator Size Shift Thelist)
   (expand (fn () (0 Size (rotate 'Thelist Shift)))))

(define gen (make-window-generator 3 -1 (sequence 0 9)))

(gen) => (1 2 3)
(gen) => (2 3 4)
(gen) => (3 4 5)
shift is negative for rotating left, positive for rotating right.

rickyboy
Posts: 607
Joined: Fri Apr 08, 2005 7:13 pm
Location: Front Royal, Virginia

Re: dolist but getting more values at the same time

Post by rickyboy »

TedWalther wrote:And it doesn't make duplicates of the original list, so should be more efficient.
This concern is not practically an issue with the function generate-moving-window. According to the manual at the section entitled Passing Data By Reference -- and this also bears out with a little testing -- if the list you pass your function is not large, "the speed difference between reference and value passing is negligible"; if your list is large enough to notice the copy, the manual recommends to pass that list by reference: "Strings and lists, which are packed in a namespace using default functors, are passed automatically by reference."

While I prefer that method, another way to avoid copying a big list in calling generate-moving-window is to change its definition from a lambda to a lambda-macro (and an accompanying letex, of course).
TedWalther wrote:It also lets you access the original list, in case your code needs to modify the original list.
Well, I don't know what to say to this, except: "Knock yourself out, brother!" :) I'm a functional programmer; so my whole outlook on programming is based on NOT doing that. ;)
(λx. x x) (λx. x x)

William James
Posts: 58
Joined: Sat Jun 10, 2006 5:34 am

Re: dolist but getting more values at the same time

Post by William James »

I enjoy using the int-reduce option in apply.
When int-reduce is 2, the behavior is like fold or reduce; when it's larger than 2, it's even more fun.

Code: Select all

> (apply (fn(_ a b c)(println a b c)) (cons nil (sequence 1 9)) 4)
123
456
789

Locked