'Call-time pass-by-reference', hoe script er op aan te passen?

Overzicht Reageren

Sponsored by: Vacatures door Monsterboard

.NET Developer / Innovatieve software / Virtual Re

Functieomschrijving Als .Net developer werken aan innovatieve software waar onder andere gebruik gemaakt wordt van Virtual Reality? Bijdragen aan een organisatie waar je uitgedaagd wordt om continu verbeteringen en ontwikkelpunten te ontdekken en door te voeren? Werken in de omgeving Putten? Reageer dan nu voor meer informatie! Het pro-actief aandragen van verbeteringen voor de bestaande applicatie; Ontwikkelen van nieuwe functionaliteiten; Doorvoeren van aanpassingen en wijzigingen; Verantwoordelijk voor koppelingen met andere systemen; Op de hoogte blijven van technische ontwikkelingen. Functie-eisen Hbo werk- en denkniveau; Een afgeronde IT gerelateerde opleiding; Minimaal 1 jaar professionele ervaring als developer; Aantoonbare kennis van C#; Initiatiefrijke

Bekijk vacature »

- Ariën -
Beheerder

- Ariën -

03/05/2016 23:12:43
Quote Anchor link
Deze code kwam ik tegen op een website, en wou ik graag gebruiken om een array aan te passen zodat de waardes verticaal staan. Check het outputje maar in de comments....

Echter lijkt het verouderd, en krijg ik dit te zien:

Fatal error: Call-time pass-by-reference has been removed in /blah/blah/test.php on line 16

Ik kan wel fijn de & (voor $acum) weghalen zoals velen mensen op internet adviseren, maar dan doet de hele functie niks meer. Hoe valt dit script toekomstbestendiger te kunnen worden gemaakt zodat het op PHP 5.4 en > werkt?
Code (php)
PHP script in nieuw venster Selecteer het PHP script
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<?php
    $matrix
= array(
        array('a', 1, 2),
        array('b', 3, 4),
        array('c', 5, 6),
        array('d', 7, 8)
    );

    $transpose = array_reduce(
        $matrix,
        function (
$acum, $row) {
            array_walk(
                $row,
                function(
$column, $index, $acum) {
                    $acum[$index][] = $column;
                },
                &
$acum
            );
            return $acum;
        },

        array()
    );

    print_r($transpose);
    // Output:
    // -------
    // a b c d
    // 1 3 5 7
    // 2 4 6 8

?>
 
PHP hulp

PHP hulp

19/01/2020 10:42:25
 
Pim -

Pim -

04/05/2016 01:54:34
Quote Anchor link
Wat je wil wordt de 'transpose' genoemd. Zie bijvoorbeeld: https://stackoverflow.com/questions/797251/transposing-multidimensional-arrays-in-php

Een van de gesuggereerde oplossingen (zelf niet getest):
Code (php)
PHP script in nieuw venster Selecteer het PHP script
1
2
3
4
5
6
<?php
function transpose($array) {
    array_unshift($array, null);
    return call_user_func_array('array_map', $array);
}

?>
 
- Ariën -
Beheerder

- Ariën -

04/05/2016 08:48:40
Quote Anchor link
Dat werkt prima, en is tevens een stuk compacter. ;-)
Hij lijkt de boel doorelkaar te gooien. Ik vermoed dat hij lege waardes niet overslaat? Ik zoek even verder...


Maar even voor de 'leer'. Hoe valt de fout in mijn code op te lossen?
Daar ben ik ook wel even nieuwsgierig naar ;-)
Gewijzigd op 04/05/2016 11:07:06 door - Ariën -
 
Pim -

Pim -

04/05/2016 12:10:50
Quote Anchor link
Het script dat jij aanhaalt, lijkt me niet de moeite van het doorgronden waard. Het probeert wat functionele technieken (reduce) te combineren met C-achtige technieken (pointers/references). Het eerste lukt nooit echt mooi in PHP en het tweede gebruikt echt niemand.

Docs over reduce: https://secure.php.net/manual/en/function.array-reduce.php
Docs over call by reference: https://secure.php.net/manual/en/language.references.pass.php [zie ook de noot over PHP >= 5.4 ]

De vorige functie die ik suggereerde werkt prima met de testdata. Is je matrix niet mooi vierkant? In dat de geval kan je de volgende gebruiken (ook van StackOverflow). Dit lijkt me de idiomatische manier om dit op te lossen:
Code (php)
PHP script in nieuw venster Selecteer het PHP script
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?php
function array_transpose(array $arr)
{

    $keys    = array_keys($arr);
    $sum     = array_values(array_map('count', $arr));

    $transposed = array();

    for ($i = 0; $i < max($sum); $i ++)
    {

        $item = array();
        foreach ($keys as $key)
        {

            $item[$key] = array_key_exists($i, $arr[$key]) ? $arr[$key][$i] : NULL;
        }

        $transposed[] = $item;
    }

    return $transposed;
}

?>



Toevoeging op 04/05/2016 12:29:20:

