-
-
Notifications
You must be signed in to change notification settings - Fork 72
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[DataStructure] Introduce the DataStructure component
- Loading branch information
Showing
7 changed files
with
506 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,137 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace Psl\DataStructure; | ||
|
||
use Psl; | ||
use Psl\Arr; | ||
use Psl\Math; | ||
|
||
/** | ||
* @psalm-template T | ||
* | ||
* @implements PriorityQueueInterface<T> | ||
*/ | ||
final class PriorityQueue implements PriorityQueueInterface | ||
{ | ||
/** | ||
* @psalm-var array<int, list<T>> | ||
*/ | ||
private array $queue = []; | ||
|
||
/** | ||
* Adds a node to the queue. | ||
* | ||
* @psalm-param T $node | ||
*/ | ||
public function enqueue(int $priority, $node): void | ||
{ | ||
$nodes = $this->queue[$priority] ?? []; | ||
$nodes[] = $node; | ||
|
||
$this->queue[$priority] = $nodes; | ||
$this->queue = Arr\filter( | ||
$this->queue, | ||
static fn(array $list): bool => Arr\count($list) !== 0 | ||
); | ||
} | ||
|
||
/** | ||
* Retrieves, but does not remove, the node at the head of this queue, | ||
* or returns null if this queue is empty. | ||
* | ||
* @psalm-return null|T | ||
*/ | ||
public function peek() | ||
{ | ||
if (0 === $this->count()) { | ||
return null; | ||
} | ||
|
||
/** @psalm-suppress MissingThrowsDocblock - we are sure that the queue is not empty. */ | ||
return $this->fetch(false); | ||
} | ||
|
||
/** | ||
* Retrieves and removes the node at the head of this queue, | ||
* or returns null if this queue is empty. | ||
* | ||
* @psalm-return null|T | ||
*/ | ||
public function pull() | ||
{ | ||
if (0 === $this->count()) { | ||
return null; | ||
} | ||
|
||
/** @psalm-suppress MissingThrowsDocblock - we are sure that the queue is not empty. */ | ||
return $this->fetch(true); | ||
} | ||
|
||
/** | ||
* Dequeues a node from the queue. | ||
* | ||
* @psalm-return T | ||
* | ||
* @throws Psl\Exception\InvariantViolationException If the Queue is invalid. | ||
*/ | ||
public function dequeue() | ||
{ | ||
return $this->fetch(true); | ||
} | ||
|
||
/** | ||
* @psalm-return T | ||
* | ||
* @throws Psl\Exception\InvariantViolationException If the Queue is invalid. | ||
*/ | ||
private function fetch(bool $remove) | ||
{ | ||
Psl\invariant(0 !== $this->count(), 'Cannot dequeue a node from an empty Queue.'); | ||
|
||
// Retrieve the list of priorities. | ||
$priorities = Arr\keys($this->queue); | ||
/** | ||
* Retrieve the highest priority. | ||
* | ||
* @var int $priority | ||
*/ | ||
$priority = Math\max($priorities); | ||
// Retrieve the list of nodes with the priority `$priority`. | ||
$nodes = Arr\at($this->queue, $priority); | ||
/** | ||
* Retrieve the first node of the list. | ||
* | ||
* @psalm-suppress MissingThrowsDocblock - we are sure that the list is not empty. | ||
*/ | ||
$node = Arr\firstx($nodes); | ||
|
||
// Remove the node if we are supposed to. | ||
if ($remove) { | ||
// If the list contained only this node, | ||
// remove the list of nodes with priority `$priority`. | ||
if (1 === Arr\count($nodes)) { | ||
unset($this->queue[$priority]); | ||
} else { | ||
// otherwise, drop the first node. | ||
$this->queue[$priority] = Arr\values(Arr\drop($nodes, 1)); | ||
} | ||
} | ||
|
||
return $node; | ||
} | ||
|
||
/** | ||
* Count the nodes in the queue. | ||
*/ | ||
public function count(): int | ||
{ | ||
$count = 0; | ||
foreach ($this->queue as $priority => $list) { | ||
$count += Arr\count($list); | ||
} | ||
|
||
return $count; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace Psl\DataStructure; | ||
|
||
use Psl; | ||
use Countable; | ||
|
||
/** | ||
* @template T | ||
*/ | ||
interface PriorityQueueInterface extends Countable | ||
{ | ||
/** | ||
* Adds a node to the queue. | ||
* | ||
* @psalm-param T $node | ||
*/ | ||
public function enqueue(int $priority, $node): void; | ||
|
||
/** | ||
* Retrieves, but does not remove, the node at the head of this queue, | ||
* or returns null if this queue is empty. | ||
* | ||
* @psalm-return null|T | ||
*/ | ||
public function peek(); | ||
|
||
/** | ||
* Retrieves and removes the node at the head of this queue, | ||
* or returns null if this queue is empty. | ||
* | ||
* @psalm-return null|T | ||
*/ | ||
public function pull(); | ||
|
||
/** | ||
* Retrieves and removes the node at the head of this queue. | ||
* | ||
* @psalm-return T | ||
* | ||
* @throws Psl\Exception\InvariantViolationException If the Queue is invalid. | ||
*/ | ||
public function dequeue(); | ||
|
||
/** | ||
* Count the nodes in the queue. | ||
*/ | ||
public function count(): int; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace Psl\DataStructure; | ||
|
||
use Psl; | ||
use Psl\Arr; | ||
|
||
/** | ||
* @psalm-template T | ||
* | ||
* @implements QueueInterface<T> | ||
*/ | ||
final class Queue implements QueueInterface | ||
{ | ||
/** | ||
* @psalm-var list<T> | ||
*/ | ||
private array $queue = []; | ||
|
||
/** | ||
* Adds a node to the queue. | ||
* | ||
* @psalm-param T $node | ||
*/ | ||
public function enqueue($node): void | ||
{ | ||
$this->queue[] = $node; | ||
} | ||
|
||
/** | ||
* Retrieves, but does not remove, the node at the head of this queue, | ||
* or returns null if this queue is empty. | ||
* | ||
* @psalm-return null|T | ||
*/ | ||
public function peek() | ||
{ | ||
return Arr\first($this->queue); | ||
} | ||
|
||
/** | ||
* Retrieves and removes the node at the head of this queue, | ||
* or returns null if this queue is empty. | ||
* | ||
* @psalm-return null|T | ||
*/ | ||
public function pull() | ||
{ | ||
if (0 === $this->count()) { | ||
return null; | ||
} | ||
|
||
/** @psalm-suppress MissingThrowsDocblock - we are sure that the queue is not empty. */ | ||
return $this->dequeue(); | ||
} | ||
|
||
/** | ||
* Dequeues a node from the queue. | ||
* | ||
* @psalm-return T | ||
* | ||
* @throws Psl\Exception\InvariantViolationException If the Queue is invalid. | ||
*/ | ||
public function dequeue() | ||
{ | ||
Psl\invariant(0 !== $this->count(), 'Cannot dequeue a node from an empty Queue.'); | ||
|
||
$node = Arr\firstx($this->queue); | ||
$this->queue = Arr\values(Arr\drop($this->queue, 1)); | ||
|
||
return $node; | ||
} | ||
|
||
/** | ||
* Count the nodes in the queue. | ||
*/ | ||
public function count(): int | ||
{ | ||
return Arr\count($this->queue); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace Psl\DataStructure; | ||
|
||
use Psl; | ||
use Countable; | ||
|
||
/** | ||
* @template T | ||
*/ | ||
interface QueueInterface extends Countable | ||
{ | ||
/** | ||
* Adds a node to the queue. | ||
* | ||
* @psalm-param T $node | ||
*/ | ||
public function enqueue($node): void; | ||
|
||
/** | ||
* Retrieves, but does not remove, the node at the head of this queue, | ||
* or returns null if this queue is empty. | ||
* | ||
* @psalm-return null|T | ||
*/ | ||
public function peek(); | ||
|
||
/** | ||
* Retrieves and removes the node at the head of this queue, | ||
* or returns null if this queue is empty. | ||
* | ||
* @psalm-return null|T | ||
*/ | ||
public function pull(); | ||
|
||
/** | ||
* Retrieves and removes the node at the head of this queue. | ||
* | ||
* @psalm-return T | ||
* | ||
* @throws Psl\Exception\InvariantViolationException If the Queue is invalid. | ||
*/ | ||
public function dequeue(); | ||
|
||
/** | ||
* Count the nodes in the queue. | ||
*/ | ||
public function count(): int; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.