php-ds / ext-ds

An extension providing efficient data structures for PHP 7
https://medium.com/p/9dda7af674cd
MIT License
2.11k stars 95 forks source link

using Pair objects in Set yields to invalid set differences #150

Closed muawijhe closed 4 years ago

muawijhe commented 4 years ago

Hi! First of all, congrats for such a nice piece of software :)

Version tested in this issue: 1.1.8-1 (Ubuntu 18.04 amd64).

I'm trying to use the Set class as container of Pair. Union operation works flawlessly. However, diff function yields to unusual results. Example:

// Let us define two functions to perform set differences
function setDiff_A(\Ds\Set $oSet, \Ds\Set $oRem) : \Ds\Set { 
    $oResult = $oSet; 
    foreach ($oRem as $oItemToRemove) {
            foreach ($oResult as $oItem) {
                if ($oItem == $oItemToRemove) {
                    $oResult->remove($oItem);
                }
            }
    }
    return $oResult;
}

function setDiff_B(\Ds\Set $oSet, \Ds\Set $oRem) : \Ds\Set { 
    $oResult = $oSet; 
    foreach ($oRem as $oItemToRemove) {
            $oResult->remove($oItemToRemove);
    }
    return $oResult;
}

function setOperationTest(bool $bObj) {
    if ($bObj) {
        echo "\nTesting With Pair Objects\n";
        $oX = new \Ds\Pair('Foo',[ 'x' => '1']);
        $oY = new \Ds\Pair('Foo',[ 'x' => '1']);
        $oZ = new \Ds\Pair('Bar',[ 'z' => '2']);
    } else {
        echo "\nTesting With Hash Arrays\n";
        $oX = ['Foo', ['x' => '1']];
        $oY = ['Foo', ['x' => '1']];
        $oZ = ['Bar', ['z' => '2']];
    }

    // Testing operator for equality
    echo "\nTesting value comparison\n";
    echo "X == Y \t << ".(($oX == $oY)  ? "OK" : "FAIL")." >>\n";

    echo "\nBuilding Sets\n";
    // Testing set of pair
    $oA = new \Ds\Set();
    $oA->add($oX);        

    echo "A = { X }\n"; print_r($oA);

    $oB = new \Ds\Set();
    $oB->add($oZ);

    echo "B = { Z }\n"; print_r($oB);

    $oC = new \Ds\Set();
    $oC->add($oY);

    echo "C = { Y }\n"; print_r($oC);

    echo "\nTesting operations...\n";
    echo "X in A \t << ".($oA->contains($oX) ? "OK" : "FAIL")." >>\n";
    echo "Y in A \t << ".($oA->contains($oY) ? "OK" : "FAIL")." >>\n";
    echo "Z in A \t << ".($oA->contains($oZ) ? "OK" : "FAIL")." >>\n";

    echo "\nTesting F =  A + B - C...\n";

    // Let us try to use Ds methods
    $oDS = $oA->union($oB);
    $oDS = $oDS->diff($oC);
    echo "\nResult with Ds:\n\n"; print_r($oDS);

    $oFA = $oA->union($oB);
    $oFA = setDiff_A($oFA,$oC);
    echo "\nResult with Function A:\n\n"; print_r($oFA);

    $oFB = $oA->union($oB);
    $oFB = setDiff_B($oFB,$oC);
    echo "\nResult with Function B:\n\n"; print_r($oFB);

    echo "\nSet Size:\n";
    echo "DS Size -> ".count($oDS->toArray())."\n";
    echo "FA Size -> ".count($oFA->toArray())."\n";
    echo "FB Size -> ".count($oFB->toArray())."\n";
}

with these support functions, lets us try to run two tests:


// setOperationTest(false);

Testing With Hash Arrays

Testing value comparison
X == Y   << OK >>

Building Sets
A = { X }
Ds\Set Object
(
    [0] => Array
        (
            [0] => Foo
            [1] => Array
                (
                    [x] => 1
                )

        )

)
B = { Z }
Ds\Set Object
(
    [0] => Array
        (
            [0] => Bar
            [1] => Array
                (
                    [z] => 2
                )

        )

)
C = { Y }
Ds\Set Object
(
    [0] => Array
        (
            [0] => Foo
            [1] => Array
                (
                    [x] => 1
                )

        )

)

Testing operations...
X in A   << OK >>
Y in A   << OK >>
Z in A   << FAIL >>

Testing F =  A + B - C...

Result with Ds:

Ds\Set Object
(
    [0] => Array
        (
            [0] => Bar
            [1] => Array
                (
                    [z] => 2
                )

        )

)

Result with Function A:

Ds\Set Object
(
    [0] => Array
        (
            [0] => Bar
            [1] => Array
                (
                    [z] => 2
                )

        )

)

Result with Function B:

Ds\Set Object
(
    [0] => Array
        (
            [0] => Bar
            [1] => Array
                (
                    [z] => 2
                )

        )

)

Set Size:
DS Size -> 1
FA Size -> 1
FB Size -> 1

now running the same test using Pair...

// setOperationTest(true);
Testing With Pair Objects

Testing value comparison
X == Y   << OK >>

Building Sets
A = { X }
Ds\Set Object
(
    [0] => Ds\Pair Object
        (
            [key] => Foo
            [value] => Array
                (
                    [x] => 1
                )

        )

)
B = { Z }
Ds\Set Object
(
    [0] => Ds\Pair Object
        (
            [key] => Bar
            [value] => Array
                (
                    [z] => 2
                )

        )

)
C = { Y }
Ds\Set Object
(
    [0] => Ds\Pair Object
        (
            [key] => Foo
            [value] => Array
                (
                    [x] => 1
                )

        )

)

Testing operations...
X in A   << OK >>
Y in A   << FAIL >>
Z in A   << FAIL >>

Testing F =  A + B - C...

Result with Ds:

Ds\Set Object
(
    [0] => Ds\Pair Object
        (
            [key] => Foo
            [value] => Array
                (
                    [x] => 1
                )

        )

    [1] => Ds\Pair Object
        (
            [key] => Bar
            [value] => Array
                (
                    [z] => 2
                )

        )

)

Result with Function A:

Ds\Set Object
(
    [0] => Ds\Pair Object
        (
            [key] => Bar
            [value] => Array
                (
                    [z] => 2
                )

        )

)

Result with Function B:

Ds\Set Object
(
    [0] => Ds\Pair Object
        (
            [key] => Foo
            [value] => Array
                (
                    [x] => 1
                )

        )

    [1] => Ds\Pair Object
        (
            [key] => Bar
            [value] => Array
                (
                    [z] => 2
                )

        )

)

Set Size:
DS Size -> 2
FA Size -> 1
FB Size -> 2

someone could give me a solid explanation of such result?

In particular,

The operation we would like to perform:

F = A + B -C = {X} + {Z} - {Y}

under the condition (X == Y), F = {Z}

Thanks in advance for your help

rtheunissen commented 4 years ago

I wouldn't recommend using Pair in Set or Map because it is not Hashable or immutable. I do hope to support immutable tuple types that can be used as keys in Map and Set in v2 - no intention to implement that in 1.x though.