Ik heb nog even naar je vorige functie gekeken, en heb 'm aan de praat gekregen:

Code (php)
PHP script in nieuw venster Selecteer het PHP script
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?php

function transpose($matrix) {
    $transpose = array_reduce(
        $matrix,
        function (
$acum, $row) {
            array_walk(
                $row,
                function(
$val, $index) use(&$acum){
                    $acum[$index][] = $val;
                }
            );

            return $acum;
        },

        array()
    );

    return $transpose;
}

?>

De binnenste 'function' wil de accumulator van de reduce function (zie reduce docs) aanpassen. In jouw voorbeeld gebeurt dat middels het derde 'userdata' argument van array_walk, maar tegenwoordig (PHP >= 5.4) kunnen we dat met closures (https://secure.php.net/manual/en/functions.anonymous.php) oplossen. Met 'use' kan je variabelen van buiten de functie gebruiken binnen de functie. Door een '&' toe te voegen kan je deze ook nog aanpassen. Dat is het gewenste gedrag.

Het enige wat er mbt call by reference veranderd is in PHP5.4 is dat je dat nu in de functie definitie moet aangeven, niet in wanneer je de functie aanroept. [in de docs 'There is no reference sign on a function call - only on function definitions. Function definitions alone are enough to correctly pass the argument by reference.] Het volgende zou dus moeten werken:
Code (php)
PHP script in nieuw venster Selecteer het PHP script
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?php
// DEMO - werkt dus niet - zie hier onder voor de reden
function transpose2($matrix) {
    $transpose = array_reduce(
        $matrix,
        function (
$acum, $row) {
            array_walk(
                $row,
                function(
$column, $index, &$acum) {
                    $acum[$index][] = $column;
                },

                $acum
            );
            return $acum;
        },

        array()
    );

    return $transpose;
}

?>

Maar dit geeft een lege array terug. Waarom? Geen idee. Zal iets met de internals van array_walk te maken hebben.

Toevoeging op 04/05/2016 12:42:48:

Laatste toevoeging.

Ze hebben de mogelijkheid om 'by reference' userdata te gebruiken verwijderd in deze commit: https://github.com/php/php-src/commit/37d7df72a62e9d63be6fb7eb83805a59f819bbf7, toegepast in PHP5.4, in ext/standard/array.c vanaf regel 1047. Niet alleen gebruikte je de 'reference' verkeerd, de hele mogelijkheid ertoe is er in 2011 uitgegooid. Het bovenstaande gebruik van een closure werkt wel en is een stuk mooier.

Zo, nu maar weer tentamens leren.
Gewijzigd op 04/05/2016 12:54:37 door Pim -
 
- Ariën -
Beheerder

- Ariën -

04/05/2016 15:07:02
Quote Anchor link
Flinke lap tekst :-)
Ik ga er nog eens naar kijken als ik terug ben van vakantie. Dan moet het vast wel goedkomen.
 
Thomas van den Heuvel

Thomas van den Heuvel

04/05/2016 15:22:11
Quote Anchor link
Ik vind dit interessante materie, maar als ik goed begrijp wat Ariën probeert te doen is dit toch gewoon een kwestie van het X- en het Y- coordinaat in een (*twee-dimensionale*) matrix met elkaar verwisselen?

Je kunt dit toch ook gewoon doen door aan de molen te draaien zonder allerlei ingewikkelde constructies?
Code (php)
PHP script in nieuw venster Selecteer het PHP script
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?php
function transpose($input) {
    $output = array();
    foreach ($input as $y => $row) {
        foreach ($row as $x => $v) {
            $output[$x][$y] = $v;
        }
    }

    return $output;
}


$input = array(
    array('a', 1, 2),
    array('b', 3, 4),
    array('c', 5, 6),
    array('d', 7, 8),
);


$input = transpose($input);
echo '<pre>'.print_r($input, true).'</pre>';
?>

Keep it simple.

EDIT: en ja, die reference (&) verplaatsen naar de functie-definitie zou ook mijn eerste ingeving zijn, maar als die functie dat niet meer ondersteunt... Custom callback functie schrijven dan maar? Of je gaat voor de bovenstaande, en naar mijn mening vele malen simpelere, oplossing. Als efficiëntie een overweging is bij de implementatie dan zul je even naar je opties moeten kijken uiteraard...
Gewijzigd op 04/05/2016 15:44:16 door Thomas van den Heuvel
 
Pim -

Pim -

04/05/2016 16:07:29
Quote Anchor link
Dat is toch precies wat ik bovenin mijn laatste post aanhaal? Alleen houdt die versie rekening met niet-rechthoekige multidimensionale arrays, wat een leuke toevoeging is, lijkt me.
Gewijzigd op 04/05/2016 16:33:51 door Pim -
 



Overzicht Reageren

 
 

Om de gebruiksvriendelijkheid van onze website en diensten te optimaliseren maken wij gebruik van cookies. Deze cookies gebruiken wij voor functionaliteiten, analytische gegevens en marketing doeleinden. U vindt meer informatie in onze privacy statement.