tags:

views:

279

answers:

8

I'm trying to translate a Perl script to PHP and I'm having trouble with some Perl things. For instance, what does @_ -1 mean? And how do I write it in PHP?

The whole function is as follows:

sub variance {
    my $sum = sum_squares (@_);
    my $deg = @_ - 1;
    return $sum/$deg;
}
+9  A: 

In this case,@_ is the arguments passed to the subroutine, as a list.

Taken in scalar context, it is the number of elements in that list.

So if you call: variance('a', 'b', 'c', 'd');, $deg will be 3.

David Dorward
Thanks! That helped a lot!
Alex
`@_` is an array, not a list.
eugene y
To be pedantic: @_ is an array containing the arguments that get passed to the subroutine as a list.
fB
+1  A: 

@_ is the list of parameters being paassed into the subroutine. When you use it in a non-scalar context such as:

sum_squares (@_)

you get the list, so presumably that would return the sum of the squares of all the numbers in that list (see comment below however).

When used in a scalar context, you get the length so in your case it would be the number of items in the $@ function list.

So your variance function is probably (I say probably since I don't have access to the sum_squares source code) calculating:

variance = arr[0]^2 + arr[1]^2 + ... + arr[N-1]^2
           --------------------------------------
                            N - 1

on the array of values being passed into your function.

I have to say that my understanding of variance is at odds with that (thouugh it's been a while since I did any stats). I'm pretty certain it's supposed to involve the sum of the squares of the differences between the values and the mean, not the values themselves. I suspect there's more happening inside sum_squares than its simple name implies.

paxdiablo
+2  A: 

It's the number of arguments, passed to the variance subroutine minus one (or index of the last element in the @_ array). @_ is used in scalar context here.

eugene y
+7  A: 

Like already said, it is the array of arguments passed to the function. It's equivalent in PHP would be

  • func_get_args() — Returns an array comprising a function's argument list
  • func_num_args() — Returns the number of arguments passed to the function

The entire function would be

function variance() {
    $sum = sum_squares(func_get_args());
    $deg = func_num_args() - 1;
    return $sum/$deg;
}
// echo variance(1,2,3,4,5); // 13.75 (55/4)

For sum_squares, there is no native function in PHP. Assuming it does what the name implies, e.g. raising each argument to the power of 2 and then summing the result, you could implement it as

function sum_squares(array $args) {
    return array_reduce(
        $args, create_function('$x, $y', 'return $x+$y*$y;'), 0
    );
}
// echo sum_squares( array(1,2,3,4,5) ); // 55

Replace create_function with a proper lambda if you are on PHP5.3

Gordon
This function throws back an error. (See the whole code above)
Alex
@Alex what error? call to undefined function sum_squares? Of course it does. It's a userland function and you have to define it somewhere. See update above for a possible implementation.
Gordon
@Gordon yes, that one. I defined it.Here is the php code: function mean($array) { return array_sum($array) / count($array); } function sum_squares($array) { $sum = 0; $mean = mean($array); foreach ($array as $key => $val) { $sum += pow($val - $mean, 2); } return $sum; }Using these functions, your version of the variance function throws back an error.
Alex
@Alex I would highly appreciate if you would add the error message. Just saying it doesn't work or it throws an error tells nothing about what might be wrong. When I use the functions you gave in the comment above, `variance(1,2,3,4,5)` will return 2.5, without an error.
Gordon
True, my bad! Here it is:Fatal error: Unsupported operand types in Document on line 20.Line 20 is this: $sum += pow($val - $mean, 2);
Alex
@Alex this is a line from your own `sum_squares` function. It is not the `variance` function that is causing the error. The `variance` function is functionally equivalent to it's Perl counterpart as given in your question. You are probably invoking it `variance(array(1,2,3,4,5))`, which will obviously cause the error you mention because func_get_args() will return **an array comprising a function's argument list**, e.g. `array(0 => array(1,2,3,4,5))`
Gordon
I get it. The idea that bothers me is that using the functions I wrote I get the same result as in Perl, BUT they are not an identical translation from Perl, like yours is. Thus, I don't know which way to go: adapt the functions in PHP or translate them as they are in Perl.
Alex
@Alex PHP and Perl both have their unique quirks. I wouldn't bother about *how* the functions are implemented in Perl as long as the desired input and output is correct. Port the functionality, not the implementation.
Gordon
+2  A: 

@_ is the incoming parameter to the sub, but referred in scalar context is the number of parameters.

In php will be something like:

function variance() {
    $arguments = func_get_args();
    $sum = sum_squares($arguments);
    $deg = sizeof($arguments);
    return $sum/$deg;
}
Enrico Carlesso
+1  A: 

To supplement the other answers, the @_ Special Variable is described in the free official Perl online documentation (along with other variables):

Within a subroutine the array @_ contains the parameters passed to that subroutine. See perlsub.

toolic
A: 

Ok, all the subroutines are as follows:

sub mean { # mean of values in an array
  my $sum = 0 ;
  foreach my $x (@_) {
    $sum += $x ;
  }
  return $sum/@_ ;
}

sub sum_squares { # sum of square differences from the mean
  my $mean = mean (@_) ;
  my $sum_squares = 0 ;
  foreach my $x (@_) {
    $sum_squares += ($x - $mean) ** 2 ;
  }
  return $sum_squares ;
}

sub variance { # variance of values in an array
  my $sum_squares = sum_squares (@_) ;
  my $deg_freedom = @_ - 1 ;
  return $sum_squares/$deg_freedom ;
}

sub median { # median of values in an array
  my @sorted = sort {$a <=> $b} (@_) ;
  if (1 == @sorted % 2) # Odd number of elements
    {return $sorted[($#sorted)/2]}
  else                   # Even number of elements
    {return ($sorted[($#sorted-1)/2]+$sorted[($#sorted+1)/2]) / 2}
}

sub histogram { # Counts of elements in an array
  my %histogram = () ;
  foreach my $value (@_) {$histogram{$value}++}
  return (%histogram) ;
}

Please bear with me because its my first time with Perl. From what I've seen (tested), the right answer in this case is the one of David Dorward. I do have another question regarding this set of subroutines that is here.

Alex
A: 
pavun_cool