[PHP] Iteration ipv recursie

Pagina: 1
Acties:

Acties:
  • 0 Henk 'm!

  • g4wx3
  • Registratie: April 2007
  • Laatst online: 12-10 08:33
Beste,

Het lukt me niet om een recursieve functie om te zetten naar een iteratie.
Dit is nodig, om de prestaties te verbeteren van het php-script om het menu-systeem te genereren

De bedoeling is om een hoop flat arrays te mergen tot 1 alsvolgt:

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
29
30
31
32
33
34
35
36
37
38
39
<?php

array folder0{
'folder1'=>'string';
'folder2'=>'string';
}

array folder1{
'folder1a'=>'string';
'folder1b'=>'string';
}


array folder2{
'folder2a'=>'string';
'folder2b'=>'string';
}


// omzetten naar

array folder0{
'folder1'=> array{
0 => 'string';
1 => array{
'folder1a'=>'string';
'folder1b'=>'string';
}
}

'folder2'=> array{
0 => 'string';
1 => array{
'folder2a'=>'string';
'folder2b'=>'string';
}
}

}



Ik probeer met een do-while lus, maar ik kom er niet aan uit, hoe moet ik het aanpakken?
PHP:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$folder = 'root';

do{
// initiate
$menu = array();
// fill
include("/$folder/menu.php");
// parse $menu to $menu[%folder%] = %string%;
include("menu_parse.php");

// add to stack
$stack[$folder][] = $menu;

}
while( list($folder,$string) = each($stack))



** extra info **

De recursieve werkende code is de volgende (als methode van een object)
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
29
30
31
32
<?php
// initiate $marraytemp
$lang = $this->get('lang');
$marray = array();

// search file menu.php
$file = "resources/html/$params/menu.php";
if(!is_file($file)){
    $file = "resources/htmlloc/$lang/$params/menu.php";
}
if(is_file($file)){
    // initiate $menu
    $menu = array();
    // read menu-file
    include($file);
    // parse $menu
    foreach($menu as $mkey=>$menu_item){
        // initiate $url_html
        $url_html = null;
        include('menu_parser.php');
        if(!empty($url_html)){
            $marray[$mkey][] = $url_html;
            $marray_temp = $this->load('recursive_marray.php',$mkey);
            if(!empty($marray_temp)){
                $marray[$mkey][] = $marray_temp;
            }
        }
    }
}

// set return value
$params = $marray;


De functie opoepen:
PHP:
1
2
    // recursive_marray
    $params = $this->load('recursive_marray.php', 'menu');

http://www.softfocus.be/


Acties:
  • 0 Henk 'm!

  • Janoz
  • Registratie: Oktober 2000
  • Laatst online: 15-10 22:15

Janoz

Moderator Devschuur®

!litemod

Ik zie helemaal geen recursieve functie eigenlijk. Ik zie eigenlijk helemaal geen functie. Het enige wat ik zie is zeer vreemde include constructies. Ik kan me voorstellen dat dit traag is door een overdreven hoeveelheid IO verkeer en veel context switches waardoor er voor php al helemaal niks te optimaliseren valt.

Ken Thompson's famous line from V6 UNIX is equaly applicable to this post:
'You are not expected to understand this'


Acties:
  • 0 Henk 'm!

  • NMe
  • Registratie: Februari 2004
  • Laatst online: 09-09 13:58

NMe

Quia Ego Sic Dico.

Misschien is het handig als je eerst even uitlegt wat jij denkt dat recursie is, zodat wij kunnen uitleggen waarom je verkeerd zit met die gedachte. ;)

'E's fighting in there!' he stuttered, grabbing the captain's arm.
'All by himself?' said the captain.
'No, with everyone!' shouted Nobby, hopping from one foot to the other.


Acties:
  • 0 Henk 'm!

  • g4wx3
  • Registratie: April 2007
  • Laatst online: 12-10 08:33
recursie, een functie die zichzelf aanroept.

Maar ik zit in een object, waarin ik recursief iets wil uitvoeren.
De classe heeft een methode load() om includes te doen,

Deze method roep ik aan om een recursieve include te doen van het 'bestand' hierboven.
$mkey wordt doorgegeven als $param.

Maar inderdaad includes zijn heel traag.

En daarbij kan ik wel begrijpen dat het wat verwarrend is.

Vandaar dat ik dit wil doen met een while(), of do{}while()

Maar ik weet niet hoe ik na een iteratie, verder kan opschuiven naar een dieper niveau.
Dus een iteratie van het type eerst breed dan dieper.

http://www.softfocus.be/


Acties:
  • 0 Henk 'm!

  • g4wx3
  • Registratie: April 2007
  • Laatst online: 12-10 08:33
