'Call-time pass-by-reference', hoe script er op aan te passen?
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)
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
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
?>
$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
?>
https://stackoverflow.com/questions/797251/transposing-multidimensional-arrays-in-php
Een van de gesuggereerde oplossingen (zelf niet getest):
Wat je wil wordt de 'transpose' genoemd. Zie bijvoorbeeld: Een van de gesuggereerde oplossingen (zelf niet getest):
Code (php)
1
2
3
4
5
6
2
3
4
5
6
<?php
function transpose($array) {
array_unshift($array, null);
return call_user_func_array('array_map', $array);
}
?>
function transpose($array) {
array_unshift($array, null);
return call_user_func_array('array_map', $array);
}
?>
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 -
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)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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;
}
?>
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)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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;
}
?>
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)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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;
}
?>
// 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 -
Ik ga er nog eens naar kijken als ik terug ben van vakantie. Dan moet het vast wel goedkomen.
Je kunt dit toch ook gewoon doen door aan de molen te draaien zonder allerlei ingewikkelde constructies?
Code (php)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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>';
?>
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
Gewijzigd op 04/05/2016 16:33:51 door Pim -