guile-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [PATCH] Avoid `SCM_VALIDATE_LIST ()'


From: Neil Jerram
Subject: Re: [PATCH] Avoid `SCM_VALIDATE_LIST ()'
Date: Mon, 1 Sep 2008 23:09:01 +0200

2008/9/1 Ludovic Courtès <address@hidden>:
> Hello,
>
> This is a followup to this discussion:
>
>  http://thread.gmane.org/gmane.lisp.guile.devel/7194
>
> The attached patch changes several list-related functions

reverse!, memq, memv, member, filter, filter!
SRFI-1: concatenate, concatenate!, member, remove, remove!

> so that they
> don't validate their input with `SCM_VALIDATE_LIST ()' since it's O(n).

I'm afraid I don't get your rationale, because all these functions are
O(n) anyway.  (For reverse*, filter* and concatenate* I believe this
is obvious.  For mem* and remove*, they should always be O(n) in
practice because it would be stupid to design a list structure where
the element being looked for or removed was not equally likely to be
anywhere along the list.)

I know you gave an example in the above-referenced thread, but IMO
that was a pathological one.  Did you find the use of
SCM_VALIDATE_LIST () causing a problem in real practical code?

> A side-effect (besides performance improvements) is that all these
> functions will now happily traverse circular lists, and will silently
> deal with dotted lists.  This is acceptable behavior IMO.

Are you sure about traversing circular lists?  From my reading of your
patch, I would expect:

(memq 'not-in-the-list some-circular-list)
=>
(don't know, still waiting...)

:-)

What do reverse* do on a circular list?  What do concatenate* do?

There are a few more specific comments below, but on balance, at the
moment, I'm seeing more disadvantages here than benefit...

Regards,
          Neil

> diff --git a/NEWS b/NEWS
> index c2bed17..cfcd43b 100644
> --- a/NEWS
> +++ b/NEWS
> @@ -56,6 +56,13 @@ When you use GDS to evaluate Scheme code from Emacs, you 
> can now use
>  This makes these internal functions technically not callable from
>  application code.
>
> +** Remove argument type checking with `list?' in some list-related functions

It's easy to read that as just "Remove argument type checking".  Could
we say "Improve scalability of some list-related functions" instead,
and leave the type checking detail to the following para?

> +
> +Several list-related functions (e.g., `memq', `list-copy', etc.) used
> +R5RS `list?' to validate their arguments.  However, `list?' has linear
> +complexity, so these functions have been changed to not resort to
> +`list?'.
> +
>  ** `guile-config link' now prints `-L$libdir' before `-lguile'
>  ** Fix memory corruption involving GOOPS' `class-redefinition'
>  ** Fix build issue on Tru64 and ia64-hp-hpux11.23 (`SCM_UNPACK' macro)
> diff --git a/libguile/list.c b/libguile/list.c
> index a1a79a4..8b0a2e4 100644
> --- a/libguile/list.c
> +++ b/libguile/list.c
> @@ -1,4 +1,4 @@
> -/* Copyright (C) 1995,1996,1997,2000,2001,2003,2004
> +/* Copyright (C) 1995,1996,1997,2000,2001,2003,2004,2008
>   * Free Software Foundation, Inc.
>   *
>   * This library is free software; you can redistribute it and/or
> @@ -367,15 +367,19 @@ SCM_DEFINE (scm_reverse_x, "reverse!", 1, 1, 0,
>           "@code{reverse!}")
>  #define FUNC_NAME s_scm_reverse_x
>  {
> -  SCM_VALIDATE_LIST (1, lst);
>    if (SCM_UNBNDP (new_tail))
>      new_tail = SCM_EOL;
>    else
> -    SCM_VALIDATE_LIST (2, new_tail);
> +    SCM_ASSERT (scm_is_pair (new_tail) || SCM_NULL_OR_NIL_P (new_tail),
> +             new_tail, 2, FUNC_NAME);

Why does new_tail need to satisfy this condition?  Please either add a
comment to explain, or just remove.

>    while (!SCM_NULL_OR_NIL_P (lst))
>      {
> -      SCM old_tail = SCM_CDR (lst);
> +      SCM old_tail;
> +
> +      SCM_VALIDATE_CONS (1, lst);
> +
> +      old_tail = SCM_CDR (lst);
>        SCM_SETCDR (lst, new_tail);
>        new_tail = lst;
>        lst = old_tail;

Note that if SCM_VALIDATE_CONS fails half way through the "list", the
input list will have been destructively modified such that it is
neither the same as when it started, nor a complete reversed list.  Is
that a concern?

> @@ -546,7 +550,8 @@ SCM_DEFINE (scm_list_copy, "list-copy", 1, 0, 0,
>    SCM * fill_here;
>    SCM from_here;
>
> -  SCM_VALIDATE_LIST (1, lst);
> +  SCM_ASSERT (scm_is_pair (lst) || SCM_NULL_OR_NIL_P (lst),
> +           lst, 1, FUNC_NAME);

Why impose this condition specifically on the head of the provided
list?  Please either explain or remove.

>
>    newlst = SCM_EOL;
>    fill_here = &newlst;
> @@ -613,8 +618,13 @@ SCM_DEFINE (scm_memq, "memq", 2, 0, 0,
>           "returned.")
>  #define FUNC_NAME s_scm_memq
>  {
> -  SCM_VALIDATE_LIST (2, lst);
> -  return scm_c_memq (x, lst);
> +  for (; !SCM_NULL_OR_NIL_P (lst); lst = SCM_CDR (lst))
> +    {
> +      SCM_VALIDATE_CONS (2, lst);
> +      if (scm_is_eq (SCM_CAR (lst), x))
> +     return lst;
> +    }
> +  return SCM_BOOL_F;
>  }
>  #undef FUNC_NAME
>
> @@ -629,9 +639,9 @@ SCM_DEFINE (scm_memv, "memv", 2, 0, 0,
>           "returned.")
>  #define FUNC_NAME s_scm_memv
>  {
> -  SCM_VALIDATE_LIST (2, lst);
>    for (; !SCM_NULL_OR_NIL_P (lst); lst = SCM_CDR (lst))
>      {
> +      SCM_VALIDATE_CONS (2, lst);
>        if (! scm_is_false (scm_eqv_p (SCM_CAR (lst), x)))
>       return lst;
>      }
> @@ -650,9 +660,9 @@ SCM_DEFINE (scm_member, "member", 2, 0, 0,
>           "empty list) is returned.")
>  #define FUNC_NAME s_scm_member
>  {
> -  SCM_VALIDATE_LIST (2, lst);
>    for (; !SCM_NULL_OR_NIL_P (lst); lst = SCM_CDR (lst))
>      {
> +      SCM_VALIDATE_CONS (2, lst);
>        if (! scm_is_false (scm_equal_p (SCM_CAR (lst), x)))
>       return lst;
>      }
> @@ -884,9 +894,11 @@ SCM_DEFINE (scm_filter, "filter", 2, 0, 0,
>    SCM walk;
>    SCM *prev;
>    SCM res = SCM_EOL;
> +
>    SCM_ASSERT (call, pred, 1, FUNC_NAME);
> -  SCM_VALIDATE_LIST (2, list);
> -
> +  SCM_ASSERT (scm_is_pair (list) || SCM_NULL_OR_NIL_P (list),
> +           list, 2, FUNC_NAME);

Same comment as above.

> +
>    for (prev = &res, walk = list;
>         scm_is_pair (walk);
>         walk = SCM_CDR (walk))
> @@ -910,9 +922,11 @@ SCM_DEFINE (scm_filter_x, "filter!", 2, 0, 0,
>    scm_t_trampoline_1 call = scm_trampoline_1 (pred);
>    SCM walk;
>    SCM *prev;
> +
>    SCM_ASSERT (call, pred, 1, FUNC_NAME);
> -  SCM_VALIDATE_LIST (2, list);
> -
> +  SCM_ASSERT (scm_is_pair (list) || SCM_NULL_OR_NIL_P (list),
> +           list, 2, FUNC_NAME);

Same comment as above.

> +
>    for (prev = &list, walk = list;
>         scm_is_pair (walk);
>         walk = SCM_CDR (walk))




reply via email to

[Prev in Thread] Current Thread [Next in Thread]