Returning the first n elements of a list

R

Ronny

Assume that I have a function f expecting a list of arguments, and an
expression
E returning a list. Of course one way to call this function is just:
f(E)

Example: f(`ls -l foo`);

Now suppose that I want to pass to the function only the first $n
elements of E.
A naive approach (assuming $n > 0) would be

f((E)[0..($n-1)])

but this raises error messages if E contains less than $n elements. One
way out
from this dilemma would be the introduction of an auxiliary array:

my @temp=(E);
f(@temp[0..($n > $#temp ? $#temp : $n-1)]);

Needless to say that this is a very ugly solution. Can someone suggest
a more
elegant way?

Ronald
 
D

Dr.Ruud

Ronny schreef:
Now suppose that I want to pass to the function only the first $n
elements of E.
[...]
One way out
from this dilemma would be the introduction of an auxiliary array:

my @temp=(E);
f(@temp[0..($n > $#temp ? $#temp : $n-1)]);


sub min { $_[0] < $_[1] ? $_[0] : $_[1] }

and use

f( @temp[ 0 .. min( $n-1, $#temp ) ] )

or

f( @temp[ 0 .. min( $n, 0+@temp ) -1 ] )


or pass the array by reference, and take cary of it it inside f().

f(\@temp, $n)
 
M

Mirco Wahab

Thus spoke Ronny (on 2006-06-07 13:34):
Now suppose that I want to pass to the function
only the first $n elements of E [but no undefs]
f(@temp[0..($n > $#temp ? $#temp : $n-1)]);

f( grep{$_}(1..3)[0..6] );

Regards

Mirco
 
M

Mirco Wahab

Thus spoke Mirco Wahab (on 2006-06-07 14:29):
Thus spoke Ronny (on 2006-06-07 13:34):
f(@temp[0..($n > $#temp ? $#temp : $n-1)]);

f( grep{$_}(1..3)[0..6] );

must be:

f( grep{defined $_}(0,0,0)[0..5] );

because grep of course bails on 0 as on undef ...

Sorry

Mirco
 
R

Ronny

Mirco said:
Thus spoke Ronny (on 2006-06-07 13:34):
Now suppose that I want to pass to the function
only the first $n elements of E [but no undefs]
f(@temp[0..($n > $#temp ? $#temp : $n-1)]);

f( grep{$_}(1..3)[0..6] );

This works, but could you enlighten me, how?

I observed that this works:

f( grep {$_} ( (1..3)[0..6]) )

but this does not:

f( ((1..3)[0..3]) )

So what peculiar blessing does grep do onto its right argument?

Ronald
 
M

Mirco Wahab

Thus spoke Ronny (on 2006-06-07 15:05):
Mirco said:
Thus spoke Ronny (on 2006-06-07 13:34):
f(@temp[0..($n > $#temp ? $#temp : $n-1)]);
f( grep{$_}(1..3)[0..6] );

This works, but could you enlighten me, how?
I observed that this works:
f( grep {$_} ( (1..3)[0..6]) )

(OTHER_LIST) <== grep <== ( L_I_S_T )

So grep constructs onother list from a
"source" list. It "filters" the L_I_S_T
elements (each gets probed as $_) and
puts only those to the OTHER_LIST for
which the {BLOCK} return true.

And 'undef' returns false when tested in
boolean context, like
if($_ == undef) return false;

0 (zero) also return false, this is why you
have to check explicitly for 'definedness'

f( grep {defined $_} @list[0..5] );

which means: only elements which are
defined (including 0) are passed to
the other list.

Regards

Mirco
 
B

Brian McCauley

Ronny said:
Assume that I have a function f expecting a list of arguments, and an
expression
E returning a list. Of course one way to call this function is just:
f(E)

Example: f(`ls -l foo`);

Now suppose that I want to pass to the function only the first $n
elements of E.
A naive approach (assuming $n > 0) would be

f((E)[0..($n-1)])

but this raises error messages if E contains less than $n elements. One
way out
from this dilemma would be the introduction of an auxiliary array:

my @temp=(E);
f(@temp[0..($n > $#temp ? $#temp : $n-1)]);

Needless to say that this is a very ugly solution. Can someone suggest
a more elegant way?

Since the auxiliary array is expendable you can splice() it.

my @temp = E;
f( splice @temp, 0, 3 );

You can even avoid naming the auxiliary array

f( splice @{[ E ]}, 0, 3 );
 
T

Tad McClellan

Mirco Wahab said:
Thus spoke Mirco Wahab (on 2006-06-07 14:29):
Thus spoke Ronny (on 2006-06-07 13:34):
f(@temp[0..($n > $#temp ? $#temp : $n-1)]);

f( grep{$_}(1..3)[0..6] );

must be:

f( grep{defined $_}(0,0,0)[0..5] );

because grep of course bails on 0 as on undef ...


But the OP didn't say that undefs are forbidden in the middle
of the list so you are (potentially) throwing away _valid_
list elements...
 
T

Ted Zlatanov

Assume that I have a function f expecting a list of arguments, and
an expression E returning a list. Of course one way to call this
function is just: f(E)

Example: f(`ls -l foo`);

Now suppose that I want to pass to the function only the first $n
elements of E.

You should consider why you only need to pass the first few elements.
It complicates things, as you see. Why not just

my @results = `command`;
f(\@results); # passes an array reference

then in f(), use the contents of the array. Splice them if you have
to. The essential thing is that your function's call is simpler, and
the *function* will decide what it wants from the data. That way,
when you decide f() needs N+1 elements of the array, you don't have to
change something in the main program flow, only in the function
itself. IMO this helps avoid spaghetti code.

Of course, you may really want to pass only this much. Perhaps data
security is important and you don't want to pass too much. Splice,
map, and grep are helpful (as others showed). You could also do
something like this:

f(shift @results, shift @results, shift @results);

which clearly indicates, to a reader (which could be you in 5 years),
what you are doing.

Ted
 
R

Ronny

Ted said:
You should consider why you only need to pass the first few elements.
It complicates things, as you see. Why not just

my @results = `command`;
f(\@results); # passes an array reference

The problem originated from the following situation:

I do a qx(COMMAND) and want to print the first $N lines of the command
only.
Note that the COMMAND may output any number of lines, even zero.
Of course I could have done a

print (qx(COMMAND|head -n $N));

or in this case a

system(COMMAND|head -n $N)

but for various reasons I didn't want to use system and I wanted to do
the "head -n" part within Perl, and without using an auxiliary
variable.
This made me think about the problem in general, how to obtain
the first n elements of a list or of an array.

Thanks to everyone here for giving me good ideas and insights!

Ronald
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
474,196
Messages
2,571,036
Members
47,631
Latest member
kukuh

Latest Threads

Top