-
-
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
10 changed files
with
697 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($node, int $priority = 0): 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,23 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace Psl\DataStructure; | ||
|
||
use Psl; | ||
use Countable; | ||
|
||
/** | ||
* @template T | ||
* | ||
* @extends QueueInterface<T> | ||
*/ | ||
interface PriorityQueueInterface extends QueueInterface | ||
{ | ||
/** | ||
* Adds a node to the queue. | ||
* | ||
* @psalm-param T $node | ||
*/ | ||
public function enqueue($node, int $priority = 0): void; | ||
} |
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,85 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace Psl\DataStructure; | ||
|
||
use Psl; | ||
use Psl\Arr; | ||
|
||
/** | ||
* A basic implementation of a queue data structure ( FIFO ). | ||
* | ||
* @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,55 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace Psl\DataStructure; | ||
|
||
use Psl; | ||
use Countable; | ||
|
||
/** | ||
* An interface representing a queue data structure ( FIFO ). | ||
* | ||
* @template T | ||
* | ||
* @see https://en.wikipedia.org/wiki/FIFO_(computing_and_electronics) | ||
*/ | ||
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace Psl\DataStructure; | ||
|
||
use Psl; | ||
use Psl\Arr; | ||
|
||
/** | ||
* An basic implementation of a stack data structure ( LIFO ). | ||
* | ||
* @template T | ||
* | ||
* @implements StackInterface<T> | ||
*/ | ||
final class Stack implements StackInterface | ||
{ | ||
/** | ||
* @psalm-var list<T> $items | ||
*/ | ||
private array $items = []; | ||
|
||
/** | ||
* Adds an item to the stack. | ||
* | ||
* @psalm-param T $item | ||
*/ | ||
public function push($item): void | ||
{ | ||
$this->items[] = $item; | ||
} | ||
|
||
/** | ||
* Retrieves, but does remove, the most recently added item that was not yet removed, | ||
* or returns null if this queue is empty. | ||
* | ||
* @psalm-return null|T | ||
*/ | ||
public function peek() | ||
{ | ||
return Arr\last($this->items); | ||
} | ||
|
||
/** | ||
* Retrieves and removes the most recently added item that was not yet removed, | ||
* or returns null if this queue is empty. | ||
* | ||
* @psalm-return null|T | ||
*/ | ||
public function pull() | ||
{ | ||
if (0 === $this->count()) { | ||
return null; | ||
} | ||
|
||
/** @psalm-suppress MissingThrowsDocblock - the stack is not empty. */ | ||
return $this->pop(); | ||
} | ||
|
||
/** | ||
* Retrieve and removes the most recently added item that was not yet removed. | ||
* | ||
* @psalm-return T | ||
* | ||
* @throws Psl\Exception\InvariantViolationException If the stack is empty. | ||
*/ | ||
public function pop() | ||
{ | ||
Psl\invariant(0 !== $this->count(), 'Cannot pop an item from an empty Stack.'); | ||
|
||
$tail = Arr\lastx($this->items); | ||
$this->items = Arr\values(Arr\take($this->items, $this->count() - 1)); | ||
|
||
return $tail; | ||
} | ||
|
||
/** | ||
* Count the items in the stack. | ||
*/ | ||
public function count(): int | ||
{ | ||
return Arr\count($this->items); | ||
} | ||
} |
Oops, something went wrong.