Ik denk dat ik het anders ga aanpakken met de volgende code.

http://pastebin.com/GAFvSew4

http://www.softfocus.be/


Acties:
  • 0 Henk 'm!

  • Janoz
  • Registratie: Oktober 2000
  • Laatst online: 15-10 22:15

Janoz

Moderator Devschuur®

!litemod

Ik zie nergens een methode, en ook in het stuk code wordt nergens een methode aangeroepen. Het kan dus onmogelijk recursie zijn. Daarnaast vind ik de manier waarop jij denkt includes te moeten gebruiken nogal exotisch. Include gebruik je zodat je verschillende bestanden kunt gebruiken om je code in op te slaan. Zoals jij het gebruikt lijkt het eerder op een onduidelijke, inefficiënte en bug gevoelige emulatie van een functie aanroep.

Probeer je code eens te schrijven zonder een include te gebruiken binnen de daadwerkelijke flow van je code.

Ken Thompson's famous line from V6 UNIX is equaly applicable to this post:
'You are not expected to understand this'


Acties:
  • 0 Henk 'm!

  • NMe
  • Registratie: Februari 2004
  • Laatst online: 09-09 13:58

NMe

Quia Ego Sic Dico.

Je kan moeilijk concluderen dat recursie geen goed plan is als je recursie niet toepast zoals het hoort.

'E's fighting in there!' he stuttered, grabbing the captain's arm.
'All by himself?' said the captain.
'No, with everyone!' shouted Nobby, hopping from one foot to the other.


Acties:
  • 0 Henk 'm!

  • diabolofan
  • Registratie: Mei 2009
  • Laatst online: 14:54
Het grote code block (is zoals de tekst erboven zegt) een methode van een class. Daaronder staat hoe die aangeroepen wordt:
PHP:
1
$params = $this->load('recursive_marray.php', 'menu');


In de methode code zie je op regel 23 inderdaad dat die methode weer aangeroepen wordt. Dit is dus duidelijk wel een recursive functie heren ;).

Of dit inderdaad een goede oplossing is qua IO performance is maar de vraag. Een oplossing zoals jij dit zei met een nested array voor subitems kan al een performance verbetering opleveren. De vraag is alleen of met de files wilt blijven werken en waarom je niet gewoon gebruik gaat maken van een database. Daarom is de vraagstelling me enigsinds onduidelijk...

Acties:
  • 0 Henk 'm!

  • kwaakvaak_v2
  • Registratie: Juni 2009
  • Laatst online: 10-10 08:02
Je was mij voor :)

oh... enne een include in een foreach loop, dat klinkt eigenlijk NOOIT als een goed plan.

oh 2, volgens mij zit er gewoon in SPL sinds 5.3 al ondersteuning voor recursive iterators.

zie http://php.net/manual/en/class.recursivetreeiterator.php en http://stackoverflow.com/...ratoriterator-work-in-php

[ Voor 22% gewijzigd door kwaakvaak_v2 op 17-03-2015 17:40 . Reden: oh2, recursivetreeiterator ]

Driving a cadillac in a fool's parade.


Acties:
  • 0 Henk 'm!

  • g4wx3
  • Registratie: April 2007
  • Laatst online: 12-10 08:33
Bedankt diabolofan, om de code te lezen, en ,Pin0, en kwaakvaak_v2, voor de lectuur die ik zal gaan lezen.

Waar ik eigenlijk al fout was is dat ik code aan het schrijven was in het view-gedeelte van het framework, met alle beperkingen vandien, maar dat konden jullie niet weten.

Vanavond ga ik proberen een goede controller te maken.

Ik was begonnen met fysieke files, uit gemak, en heb wel graag het voordeel om met "fysieke" menu files te werken, de gemaakte, statische pagina's+menu's zijn veel "draagbaarder". Daardoor kan ik me weer meer richten op design.

Bedankt alvast, ik zal komen met een mooie oplossing

http://www.softfocus.be/


Acties:
  • 0 Henk 'm!

  • g4wx3
  • Registratie: April 2007
  • Laatst online: 12-10 08:33
Ik heb perfect code nu

de truuc is om eerst alle menu's te verzamelen in een tabel met parrent child
Vervolgens herorganiseer ik ieder element met een foreach
Om dan pas echt aan de iteratie te beginnen waar het om draait
* Qua performance maakt dit echt een heel groot verschil (bijna 50%)

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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
    // root
    $parent = 'menu';
    // initiate output
    $params = "\r\n".'<ul>'."\r\n";
    // pointer
    $parent_stack = array();
    $items = array();
    foreach($menu as  $item){
        $items[$item['parrent_id']][] = $item;
    }    
