Amanda
Amanda

Reputation: 63

PHP recursive foreach unexpected behavior

Update:

I figured out what was causing the infinite loop and put in a check to make sure each node was only included in the path once. This now returns a multi-level array with the expected values. Any ideas on how to get this all in a single-level array?

Array
(
    [0] => Array
        (
            [0] => 200
            [1] => Array
                (
                    [0] => Array
                        (
                            [0] => 109
                        )

                    [1] => 155
                )

            [2] => Array
                (
                    [0] => Array
                        (
                            [0] => 164
                        )

                    [1] => 110
                )

        )

)

New code:

function buildPath($nodes,$src,$target,$pathSum,$elements=array(1))
        {
            $paths=array();
            foreach($nodes[$src] as $dest=>$dist)
            {
                $e=$elements;
                $sum=$pathSum+$dist;
                if(!in_array($dest,$e))
                {                   
                    if($dest==$target)
                    {
                        $paths[]=$sum;
                    }
                    else
                    {
                        $e[]=$dest;
                        $paths[]=buildPath($nodes,$dest,$target,$sum,$e);
                    }
                }
            }
            return $paths;
        }

Original Post

long time lurker, first time poster. I would consider myself intermediate level in php, and I'm starting to dive into recursive functions. I ran into some rather unexpected behavior, and I'm very confused.

function buildPath($nodes,$src,$target,$pathSum)
    {
        $paths=array();
        foreach($nodes[$src] as $dest=>$dist)
        {
            $sum=$pathSum+$dist;
            if($dest==$target)
            {
                $paths[]=$sum;
            }
            elseif($dest!=$target)
            {
                $paths[]=buildPath($nodes,$dest,$target,$sum);
            }
        }
        return $paths;
    }

When I call the function with:

$src=1
$target=4
$pathSum=0
$nodes= Array
(
    [1] => Array
        (
            [4] => 200
            [2] => 5
            [3] => 10
        )

    [4] => Array
        (
            [1] => 200
            [2] => 150
            [3] => 100
        )

    [2] => Array
        (
            [1] => 5
            [3] => 4
            [4] => 150
        )

    [3] => Array
        (
            [1] => 10
            [2] => 4
            [4] => 100
        )

)

it runs an infinite loop until it times out. I started echoing out variables at different times to try and debug. When I call:

function buildPath($nodes,$src,$target,$pathSum)
        {
            $paths=array();
            foreach($nodes[$src] as $dest=>$dist)
            {
                $sum=$pathSum+$dist;
                if($dest==$target)
                {
                    $paths[]=$sum;
                    echo "$src->$dest, Target=$target, distance=$dist, sum=$sum. | ";
                }
                elseif($dest!=$target)
                {
                    $paths[]=buildPath($nodes,$dest,$target,$sum);
                    echo "$src->$dest, elseif clause | ";
                }
            }
            print_r($paths);
            return $paths;
        }

with the same inputs, I get the following output:

1->4, Target=4, distance=200, sum=200. | 1->4, Target=4, distance=200, sum=210. | 1->4, Target=4, distance=200, sum=220. | 1->4, Target=4, distance=200, sum=230. | 1->4, Target=4, distance=2, sum=240. | 1->4, Target=4, distance=200, sum=250. | 

repeated until the script times out, incrementing sum by 10 each iteration. dumping $paths reveals that it is not appending anything to the $paths array, simply updating $paths[0] to the new sum.

What am I missing here? Am I just completely misunderstanding how recursion should work?

Upvotes: 2

Views: 126

Answers (3)

Amanda
Amanda

Reputation: 63

Managed to solve it like this:

function buildPath($nodes,$src,$target,$pathSum,$elements=array(1))
        {
            $paths=array();
            foreach($nodes[$src] as $dest=>$dist)
            {
                $e=$elements;
                $sum=$pathSum+$dist;
                if(!in_array($dest,$e))
                {                   
                    if($dest==$target)
                    {
                        $paths[]=$sum;
                    }
                    else
                    {
                        $e[]=$dest;
                        $paths=array_merge($paths,buildPath($nodes,$dest,$target,$sum,$e));
                    }
                }
            }
            return $paths;
        }

Upvotes: 2

Poiz
Poiz

Reputation: 7617

Simply put the $paths outside the function and use it within the function as a Global variable like below and you may test it out here:

    <?php

        $src        = 1;
        $target     = 4;
        $pathSum    = 0;
        $nodes      = [
            1=>[4=>200,    2=>5,   3=>10],
            4=>[1=>200,    2=>150, 3=>100],
            2=>[1=>5,      3=>4,   4=>150],
            3=>[1=>10,     2=>4,   4=>100],
        ];

        $paths  = array();    //<== OUTSIDE THE FUNCTION.... AS GLOBAL VARIABLE...

        var_dump( buildPath($nodes, $src, $target, $pathSum) );

        function buildPath($nodes, $src, $target, $pathSum, $elements=array(1)) {
            global $paths;
            foreach($nodes[$src] as $dest=>$dist){
                $e      = $elements;
                $sum    = $pathSum+$dist;

                if(!in_array($dest, $e))    {
                    if($dest == $target){
                        $paths[] = $sum;
                    }else{
                        // SIMPLY RECURSE & NOT BUILD $path HERE
                        $e[] = $dest;
                        buildPath($nodes, $dest, $target, $sum, $e);
                    }
                }
            }
            return $paths;
        }

        // THE var_dump ABOVE PRODUCES:::           
        array (size=5)
          0 => int 200
          1 => int 109
          2 => int 155
          3 => int 164
          4 => int 110

Upvotes: 2

Arup Garai
Arup Garai

Reputation: 151

Can you please change and check from

 elseif($dest!=$target)
        {
            $paths[]=buildPath($nodes,$dest,$n,$sum);
        }

To

 elseif($dest<=$target)
        {
            $paths[]=buildPath($nodes,$dest,$n,$sum);
        }

Upvotes: 1

Related Questions