Page 1 of 1
dolist but getting more values at the same time
Posted: Sun Mar 28, 2010 10:30 pm
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!)
Re: dolist but getting more values at the same time
Posted: Mon Mar 29, 2010 8:50 am
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
>
Re: dolist but getting more values at the same time
Posted: Mon Mar 29, 2010 11:55 am
by ale870
good solution, thanks!
Re: dolist but getting more values at the same time
Posted: Mon Mar 29, 2010 4:38 pm
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
Re: dolist but getting more values at the same time
Posted: Tue Mar 30, 2010 9:07 am
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.
Re: dolist but getting more values at the same time
Posted: Tue Mar 30, 2010 4:57 pm
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! :)
Re: dolist but getting more values at the same time
Posted: Tue Mar 30, 2010 5:04 pm
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.
Re: dolist but getting more values at the same time
Posted: Thu Sep 29, 2011 7:11 am
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.
Re: dolist but getting more values at the same time
Posted: Thu Sep 29, 2011 12:12 pm
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.
Re: dolist but getting more values at the same time
Posted: Thu Sep 29, 2011 12:52 pm
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" "✉ km@krot.org")
; ("http://validator.w3.org/check?uri=referer" ".")
))
Re: dolist but getting more values at the same time
Posted: Thu Sep 29, 2011 3:37 pm
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))
Re: dolist but getting more values at the same time
Posted: Thu Sep 29, 2011 5:00 pm
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.
Re: dolist but getting more values at the same time
Posted: Thu Sep 29, 2011 7:35 pm
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"
Re: dolist but getting more values at the same time
Posted: Thu Sep 29, 2011 8:27 pm
by rickyboy
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.
I know I would lose in a fight with him, so I'm not going there. :)
Re: dolist but getting more values at the same time
Posted: Fri Sep 30, 2011 7:38 am
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.
Re: dolist but getting more values at the same time
Posted: Fri Sep 30, 2011 7:43 pm
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.
Re: dolist but getting more values at the same time
Posted: Sat Oct 01, 2011 4:49 am
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.
Re: dolist but getting more values at the same time
Posted: Mon Oct 03, 2011 2:34 am
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. ;)
Re: dolist but getting more values at the same time
Posted: Thu Mar 08, 2012 2:45 am
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