/*
 Dit werkt omdat each() onthoud waar de interne pointer zit.
 Parrent stack houdt bij op welk niveau je zit
 
 De niveaus hebben een key op 1ste dimensie dankzij de foreach lus hierboven
 */
    while(is_array($parent_stack))
    {
        $item = each($items[$parent]);
        if ( !empty( $item ) )
        {
            // build list-item
            // extract first paramater from url
            $url = explode('=',$item['value']['link']);
            $key = $url[0];
            $value = $url[1];
            $value = $item['value']['id'];
            $class = '';
            
            // merge URL-parameters or not..
            if($item['value']['link'][0]=='#')
                $url = $item['value']['link'];
            else
            {
                $url = "?". $html->uri($item['value']['link']);
                // add .active 
                if($html->pg[$key]==$value)
                    $class = " active";
            }
            $class = 'class="'.$item['value']['position'].$class.'"';                

            // add list-item to final string 
            $params .= '<li><a href="'.$url.'" '.$class.'>'.$item['value']['title'].'</a>';
            
            // ga een menu dieper als er nog andere niveaus bestaan
            if ( !empty( $items[$item['value']['id']] )){
                $params .= "\r\n".'<ul>'."\r\n";
                array_push( $parent_stack, $parent );        
                $parent = $item['value']['id'];
            }
            // of sluit en ga naar het volgende list-item
            else{
                $params .= '</li>'."\r\n";
            }
        }
        // kom een niveau terug, of stop alles
        else{
            $params .= '</ul>'."\r\n";
            if(!empty($parent_stack))
            $parent = array_pop( $parent_stack );
            else
            $parent_stack=null;
        }
    }

PS, Dit wordt ingeladen in een methode van een object dat automatisch $params terug geeft, vandaar de naam
PS2, een kleine bug vermoed ik als er helemaal niks in de array zit, dan krijg ik <ul></ul>, maar dat kan ik makkelijk oplossen

... en dan bedenken dat het eigenlijk wel al werkte :) maar dit is veel beter

[ Voor 3% gewijzigd door g4wx3 op 18-03-2015 01:42 ]

http://www.softfocus.be/


Acties:
  • 0 Henk 'm!

  • Soultaker
  • Registratie: September 2000
  • Laatst online: 08:34
Ziet er prima uit. :)

Je kunt je logica nog wat versimpelen door je huidige $parent niet als aparte variabele, maar simpelweg aan het eind van $parent_stack op te slaan.

while(is_array($parent_stack)) wordt dan while(!empty($parent_stack)) en je kunt regel 48-49 en 59-62 versimpelen door alleen een push/pop te doen.

Acties:
  • 0 Henk 'm!

  • g4wx3
  • Registratie: April 2007
  • Laatst online: 12-10 08:33
inderdaad, en dan zal ik deze ook moeten initieren $parent_stack = array($parent);

Sowiso ga ik er nog aan sleutelen om slechts één niveau op te vragen, en de html-glue flexibeler maken (om een output te krijgen zonder lijstje) en een enkel niveau in een array.

:)

[ Voor 5% gewijzigd door g4wx3 op 18-03-2015 11:55 ]

http://www.softfocus.be/


Acties:
  • 0 Henk 'm!

  • Barryvdh
  • Registratie: Juni 2003
  • Laatst online: 16:51
Je zou ook met recursie je items kunnen bouwen. En ik zou eerst de tree maken en de html ed. los doen.

Je zou bijv een tree kunnen maken, waarbij elk item een 'children' array heeft. Als die gevuld is kan je dus je submenu tonen.

PHP:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
function make_tree($items, $parentId = null) {
    $tree = [];
    if (isset($items[$parentId]) && $children = $items[$parentId]) {
        foreach($children as $child) {
            $item = [];
            $item['title'] = $child['value']['title'];
            $item['url'] = $child['value']['url'];
            $item['class'] = 'active';
            $item['children'] = make_tree($items, $child['value']['id']);
            $tree[] = $item;
        }
    }

    return $tree;
}

$items = array();
foreach($menu as  $item){
    $items[$item['parent_id']][] = $item;
} 

$tree = make_tree($items);


(En overal consistent parent gebruiken, niet 'parrent' ;))

In plaats van je hele item meegeven kan je daar ook je url/titel/class etc. toekennen natuurlijk, zodat je een logisch object hebt.

In je view hoef je dan alleen de waardes te tonen, ook met recursie

PHP:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function render_menu($tree) {
    $output = '';
    if ($tree) {
        $output .= '<ul>';
        foreach ($tree as $item) {
            $output .= '<li><a href="'.$item['url'].'" class="'.$item['class'].'">'.$item['title'].'</a></li>';
            if ($item['children']) {
                $output .= render_menu($item['children']);
            }
        }
        $output .= '</ul>';
    }

    return $output;
}
Pagina: 1