newLISP Fan Club

Forum => newLISP in the real world => Topic started by: ale870 on March 28, 2010, 03:30:36 PM

Title: dolist but getting more values at the same time
Post by: ale870 on March 28, 2010, 03:30:36 PM
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!)
Title: Re: dolist but getting more values at the same time
Post by: johu on March 29, 2010, 01:50:30 AM
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

>
Title: Re: dolist but getting more values at the same time
Post by: ale870 on March 29, 2010, 04:55:28 AM
good solution, thanks!
Title: Re: dolist but getting more values at the same time
Post by: cormullion on March 29, 2010, 09:38:41 AM
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
Title: Re: dolist but getting more values at the same time
Post by: johu on March 30, 2010, 02:07:42 AM
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.
Title: Re: dolist but getting more values at the same time
Post by: cormullion on March 30, 2010, 09:57:00 AM
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! :)
Title: Re: dolist but getting more values at the same time
Post by: ale870 on March 30, 2010, 10:04:48 AM
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.
Title: Re: dolist but getting more values at the same time
Post by: Kirill on September 29, 2011, 12:11:39 AM
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.
Title: Re: dolist but getting more values at the same time
Post by: cormullion on September 29, 2011, 05:12:48 AM
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.
Title: Re: dolist but getting more values at the same time
Post by: Kirill on September 29, 2011, 05:52:15 AM
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:



(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" ".")
                    ))
Title: Re: dolist but getting more values at the same time
Post by: rickyboy on September 29, 2011, 08:37:12 AM
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))
Title: Re: dolist but getting more values at the same time
Post by: rickyboy on September 29, 2011, 10:00:01 AM
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.
Title: Re: dolist but getting more values at the same time
Post by: Lutz on September 29, 2011, 12:35:15 PM
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



- 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



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"
Title: Re: dolist but getting more values at the same time
Post by: rickyboy on September 29, 2011, 01:27:43 PM
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

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.



(//%3C/s%3E%3CURL%20url=%22http://2.bp.blogspot.com/-LwBEe04bQAE/Tg58RY8c4RI/AAAAAAAAAMI/JeCDdk8GzGQ/s1600/IMG_1066.jpg%22%3E%3CLINK_TEXT%20text=%22http://2.bp.blogspot.com/-LwBEe04bQAE/T%20...%20G_1066.jpg%22%3Ehttp://2.bp.blogspot.com/-LwBEe04bQAE/Tg58RY8c4RI/AAAAAAAAAMI/JeCDdk8GzGQ/s1600/IMG_1066.jpg%3C/LINK_TEXT%3E%3C/URL%3E%3Ce%3E)



I know I would lose in a fight with him, so I'm not going there.  :)
Title: Re: dolist but getting more values at the same time
Post by: Kirill on September 30, 2011, 12:38:30 AM
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.
Title: Re: dolist but getting more values at the same time
Post by: TedWalther on September 30, 2011, 12:43:18 PM
Here is my solution:



(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.
Title: Re: dolist but getting more values at the same time
Post by: Lutz on September 30, 2011, 09:49:00 PM
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.




(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.
Title: Re: dolist but getting more values at the same time
Post by: rickyboy on October 02, 2011, 07:34:30 PM
Quote from: "TedWalther"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 (//http) -- 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).


Quote from: "TedWalther"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.  ;)
Title: Re: dolist but getting more values at the same time
Post by: William James on March 07, 2012, 06:45:01 PM
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.



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