public inbox for kawa@sourceware.org
 help / color / mirror / Atom feed
From: Damien MATTEI <Damien.Mattei@unice.fr>
To: kawa@sourceware.org, "Sudarshan S Chawathe" <chaw@eip10.org>
Subject: Re: StackOverflowError in a specialized map
Date: Tue, 21 Mar 2017 14:01:00 -0000	[thread overview]
Message-ID: <201703211500.57497.Damien.Mattei@unice.fr> (raw)
In-Reply-To: <23077.1489759623@vereq.eip10.org>

yes, thank you, but i try to stay in a functional programming style and avoid "loop"
 that make code unreadable and hard to debug,
 i agree that append should be forbide too as it is require to explore the whole list to append a list
at the tail at each new element, so i rewrite it with "cons" , and i have then only a single reverse 
 on the whole list :

;; map-nil-iter-optim : a map version that exclude nil results
;; the same as map-with-lambdas but will exclude from the result list the '() result of function
;;
;; (map-nil-iter-optim + '() '(1 2 3) '(4 5 6))  -> '(5 7 9)
;; (map-nil-iter-optim (lambda (a b) (if (= a 2) '() (+ a b))) '() '(1 2 3) '(4 5 6)) -> '(5 9)
(define map-nil-iter-optim
  
  (lambda (function result list1 . more-lists)

    (letrec ((some?
	      (lambda (fct list)
		;; returns #t if (function x) returns #t for 
		;; some x in the list
		(and (pair? list)
		     (or
		      (fct (car list))
		      (some? fct (cdr list)))))))

      
      ;; variadic map implementation terminates
      ;; when any of the argument lists is empty.
      (let ((lists (cons list1 more-lists)))

	(if (some? null? lists)
	    result
	    (let* ((funct-result (apply function (map car lists))))

	      (if (null? funct-result)
		  (apply map-nil-iter-optim function result (map cdr lists))
		  (apply
		   map-nil-iter-optim
		   function
		   (cons funct-result result) ;; cons now create a reversed result list
		   (map cdr lists)))))))))



;; (map-nil-iter-optim-call +  '(1 2 3) '(4 5 6))  -> '(5 7 9)
;; (map-nil-iter-optim-call (lambda (a b) (if (= a 2) '() (+ a b))) '(1 2 3) '(4 5 6))  -> '(5 9)
(define map-nil-iter-optim-call
  (lambda (function list1 . more-lists)
    (reverse  ;; cons had created a reversed result list
     (apply map-nil-iter-optim function '() (cons list1 more-lists)))))

i'm still searching a fastest way to do that...
perheaps some funfamental routines as map* should be written with set-cdr! and loops and never touch them again.... and build functional programming on top of those low level routines...
i do not know.

Damien

Le Friday 17 March 2017 15:07:03 Sudarshan S Chawathe, vous avez écrit :
> I wrote the following, which seems to work as desired (if I understand
> the original motivation correctly) and seems simpler and more idiomatic
> to me.
> 
> It also avoids the use of 'append', which would lead to quadratic
> running time.  It also does not need the --full-tailcalls option to Kawa
> (which figures out the simple case of tail call used).  It uses SRFI 1
> for the 'any' procedure, but that dependency is easy to remove if
> desired.
> 
> (import (srfi 1))
> 
> (define (map/remove-nulls proc . lsts)
>   (let loop ((lsts lsts)
>              (result '()))
>     (if (any null? lsts)
>         (reverse result)
>         (loop (map cdr lsts)
>               (let ((proc-result (apply proc
>                                         (map car lsts))))
>                 (if (null? proc-result)
>                     result
>                     (cons proc-result
>                           result)))))))
> 
> ;; a few tests
> 
> (map/remove-nulls +  '(1 2 3) '(4 5 6))
> 
> (map/remove-nulls (lambda (a b) (if (= a 2) '() (+ a b))) '(1 2 3) '(4 5 6))
> 
> (length (map/remove-nulls (lambda (i j)
>                             (if (even? i)
>                                 '()
>                                 j))
>                           (iota (expt 10 6))
>                           (iota (expt 10 7))))
> 
> Regards,
> 
> -chaw
> 
> 
> > From: Damien MATTEI <Damien.Mattei@unice.fr>
> > To: kawa@sourceware.org
> > Subject: Re: StackOverflowError in a specialized map
> > Date: Fri, 17 Mar 2017 12:38:40 +0100
> > 
> > this and only this version works and WITH --full-tailcalls:
> > 
> > Le Thursday 16 March 2017 11:54:48 Damien MATTEI, vous avez =E9crit=A0:
> > > really hard to solve, in a recursive way....
> > >=20
> > > i have  rewrite the function with as much possible of tail recursion but =
> > it fails again:
> > >=20
> > > ;; map-nil-iter : a map version that exclude nil results
> > > ;; the same as map-with-lambdas but will exclude from the result list the=
> >  '() result of function
> > > ;;
> > > ;; (map-nil-iter + '() '(1 2 3) '(4 5 6))  -> '(5 7 9)
> > > ;; (map-nil-iter (lambda (a b) (if (=3D a 2) '() (+ a b))) '() '(1 2 3) '=
> > (4 5 6)) -> '(5 9)
> > > (define map-nil-iter
> > >=20=20=20
> > >   (lambda (function result list1 . more-lists)
> > >=20
> > >     (letrec ((some?
> > > 	      (lambda (fct list)
> > > 		;; returns #t if (function x) returns #t for=20
> > > 		;; some x in the list
> > > 		(and (pair? list)
> > > 		     (or
> > > 		      (fct (car list))
> > > 		      (some? fct (cdr list)))))))
> > >=20
> > >=20=20=20=20=20=20=20
> > >       ;; variadic map implementation terminates
> > >       ;; when any of the argument lists is empty.
> > >       (let ((lists (cons list1 more-lists)))
> > > 	(if (some? null? lists)
> > > 	    result
> > > 	    (let ((funct-result (apply function (map car lists))))
> > > 	      (if (null? funct-result)
> > > 		  (apply map-nil-iter function result (map cdr lists))
> > > 		  (apply
> > > 		   map-nil-iter
> > > 		   function
> > > 		   (append result (list funct-result))
> > > 		   (map cdr lists)))))))))
> > >=20
> > >=20
> > > ;; (map-nil-iter-call +  '(1 2 3) '(4 5 6))  -> '(5 7 9)
> > > ;; (map-nil-iter-call (lambda (a b) (if (=3D a 2) '() (+ a b))) '(1 2 3) =
> > '(4 5 6))  -> '(5 9)
> > > (define map-nil-iter-call
> > >   (lambda (function list1 . more-lists)
> > >     (apply map-nil-iter function '() (cons list1 more-lists))))
> > >=20
> > > the recursive call of map-nil-iter are no more encapsuled
> > >=20
> > > have not try the --full-tailcalls options, will do it later....
> > 
> > tested and it works=20
> > 
> > great!
> > 
> > got the idea from university course and i reread the chapter at page 27 of =
> > second edition of
> > SICP Structure and Interpretation of Computer Program (Abelson  adn Sussman=
> > )about tail recursion .... explaining how to transform a recursion in an it=
> > erative form using an accumulator to pass result on the factorial example...
> > perhaps the first time i really read this strange old book i bought 25 year=
> > s ago ...
> > 
> > hope it will works with other scheme too, seems in Scheme norm to transform=
> >  tail call in iterations automatically when possible.
> > damien
> > >=20
> > > Damien
> > >=20
> > >=20
> > > Le Wednesday 15 March 2017 17:22:50 Per Bothner, vous avez =E9crit=A0:
> > > > On 03/15/2017 08:08 AM, Damien MATTEI wrote:
> > > > > Hello,
> > > > >
> > > > > i have a custom definition of map ( https://github.com/damien-mattei/=
> > LOGIKI/blob/master/lib/map.scm at  line 392):
> > > > >
> > > > > ;; map-nil : a map version that exclude nil results
> > > > > ;; the same as map-with-lambdas but will exclude from the result list=
> >  the '() result of function
> > > > > ;;
> > > > > ;; (map-nil + '(1 2 3) '(4 5 6)) -> '(5 7 9)
> > > > > (define map-nil
> > > > >   (lambda (function list1 . more-lists)
> > > > >     (letrec ((some? (lambda (fct list)
> > > > > 		      ;; returns #f if (function x) returns #t for
> > > > > 		      ;; some x in the list
> > > > > 		      (and (pair? list)
> > > > > 			   (or (fct (car list))
> > > > > 			       (some? fct (cdr list)))))))
> > > > >
> > > > >       ;; variadic map implementation terminates
> > > > >       ;; when any of the argument lists is empty.
> > > > >       (let ((lists (cons list1 more-lists)))
> > > > > 	(if (some? null? lists)
> > > > > 	    '()
> > > > > 	    (let ((funct-result (apply function (map car lists))))
> > > > > 	      (if (null? funct-result)
> > > > > 		  (apply map-nil function (map cdr lists))
> > > > > 		  (cons funct-result
> > > > > 			(apply map-nil function (map cdr lists))))))))))
> > > > >
> > > > > i use it in various project since many years and this times it create=
> > s a java StackOverflowError on a big list (22527 elements),
> > > >=20
> > > > Not unexpected - you're recursing on a big list.
> > > >=20
> > > > > i'm not sure if it's only in kawa and java that the problem occur so
> > > > > i'm near to code this function differently,
> > > >=20
> > > > Try compiling map-nil with --full-tailcalls.   That might do it.
> > > >=20
> > > > However, if the stack overflow is in the final apply, that won't be eno=
> > ugh,
> > > > because that apply is not a tail-call (because of the cons of the resul=
> > t).
> > > >=20
> > > > You can also try replacing:
> > > >=20=20=20=20=20
> > > >      (apply map-nil function (map XXX))
> > > >=20
> > > > by splice-notation:
> > > >=20
> > > >      (map-nil function @(map XXX))
> > > >=20
> > > > However, I don't think that would be enough for Kawa to be able to inli=
> > ne the tailcalls.
> > > > That would require the splice argument to match up with the more-lists =
> > rest argument,
> > > > which it doesn't.
> > > >=20
> > > > If you don't mind using set-cdr! that would be a relatively simple and =
> > efficient solution:
> > > >=20
> > > >    (if (not (null? funct-result))
> > > >       (let ((new-cons (cons funct-result '()))
> > > >           (if (null? last-pair)
> > > >               (set! result new-cons)
> > > >               (set-cdr! last-pair new-pair))
> > > >           (set! last-pair new-cons))))
> > > >=20
> > > > Instead of using map, put the above snippet inside a for-each.
> > > >=20
> > > > (The list1 parameter seems strange, as it isn't used, as far as i can t=
> > ell.)
> > >=20
> > >=20
> > >=20
> > 
> > 
> > 
> > --=20
> > Damien.Mattei@unice.fr, Damien.Mattei@oca.eu, UNS / OCA / CNRS
> > 
> 



-- 
Damien.Mattei@unice.fr, Damien.Mattei@oca.eu, UNS / OCA / CNRS

  reply	other threads:[~2017-03-21 14:01 UTC|newest]

Thread overview: 15+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2017-03-15 15:08 Damien MATTEI
2017-03-15 16:23 ` Per Bothner
2017-03-16 10:54   ` Damien MATTEI
2017-03-17 11:38     ` Damien MATTEI
2017-03-17 14:07       ` Sudarshan S Chawathe
2017-03-21 14:01         ` Damien MATTEI [this message]
2017-03-21 15:47           ` Per Bothner
2017-03-25 22:56           ` Sudarshan S Chawathe
2017-03-26  9:49             ` Damien Mattei
2017-03-26  9:55               ` Damien Mattei
2017-03-26 10:56                 ` Damien Mattei
2017-03-28  8:42             ` Damien MATTEI
2017-03-28 14:21               ` Damien MATTEI
2017-03-17 10:37   ` Damien MATTEI
2017-03-17 16:00     ` Per Bothner

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=201703211500.57497.Damien.Mattei@unice.fr \
    --to=damien.mattei@unice.fr \
    --cc=chaw@eip10.org \
    --cc=kawa@sourceware.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).