আরেকটি, সমতল কাঠামোটিকে $tree
শ্রেণিবিন্যাসে রূপান্তর করার আরও সহজ উপায় । এটি প্রকাশের জন্য কেবলমাত্র একটি অস্থায়ী অ্যারে প্রয়োজন:
// add children to parents
$flat = array(); # temporary array
foreach ($tree as $name => $parent)
{
$flat[$name]['name'] = $name; # self
if (NULL === $parent)
{
# no parent, is root element, assign it to $tree
$tree = &$flat[$name];
}
else
{
# has parent, add self as child
$flat[$parent]['children'][] = &$flat[$name];
}
}
unset($flat);
শ্রেণিবিন্যাসকে বহুমাত্রিক অ্যারে রূপান্তর করার জন্য এগুলিই:
Array
(
[children] => Array
(
[0] => Array
(
[children] => Array
(
[0] => Array
(
[name] => H
)
[1] => Array
(
[name] => F
)
)
[name] => G
)
[1] => Array
(
[name] => E
[children] => Array
(
[0] => Array
(
[name] => A
)
[1] => Array
(
[children] => Array
(
[0] => Array
(
[name] => B
)
)
[name] => C
)
)
)
)
[name] => D
)
যদি আপনি পুনরাবৃত্তি এড়াতে চান তবে আউটপুট কম তুচ্ছ হয় (বড় কাঠামোর সাথে বোঝা হতে পারে)।
অ্যারে আউটপুট দেওয়ার জন্য আমি সবসময়ই ইউএল / এলআই "দ্বিধা" সমাধান করতে চেয়েছিলাম। দ্বিধাটি হ'ল, প্রতিটি আইটেমই জানে না বাচ্চারা অনুসরণ করবে কি না বা পূর্ববর্তী কতগুলি উপাদান বন্ধ করা দরকার। অন্য উত্তরে আমি ইতিমধ্যে সমাধান করেছি যে আমার নিজের লিখিত প্রদত্ত একটি মেটা-তথ্য RecursiveIteratorIterator
ব্যবহার করে getDepth()
এবং অন্যান্য মেটা-তথ্য সন্ধান করে Iterator
: নেস্টেড সেট মডেলটিকে একটি <ul>
"বদ্ধ" সাবট্রিজ লুকিয়ে রাখা । এই উত্তর পাশাপাশি দেখায় যে পুনরাবৃত্তকারীদের সাথে আপনি বেশ নমনীয়।
তবে এটি একটি পূর্ব-সাজানো তালিকা ছিল, সুতরাং আপনার উদাহরণের জন্য উপযুক্ত হবে না। অতিরিক্তভাবে আমি সর্বদা এক ধরণের স্ট্যান্ডার্ড ট্রি স্ট্রাকচার এবং এইচটিএমএল <ul>
এবং এর জন্য এটি সমাধান করতে চেয়েছিলাম<li>
উপাদানগুলির ।
আমি যে প্রাথমিক ধারণাটি সামনে এলাম তা হ'ল:
TreeNode
- প্রতিটি উপাদানকে একটি সরল TreeNode
প্রকারে বিমুক্ত করে যা এর মান (যেমন Name
) সরবরাহ করতে পারে এবং এতে বাচ্চা আছে কি না।
TreeNodesIterator
- RecursiveIterator
এগুলির মধ্যে একটি সেট (অ্যারে) এর মাধ্যমে পুনরাবৃত্তি করতে সক্ষম TreeNodes
। এটি মোটামুটি সহজ কারণ TreeNode
টাইপটি ইতিমধ্যে জানে যে এর কোনও সন্তান রয়েছে এবং কোনটি।
RecursiveListIterator
- RecursiveIteratorIterator
এটির যে কোনও ইভেন্টের পুনরাবৃত্তভাবে যে কোনও ধরণের পুনরুক্তি ঘটে যখন প্রয়োজনীয় সমস্ত ইভেন্ট রয়েছে RecursiveIterator
:
beginIteration
/ endIteration
- মূল তালিকার শুরু এবং শেষ।
beginElement
/ endElement
- প্রতিটি উপাদানটির শুরু এবং শেষ।
beginChildren
/ endChildren
- প্রতিটি বাচ্চার তালিকার শুরু এবং শেষ। এটি RecursiveListIterator
কেবলমাত্র ফাংশন কলগুলির আকারে এই ইভেন্টগুলি সরবরাহ করে। শিশুদের তালিকাগুলি, যেমন এটি তালিকার পক্ষে সাধারণ <ul><li>
, তত্ক্ষণাত এটির মূল <li>
উপাদানটির ভিতরে খোলা এবং বন্ধ করা হয় । অতএব endElement
ঘটনাটি সেই endChildren
ইভেন্টের পরে বরখাস্ত করা হয় । এই শ্রেণীর ব্যবহারকে প্রশস্ত করতে এটি পরিবর্তন বা কনফিগারযোগ্য তৈরি করা যেতে পারে। ঘটনাগুলি আলাদা রাখার জন্য ইভেন্টগুলি তখন ডেকরেটর অবজেক্টে ফাংশন কল হিসাবে বিতরণ করা হয়।
ListDecorator
- একটি "সজ্জাকারী" শ্রেণি যা কেবলমাত্র ইভেন্টগুলির গ্রহণযোগ্য RecursiveListIterator
।
আমি মূল আউটপুট যুক্তি দিয়ে শুরু করি। এখন শ্রেণিবদ্ধ $tree
অ্যারে নেওয়া , চূড়ান্ত কোড নীচের মত দেখাচ্ছে:
$root = new TreeNode($tree);
$it = new TreeNodesIterator(array($root));
$rit = new RecursiveListIterator($it);
$decor = new ListDecorator($rit);
$rit->addDecorator($decor);
foreach($rit as $item)
{
$inset = $decor->inset(1);
printf("%s%s\n", $inset, $item->getName());
}
প্রথমে আসুন এটি খতিয়ে দেখি ListDecorator
যা কেবল <ul>
এবং <li>
উপাদানগুলিকে আবৃত করে এবং কীভাবে তালিকা কাঠামোটি আউটপুট হয় সে সম্পর্কে সিদ্ধান্ত নিচ্ছে:
class ListDecorator
{
private $iterator;
public function __construct(RecursiveListIterator $iterator)
{
$this->iterator = $iterator;
}
public function inset($add = 0)
{
return str_repeat(' ', $this->iterator->getDepth()*2+$add);
}
কনস্ট্রাক্টর এটিতে কাজ করা তালিকার পুনরাবৃত্তি গ্রহণ করে। inset
আউটপুটটির সুন্দর ইন্ডেন্টেশনের জন্য কেবল একটি সহায়ক ফাংশন। বাকি প্রতিটি ইভেন্টের জন্য আউটপুট ফাংশন:
public function beginElement()
{
printf("%s<li>\n", $this->inset());
}
public function endElement()
{
printf("%s</li>\n", $this->inset());
}
public function beginChildren()
{
printf("%s<ul>\n", $this->inset(-1));
}
public function endChildren()
{
printf("%s</ul>\n", $this->inset(-1));
}
public function beginIteration()
{
printf("%s<ul>\n", $this->inset());
}
public function endIteration()
{
printf("%s</ul>\n", $this->inset());
}
}
এই আউটপুট ফাংশনগুলি মাথায় রেখে, এটি আবার প্রধান আউটপুট মোড়ানো / লুপ, আমি ধাপে ধাপে এটি দিয়ে যাচ্ছি:
$root = new TreeNode($tree);
মূলটি তৈরি করুন TreeNode
যা পুনরাবৃত্তি শুরু করতে ব্যবহৃত হবে:
$it = new TreeNodesIterator(array($root));
এটি TreeNodesIterator
এমন একটি RecursiveIterator
যা একক $root
নোডের মাধ্যমে পুনরাবৃত্তির পুনরাবৃত্তি সক্ষম করে । এটি অ্যারে হিসাবে পাস করা হয়েছে কারণ সেই শ্রেণীর পুনরাবৃত্তি করার জন্য কিছু দরকার এবং বাচ্চাদের একটি সেট দিয়ে পুনরায় ব্যবহারের অনুমতি দেয় যা TreeNode
উপাদানগুলির একটি অ্যারেও ।
$rit = new RecursiveListIterator($it);
এটি RecursiveListIterator
এমন একটি RecursiveIteratorIterator
যা বলা ঘটনাগুলি সরবরাহ করে। এটির ব্যবহার করতে, কেবলমাত্র ListDecorator
(উপরের শ্রেণি) সরবরাহ করতে হবে এবং এর সাথে নিযুক্ত করা হবে addDecorator
:
$decor = new ListDecorator($rit);
$rit->addDecorator($decor);
তারপরে সবকিছু ঠিকঠাক foreach
হয়ে যায় এবং প্রতিটি নোড আউটপুট করে:
foreach($rit as $item)
{
$inset = $decor->inset(1);
printf("%s%s\n", $inset, $item->getName());
}
যেমন এই উদাহরণটি দেখায়, পুরো আউটপুট যুক্তি ListDecorator
ক্লাসে এবং এই এককটিতে আবদ্ধforeach
। সম্পূর্ণ পুনরাবৃত্তির ট্র্যাভারসাল পুরোপুরি এসপিএল পুনরাবৃত্ত পুনরাবৃত্তকারীগুলিতে সম্পূর্ণভাবে আবদ্ধ করা হয়েছে যা একটি স্ট্যাকড পদ্ধতি সরবরাহ করে, এর অর্থ অভ্যন্তরীণভাবে কোনও পুনরাবৃত্তি ফাংশন কল করা হয় না।
ইভেন্ট ভিত্তিক ListDecorator
আপনাকে বিশেষভাবে আউটপুট পরিবর্তন করতে এবং একই ডেটা কাঠামোর জন্য একাধিক ধরণের তালিকা সরবরাহ করতে দেয় allows এমনকি অ্যারে ডেটাটি ইনপ্যাপুলেট করা হওয়ায় ইনপুটটি পরিবর্তন করা এমনকি সম্ভবTreeNode
।
সম্পূর্ণ কোড উদাহরণ:
<?php
namespace My;
$tree = array('H' => 'G', 'F' => 'G', 'G' => 'D', 'E' => 'D', 'A' => 'E', 'B' => 'C', 'C' => 'E', 'D' => null);
// add children to parents
$flat = array(); # temporary array
foreach ($tree as $name => $parent)
{
$flat[$name]['name'] = $name; # self
if (NULL === $parent)
{
# no parent, is root element, assign it to $tree
$tree = &$flat[$name];
}
else
{
# has parent, add self as child
$flat[$parent]['children'][] = &$flat[$name];
}
}
unset($flat);
class TreeNode
{
protected $data;
public function __construct(array $element)
{
if (!isset($element['name']))
throw new InvalidArgumentException('Element has no name.');
if (isset($element['children']) && !is_array($element['children']))
throw new InvalidArgumentException('Element has invalid children.');
$this->data = $element;
}
public function getName()
{
return $this->data['name'];
}
public function hasChildren()
{
return isset($this->data['children']) && count($this->data['children']);
}
/**
* @return array of child TreeNode elements
*/
public function getChildren()
{
$children = $this->hasChildren() ? $this->data['children'] : array();
$class = get_called_class();
foreach($children as &$element)
{
$element = new $class($element);
}
unset($element);
return $children;
}
}
class TreeNodesIterator implements \RecursiveIterator
{
private $nodes;
public function __construct(array $nodes)
{
$this->nodes = new \ArrayIterator($nodes);
}
public function getInnerIterator()
{
return $this->nodes;
}
public function getChildren()
{
return new TreeNodesIterator($this->nodes->current()->getChildren());
}
public function hasChildren()
{
return $this->nodes->current()->hasChildren();
}
public function rewind()
{
$this->nodes->rewind();
}
public function valid()
{
return $this->nodes->valid();
}
public function current()
{
return $this->nodes->current();
}
public function key()
{
return $this->nodes->key();
}
public function next()
{
return $this->nodes->next();
}
}
class RecursiveListIterator extends \RecursiveIteratorIterator
{
private $elements;
/**
* @var ListDecorator
*/
private $decorator;
public function addDecorator(ListDecorator $decorator)
{
$this->decorator = $decorator;
}
public function __construct($iterator, $mode = \RecursiveIteratorIterator::SELF_FIRST, $flags = 0)
{
parent::__construct($iterator, $mode, $flags);
}
private function event($name)
{
// event debug code: printf("--- %'.-20s --- (Depth: %d, Element: %d)\n", $name, $this->getDepth(), @$this->elements[$this->getDepth()]);
$callback = array($this->decorator, $name);
is_callable($callback) && call_user_func($callback);
}
public function beginElement()
{
$this->event('beginElement');
}
public function beginChildren()
{
$this->event('beginChildren');
}
public function endChildren()
{
$this->testEndElement();
$this->event('endChildren');
}
private function testEndElement($depthOffset = 0)
{
$depth = $this->getDepth() + $depthOffset;
isset($this->elements[$depth]) || $this->elements[$depth] = 0;
$this->elements[$depth] && $this->event('endElement');
}
public function nextElement()
{
$this->testEndElement();
$this->event('{nextElement}');
$this->event('beginElement');
$this->elements[$this->getDepth()] = 1;
}
public function beginIteration()
{
$this->event('beginIteration');
}
public function endIteration()
{
$this->testEndElement();
$this->event('endIteration');
}
}
class ListDecorator
{
private $iterator;
public function __construct(RecursiveListIterator $iterator)
{
$this->iterator = $iterator;
}
public function inset($add = 0)
{
return str_repeat(' ', $this->iterator->getDepth()*2+$add);
}
public function beginElement()
{
printf("%s<li>\n", $this->inset(1));
}
public function endElement()
{
printf("%s</li>\n", $this->inset(1));
}
public function beginChildren()
{
printf("%s<ul>\n", $this->inset());
}
public function endChildren()
{
printf("%s</ul>\n", $this->inset());
}
public function beginIteration()
{
printf("%s<ul>\n", $this->inset());
}
public function endIteration()
{
printf("%s</ul>\n", $this->inset());
}
}
$root = new TreeNode($tree);
$it = new TreeNodesIterator(array($root));
$rit = new RecursiveListIterator($it);
$decor = new ListDecorator($rit);
$rit->addDecorator($decor);
foreach($rit as $item)
{
$inset = $decor->inset(2);
printf("%s%s\n", $inset, $item->getName());
}
Outpupt:
<ul>
<li>
D
<ul>
<li>
G
<ul>
<li>
H
</li>
<li>
F
</li>
</ul>
</li>
<li>
E
<ul>
</li>
<li>
A
</li>
<li>
C
<ul>
<li>
B
</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
</ul>
ডেমো (পিএইচপি 5.2 বৈকল্পিক)
একটি সম্ভাব্য রূপটি RecursiveIterator
একটি পুনরাবৃত্তি হবে যা যে কোনওটির উপরে পুনরাবৃত্তি করে এবং ঘটতে পারে এমন সমস্ত ইভেন্টের উপর একটি পুনরাবৃত্তি সরবরাহ করে। ফোরচ লুপের অভ্যন্তরে একটি স্যুইচ / কেস তারপরে ঘটনাগুলি মোকাবেলা করতে পারে।
সম্পর্কিত: