Anonymous Monk has asked for the wisdom of the Perl Monks concerning the following question:

Hi everyone. I have a question about the Perl API.

I have a C function that manipulates an AV. Basically, it needs to push SV *'s containing unique strings into the AV, and then periodically remove an SV from the list. To remove an SV, I'd like to, um, grep it, kind of like this:

$scalar_to_remove = 'unique_string'; @array = grep({$_ ne $scalar_to_remove} @array);

How exactly can I accomplish this from C? Do I need to use perl_call_pv(sv_2mortal(newSVpvn("grep", 4))) and then do some stack magic to satisfy grep's calling conventions, then some more stack magic to copy back the resulting array? If so, is there any other simpler way to remove a scalar from an AV? A C equivalent of splice() maybe? I looked into av_delete(), but it's recent as of 5.6, which makes me suspect that it behaves just like delete() does when called on an array (undefs the scalar but doesn't remove it from the list; doesn't shrink the list).

Thanks in advance.

Replies are listed 'Best First'.
Re: Removing an element from an AV?
by tachyon (Chancellor) on Oct 07, 2004 at 00:04 UTC

    Here is one way to do it simply using av_shift and av_push iterating over the AV* It is probably not the best or fastest way. In general it is not recommended to operate on an array while iterating over it, although it can be done in certain circumstances. An alternative method, that removes the shift overhead is simply to create a new temp AV*, iterate over your original AV* extracting the values and doing the comparison and push into your new AV*. Don't forget to destroy your excess AV* or you will leak. See perlguts for all the handy macros you have available.....

    use Inline 'C'; my @ary = ('aa'..'az', 'aj'); my $rc = remove_str( \@ary, 'aj' ); print"$rc\n@ary\n"; __END__ __C__ int remove_str( SV* av_ref, SV* remove ) { AV* terms; SV* element; I32 num, i; STRLEN len; int retval = 0; terms = (AV *)SvRV(av_ref); num = av_len(terms); for( i=0; i<=num; i++ ) { element = av_shift(terms); if( strcmp( SvPV(element,len), SvPV(remove,len) ) ) av_push(terms, element); else retval++; } return retval; }

    cheers

    tachyon

      Thanks! Works perfectly.
Re: Removing an element from an AV?
by ikegami (Patriarch) on Oct 06, 2004 at 22:59 UTC

    That you want to use splice tells me you know the index you want to delete. As such, it seems the me the following will do the trick:

    AV* av; I32 i; // to delete I32 p = i; I32 c = av_len(AV) - i; while (p++, c--) { ...move from p to p-1... } ...shrink the array by one...
Re: Removing an element from an AV?
by dave_the_m (Monsignor) on Oct 06, 2004 at 23:43 UTC
    av_delete() may be what you're looking for.

    Dave.

Re: Removing an element from an AV?
by Fletch (Bishop) on Oct 07, 2004 at 01:13 UTC

    Just a thought, but if the order's not important you might could use a hash / HV* instead of an array. That'll let you maintain the list with hv_delete_ent() and get a list of the items with keys (and / or the C equivalent by building an iterator and working up your own AV*).

      Thanks, it's something to consider. It would probably be faster too, right? I may just do this.