Uma biblioteca simples de algarismos romanos em PHP
Esta biblioteca inclui alguns filtros simples para converter uma string
com número romano em um int
que representa a entrada como decimal e decimal int
em uma string
com número romano como resultado.
use Romans Filter RomanToInt ;
$ filter = new RomanToInt ();
$ result = $ filter -> filter ( ' MCMXCIX ' ); // 1999
use Romans Filter IntToRoman ;
$ filter = new IntToRoman ();
$ result = $ filter -> filter ( 1999 ); // MCMXCIX
Este pacote usa o Composer como repositório padrão. Você pode instalá-lo adicionando o nome do pacote na seção require
do composer.json
, apontando para a última versão estável.
{
"require" : {
"wandersonwhcr/romans" : " ^1.0 "
}
}
Além disso, Romanos usa versionamento semântico. As seguintes versões de pacote suportam estas versões de PHP:
1.0.*
: PHP ^7.0
(Augusto)1.1.*
: PHP ^7.0
(Tibério)1.2.*
: PHP >=7.4
(Calígula)1.3.*
: PHP >=7.4
(Cláudio)1.4.*
: PHP >=7.4
(Nero)1.5.*
: PHP >=8.0
(Galba) Esta biblioteca pode ser utilizada como dependência para projetos, facilitando integrações com bibliotecas ou frameworks. Caso queira adicionar mais itens nesta lista, por favor, abra um issue ou crie um pull request, adicionando seu projeto em ordem alfabética.
O pacote Romans
usa uma abordagem Lexer-Parser e um Autômato Finito Determinístico (DFA) para converter o número romano em int
, usando uma biblioteca Grammar Token.
use Romans Grammar Grammar ;
use Romans Lexer Lexer ;
use Romans Parser Parser ;
$ grammar = new Grammar ();
$ lexer = new Lexer ( $ grammar );
$ parser = new Parser ( $ grammar );
$ tokens = $ lexer -> tokenize ( ' MCMXCIX ' );
/*
$tokens = [
0 => 'M' // Grammar::T_M
1 => 'C', // Grammar::T_C
2 => 'M', // Grammar::T_M
3 => 'X', // Grammar::T_X
4 => 'C', // Grammar::T_C
5 => 'I', // Grammar::T_I
6 => 'X', // Grammar::T_X
];
*/
$ result = $ parser -> parse ( $ tokens ); // 1999
O filtro RomanToInt
usa Lexer para tokenizar a entrada e Parser para construir o número int
. Quando erros são encontrados, o Lexer ou Parser lança exceções para notificar o problema com um código específico.
use Romans Filter RomanToInt ;
use Romans Lexer Exception as LexerException ;
use Romans Parser Exception as ParserException ;
$ filter = new RomanToInt ();
try {
$ filter -> filter ( $ input );
} catch ( LexerException $ e ) {
// Unknown Token (LexerException::UNKNOWN_TOKEN)
} catch ( ParserException $ e ) {
// Invalid Token Type (Not String) (ParserException::INVALID_TOKEN_TYPE)
// Unknown Token (ParserException::UNKNOWN_TOKEN)
// Invalid Roman (ParserException::INVALID_ROMAN)
}
Você pode usar esta estrutura para validar números romanos, adicionando um bloco try..catch para verificar se $input
é válido. Além disso, você pode validar se um int
pode ser filtrado para Roman usando um filtro IntToRoman
.
use Romans Filter IntToRoman ;
use Romans Filter Exception as FilterException ;
$ filter = new IntToRoman ();
try {
$ filter -> filter ( $ input );
} catch ( FilterException $ e ) {
// Invalid Integer (< 0) (FilterException::INVALID_INTEGER)
}
O valor zero é representado como string
"N"
, a inicial de nulla ou nihil , a palavra latina para "nada" (ver referências).
use Romans Filter RomanToInt ;
use Romans Filter IntToRoman ;
$ filter = new RomanToInt ();
$ result = $ filter -> filter ( ' N ' ); // 0 (Zero)
$ filter = new IntToRoman ();
$ result = $ filter -> filter ( 0 ); // N
Este pacote usa PSR-6 Caching Interface para melhorar a execução, principalmente em loops (como while
ou foreach
) usando bibliotecas de cache. Qualquer implementação PSR-6 pode ser usada e sugerimos o pacote Symfony Cache.
use Romans Filter IntToRoman ;
use Romans Filter RomanToInt ;
use Symfony Component Cache Adapter ArrayAdapter ;
$ cache = new ArrayAdapter ();
$ filter = new RomanToInt ();
$ filter -> setCache ( $ cache );
$ result = $ filter -> filter ( ' MCMXCIX ' ); // 1999
$ result = $ filter -> filter ( ' MCMXCIX ' ); // 1999 (from cache)
$ filter = new IntToRoman ();
$ filter -> setCache ( $ cache );
$ result = $ filter -> filter ( 1999 ); // MCMXCIX
$ result = $ filter -> filter ( 1999 ); // MCMXCIX (from cache)
Você pode usar o Docker Compose para construir uma imagem e executar um contêiner para desenvolver e testar este pacote.
docker-compose build
docker-compose run --rm romans composer install
docker-compose run --rm romans composer test
Esta seção descreve técnicas usadas por este pacote para converter números romanos em int
e vice-versa.
A abordagem Lexer-Parser foi escolhida porque a validação e leitura de números romanos são mais simplificadas: o Lexer é responsável por ler e transformar a entrada em tokens, validando o conteúdo no nível char; e o Parser é responsável por transformar tokens em números, validar o conteúdo em nível de posição e converter para int
através de um DFA.
A Wikipedia diz que “a análise lexical é o processo de conversão de uma sequência de caracteres em uma sequência de tokens”, que “é uma estrutura que representa um lexema que indica explicitamente sua categorização para fins de análise”. Ainda, a Wikipedia diz que “análise ou análise sintática é o processo de análise de símbolos em conformidade com as regras de uma gramática formal”.
Esta estrutura facilita o desenvolvimento de uma estrutura responsável por ler uma string
de entrada, convertendo-a para outra estrutura de acordo com o charset específico e sua posição dentro da entrada.
Um AFD foi desenvolvido para verificar se uma string
com número romano é válida. Esta técnica foi escolhida porque outras implementações simplesmente convertem a entrada sem verificar regras, como quatro caracteres sequenciais.
A definição atual do autômato é declarada abaixo.
M = (Q, Σ, δ, q0, F)
Q = { a, b, c, d, e, f, g, y, z }
Σ = { I, V, X, L, C, D, M, N }
q0 = g
F = { z }
z -> ε
y -> $z
a -> y | Iy | IIy | IIIy
b -> a | IVy | Va | IXy
c -> b | Xb | XXb | XXXb
d -> c | XLb | Lc | XCb
e -> d | Cd | CCd | CCCd
f -> e | CDd | De | CMd
g -> f | Ny | Mg
Este pacote é de código aberto e está disponível sob a licença MIT descrita em LICENSE.