tags:

views:

107

answers:

5

I'm having trouble translating a subroutine from Perl to PHP (I'm new to Perl). The entire subroutine is as follows:

sub find_all_subsets {
  if (1 == scalar (@_)) {return [@_]}
  else {
    my @all_subsets = () ;
    my $last_item = pop (@_) ;
    my @first_subsets = find_all_subsets (@_) ;
    foreach my $subset (@first_subsets) {
      push (@all_subsets, $subset) ;
      my @ext_subset = @{$subset} ;
      push (@ext_subset, $last_item) ;
      push (@all_subsets, [@ext_subset]) ;
    }
    push (@all_subsets, [$last_item]) ;
    return (@all_subsets) ;
  }
}

My problem is that I really don't quite understand the Perl syntax, so I'm having trouble writing these @{$subset}, [@ext_subset] and [$last_item] in PHP.

Thanks and sorry if the question is stupid.

+4  A: 

[] is an array referencing operator, for turning an array into an array reference

@{} is an array dereferencing operator, turning an array reference into an array

@a = (1,2,3);
$a = [ @a ];
@b = @{$a};       # now @b ==> (1,2,3)
mobrule
@mobrule => just to clarify `[ ... ]` turns a list into an array reference, \ would be the array (and everything else) referencing operator
Eric Strom
+1  A: 

Something like this:

@{$subset}    # Return array reference as an ordinary array, not applicable in PHP.
[@ext_subset] # Turn an array into an array reference, not applicable in PHP.
[$last_item]  # Create an array with $last_item as the only element, equal to array($last_item) in PHP.
Mikael S
A: 

So is this correct (in php)?

function find_all_subsets($array) {
    if (count($array) == 1) {
        return $array;
    }
    else {
        $all_subsets = array();
        $last_item = array_pop($array);
        $first_subsets = find_all_subsets($array);
        foreach ($first_subsets as $key => $val) {
            array_push($all_subsets, $val);
            $ext_subset[] = $val;
            array_push($ext_subset, $last_item);
            array_push($all_subsets, $ext_subset);
        }
        array_push($all_subsets, $last_item);
        return $all_subsets;
    }
}
Alex
A: 

in case you just need to find all subsets, it goes like this

function subsets($ary) {
    $out = array();
    $len = count($ary);
    for($i = 0; $i < (1 << $len); $i++) {
        for($p = array(), $j = 0; $j < $len; $j++)
            if($i & (1 << $j))
                $p[] = $ary[$j];
        $out[] = $p;
    }
    return $out;
}
stereofrog
the original perl sub returns a 2^n - 1 rows array (where n = count($ary)). yours returns a n rows array.
Alex
hmm no it doesn't
stereofrog
yes, it does. e.g.:$ary = array(1,2,3,4,5);in php count(subsets($ary)) = 32while in perl if my @ary2 = find_all_subsets($ary); scalar(@ary2) = 31.
Alex
There are two major differences to the original perl script: a) This script returns array() as a subset, the original doesn't. b) The "sorting order" of the subsets. How the []/@{} reference thingy might affect the result if you pass something other than numbers is beyond my knowledge of perl.
VolkerK
@Alex: Do you have to replicate each and every "feature" of the original perl function, i.e. does it have to behave _exactly_(!) the same no matter what you pass to the function?
VolkerK
@VolkerK no, I just need the same result. The fact is that I have a perl script that I need to translate into PHP and it's the first time I deal with perl. This particular subroutine does this: Takes an array of size n and returns a two-dimensional array of 2^n - 1 rows, consisting of all non-empty subsets of the original array. (the comment in the perl script)
Alex
+2  A: 

When I run

use Data::Dumper;
$Data::Dumper::Terse = 1;
$Data::Dumper::Indent = 0; 
my @x = (1,2,3,4);
my @y = find_all_subsets(@x);
foreach my $subset (@y) {
  print Dumper($subset), "\n";
}

(+ the original script) the output is

[1]
[1,4]
[1,3]
[1,3,4]
[1,2]
[1,2,4]
[1,2,3]
[1,2,3,4]
[2]
[2,4]
[2,3]
[2,3,4]
[3]
[3,4]
[4]

Please note the [ ] in the output, we'll come back to them later.

function find_all_subsets (array $x) {
  if ( 1>= count($x) ) { // the >= differs from the original script, use == or === if you want to keep it "more original"
    return array($x);
  } 
  else {
    $all_subsets = array();
    $last_item = array_pop($x);
    $first_subsets = find_all_subsets($x) ;
    foreach ($first_subsets as $subset) {
      array_push($all_subsets, $subset);
      array_push($subset, $last_item);
      array_push($all_subsets, $subset);
    }
    array_push ($all_subsets, array($last_item));
    return $all_subsets;
  }
}

$x = array(1,2,3,4);
$y = find_all_subsets($x);
foreach($y as $subset) {
  echo '(', join(',', $subset), ")\n";
}

produces

(1)
(1,4)
(1,3)
(1,3,4)
(1,2)
(1,2,4)
(1,2,3)
(1,2,3,4)
(2)
(2,4)
(2,3)
(2,3,4)
(3)
(3,4)
(4)

so far so good. Now back to the [ ]. Data::Dumper chose this [ ] and not ( ) beacuse it's not an array but an array-reference (bare me if I don't use the correct terms; perl is really not my strong suit). Let's change the perl test script and look at the effect all those reference thingies have.

use Data::Dumper;
$Data::Dumper::Terse = 1;
$Data::Dumper::Indent = 0; 
$x2 = 2;
my @x = (1,\$x2,3,4);
my @y = find_all_subsets(@x);
$x2 = 99;
foreach my $subset (@y) {
  print Dumper($subset), "\n";
}

and the output changes to

[1]
[1,4]
[1,3]
[1,3,4]
[1,\99]
[1,\99,4]
[1,\99,3]
[1,\99,3,4]
[\99]
[\99,4]
[\99,3]
[\99,3,4]
[3]
[3,4]
[4]

You see, I change $x2 after the call to find_all_subsets() but still in the result the new value is used and Data::Dumper marks the "value" as a reference ( \99 instead of simply 99 ). Do you need this feature in your php script, too?

VolkerK
nope, just simply 99. the array that that i need to analyze is numbers only (integers), so this will just do. thanks a lot!
Alex