dolist but getting more values at the same time

Started by ale870, March 28, 2010, 03:30:36 PM

Previous topic - Next topic

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

#1
A code example :


(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 :
Quote
> (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 :


(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 :


Quote> (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

#2
good solution, thanks!
--

cormullion

#3
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."


(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

#4
QuoteIf 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,


> (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.

(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

#5
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

#6
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

#7
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



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"




(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

#8
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:



(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

#9
The issue appeared as I wanted to automatically create the menu on my updated web page http://km.krot.org/">//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">//http://km.krot.org/english.



And the page structure is defined like this:



(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

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


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

#11
Addendum.  Change in code for readability.



Before:
(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):
(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. :-)
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

#12
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/newlisp_manual.html#REPL">http://www.newlisp.org/downloads/newlis ... .html#REPL">http://www.newlisp.org/downloads/newlisp_manual.html#REPL



- for the moving window have you tried using explode?


> (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/viewtopic.php?f=12&p=19608#p19608">http://newlispfanclub.alh.net/forum/vie ... 608#p19608">http://newlispfanclub.alh.net/forum/viewtopic.php?f=12&p=19608#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">http://lmf-ramblings.blogspot.com

not

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

rickyboy

#13
Quote from: "Lutz"Come on, rickyboy!  [cmd] is so yesterday! Here's how you do it:



RTFM: http://www.newlisp.org/downloads/newlisp_manual.html#REPL">http://www.newlisp.org/downloads/newlis ... .html#REPL">http://www.newlisp.org/downloads/newlisp_manual.html#REPL

Ah, very cool!  Thanks!


Quote from: "Lutz"
- for the moving window have you tried using explode?


> (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.


Quote from: "Lutz"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.



http://2.bp.blogspot.com/-LwBEe04bQAE/Tg58RY8c4RI/AAAAAAAAAMI/JeCDdk8GzGQ/s1600/IMG_1066.jpg">



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

Kirill

#14
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.