Romanização de código


33

O desafio é tornar qualquer código romano válido no idioma escolhido.

Eles não devem aparecer dentro de strings ou algo parecido, mas funcionam como quaisquer outros tokens, literais como números (em árabe ), caracteres ou strings; ou identificadores de variável / método / função etc.

Por exemplo, em Java, o seguinte teria que compilar e executar como se tivesse isido inicializado para 42:

int i = XLII;

A análise real dos números é secundária; portanto, você pode usar uma biblioteca, se quiser, mas este é um concurso de popularidade; portanto, a criatividade é incentivada.

Você não pode usar nenhum idioma que realmente use algarismos romanos, se houver.

Boa sorte.


1
Então, precisamos escrever uma extensão para a linguagem, criando assim uma nova?
Kendall Frey

4
Eu vou reclamar se eu quiser, porque os idiomas que eu uso não são extensíveis assim, então eu não posso nem participar.
Kendall Frey

3
@KendallFrey A fonte precisaria compilar e executar. Para Java, você pode escrever um "compilador" que edita a fonte e compila programaticamente . Uma maneira de compilar é executando um #Process
Justin

1
Parece chato na maioria dos idiomas. Por exemplo, em python, eu simplesmente escreveria um script que usa astpara analisar a fonte. Insira na parte superior do AST a definição dos números romanos de 1 a 3999. Compile tudo e execute-o. É chato escrever o código para lidar com o processo.
Bakuriu 13/02/2014

2
@Bakuriu e seu comentário também é chato. Este é um concurso de popularidade, então você deve tentar criar algo divertido. Eu acho que há algumas respostas legais aqui que são mais imaginativas (do que compilar uma linguagem de script).
Daniero

Respostas:


40

C

Existem apenas numerais romanos, desde 4000 e superiores não possuem notação padrão, e o pré-processador é uma ferramenta de descompressão maravilhosa, especialmente se você não tiver problemas com o fato de o código ter um comportamento indefinido.

enum{_
#define a_
#define d_
#define g_
#define j_
#define a(x)c(x)b
#define b(x)c(x)a
#define c(x)x,
#define d(x)f(x)e
#define e(x)f(x)d
#define f(x)m(a(x)(x##I)(x##II)(x##III)(x##IV)(x##V)(x##VI)(x##VII)(x##VIII)(x##IX))
#define g(x)i(x)h
#define h(x)i(x)g
#define i(x)m(d(x)(x##X)(x##XX)(x##XXX)(x##XL)(x##L)(x##LX)(x##LXX)(x##LXXX)(x##XC))
#define j(x)l(x)k
#define k(x)l(x)j
#define l(x)m(g(x)(x##C)(x##CC)(x##CCC)(x##CD)(x##D)(x##DC)(x##DCC)(x##DCCC)(x##CM))
#define m(x)n(x)
#define n(...)__VA_ARGS__##_
m(j()(M)(MM)(MMM))
};

Isso define todos os algarismos romanos de Iaté MMMCMXCIXcomo constantes de enumeração, mais _(que podem ser substituídos por qualquer coisa que você quiser) como zero.


12
Absolutamente brilhante, proponha adicioná-lo à próxima versão C (C2X?) Como <roman.h>! Com os formatos% r e% R printf!

3
Eu pensei que sabia como usar o pré-processador, até agora: | Você poderia atualizar sua resposta com um exemplo de uso mínimo dessa enumeração?
Klingt.net

@YiminRong E scanftambém :) @ klingt.net Não sei ao certo que tipo de exemplo você está procurando. Um bastante simples seriaint main() { return MMMCMXCIX - M - M - M - CM - XC - IX; }
hvd

Gosto da ideia, mas por que você optou por um comportamento indefinido quando o comportamento definido era bastante simples? Não julgando, apenas curioso?
CasaDeRobison 14/02

1
@CasaDeRobison Por diversão, principalmente. Gosto porque é um dos poucos casos de comportamento indefinido no momento do pré-processamento que não é sinalizado como um erro pelos compiladores atuais e provavelmente não será no futuro. Eu nunca escrevo nada assim no código que deveria ser útil, mas o código postado aqui não deve ser útil, então qual a melhor ocasião para experimentá-lo?
HVD

15

Rubi

def Object.const_missing(const)
  i = const.to_s
  if i =~ /^[IVXLCDM]+$/
    #http://codegolf.stackexchange.com/questions/16254/convert-arbitrary-roman-numeral-input-to-integer-output
    %w[IV,4 IX,9 XL,40 XC,90 CD,400 CM,900 I,1 V,5 X,10 L,50 C,100 D,500 M,1000].map{|s|k,v=s.split ?,;i.gsub!(k,?++v)}
    eval(i)
  else
    super
  end
end

Todos os algarismos romanos (maiúsculos) agora serão analisados ​​como seus equivalentes decimais. O único problema é que eles ainda são atribuíveis: você pode fazer X = 9, mas não 10 = 9. Eu não acho que há uma maneira de consertar isso.


1
Isto é perfeito. A questão da atribuição não é um problema; Você pode usar atribuições para fazer todo tipo de coisa estúpida.
Daniero

12

JavaScript (ES6)

Use Proxypara pegar algarismos romanos.

Testável no Firefox (mais recente) no JSFiddle .
Não pode ser testado no Chrome (com Traceur), pois a Proxyimplementação foi interrompida.

// Convert roman numeral to integer.
// Borrowed from http://codegolf.stackexchange.com/a/20702/10920
var toDecimal = (s) => {
  var n = 0;
  var X = {
    M: 1e3, CM: 900, D: 500, CD: 400, C: 100, XC: 90, 
    L: 50,  XL: 40,  X: 10,  IX: 9,   V: 5,   IV: 4,  I: 1
  };

  s.replace(/[MDLV]|C[MD]?|X[CL]?|I[XV]?/g, (d) => n += X[d]);
  return n;
};

// Whether a string is a valid roman numeral.
var isRoman = (s) => s.match(/^[MDCLXVI]+$/);

// Profixy global environment to catch roman numerals.
// Since it uses `this`, global definitions are still accessible.
var romanEnv = new Proxy(this, {
  get: (target, name) => isRoman(name) ? toDecimal(name) : target[name],
  set: (target, name, value) => isRoman(name) ? undefined : (target[name] = value),
  has: (target, name) => isRoman(name) || name in target,
  hasOwn: (target, name) => name in target
});

Uso:

with (romanEnv) {
  var i = MIV + XLVIII;
  console.log(i); // 1052
}

10

C & C ++ (resposta atualizada)

Como observado em um comentário, minha solução original teve dois problemas:

  1. Parâmetros opcionais estão disponíveis apenas nos padrões C99 e posteriores da família de idiomas.
  2. A vírgula à direita na definição de enum também é específica para C99 e posterior.

Como eu queria que meu código fosse o mais genérico possível para trabalhar em plataformas mais antigas, decidi fazer outra tentativa. É mais longo do que era antes, mas funciona em compiladores e pré-processadores configurados no modo de compatibilidade C89 / C90. Todas as macros recebem um número apropriado de argumentos no código-fonte, embora às vezes essas macros "se expandam" para nada.

O Visual C ++ 2013 (versão 12) emite avisos sobre parâmetros ausentes, mas nem o mcpp (um pré-processador de código aberto que reivindica alta conformidade com o padrão) nem o gcc 4.8.1 (com -std = iso9899: 1990 -pedantic-errors switches) emitem avisos ou erros para essas chamadas de macro com uma lista de argumentos efetivamente vazia.

Depois de revisar o padrão relevante (ANSI / ISO 9899-1990, 6.8.3, Substituição de macro), acho que há ambiguidade suficiente para que isso não deva ser considerado fora do padrão. "O número de argumentos na invocação de uma macro do tipo função deve concordar com o número de parâmetros na definição da macro ...". Parece não impedir uma lista de argumentos vazia, desde que os parênteses necessários (e vírgulas no caso de vários parâmetros) estejam disponíveis para chamar a macro

Quanto ao problema de vírgula à direita, isso é resolvido adicionando um identificador extra à enumeração (no meu caso, MMMM, que parece tão razoável quanto qualquer outra coisa para o identificador seguir 3999, mesmo que não obedeça às regras aceitas do seqüenciamento de números romanos exatamente).

Uma solução um pouco mais limpa envolveria mover a enum e dar suporte a macros para um arquivo de cabeçalho separado, como foi implícito em um comentário em outro lugar, e usar undef dos nomes de macro imediatamente após serem usados, para evitar poluir o espaço para nome. Sem dúvida, nomes de macro melhores devem ser escolhidos também, mas isso é adequado para a tarefa em questão.

Minha solução atualizada, seguida pela minha solução original:

#define _0(i,v,x)
#define _1(i,v,x) i
#define _2(i,v,x) i##i
#define _3(i,v,x) i##i##i
#define _4(i,v,x) i##v
#define _5(i,v,x) v
#define _6(i,v,x) v##i
#define _7(i,v,x) v##i##i
#define _8(i,v,x) v##i##i##i
#define _9(i,v,x) i##x

#define k(p,s) p##s,
#define j(p,s) k(p,s)
#define i(p) j(p,_0(I,V,X)) j(p,_1(I,V,X)) j(p,_2(I,V,X)) j(p,_3(I,V,X)) j(p,_4(I,V,X)) j(p,_5(I,V,X)) j(p,_6(I,V,X)) j(p,_7(I,V,X)) j(p,_8(I,V,X)) j(p,_9(I,V,X))
#define h(p,s) i(p##s)
#define g(p,s) h(p,s)
#define f(p) g(p,_0(X,L,C)) g(p,_1(X,L,C)) g(p,_2(X,L,C)) g(p,_3(X,L,C)) g(p,_4(X,L,C)) g(p,_5(X,L,C)) g(p,_6(X,L,C)) g(p,_7(X,L,C)) g(p,_8(X,L,C)) g(p,_9(X,L,C))
#define e(p,s) f(p##s)
#define d(p,s) e(p,s)
#define c(p) d(p,_0(C,D,M)) d(p,_1(C,D,M)) d(p,_2(C,D,M)) d(p,_3(C,D,M)) d(p,_4(C,D,M)) d(p,_5(C,D,M)) d(p,_6(C,D,M)) d(p,_7(C,D,M)) d(p,_8(C,D,M)) d(p,_9(C,D,M))
#define b(p) c(p)
#define a() b(_0(M,N,O)) b(_1(M,N,O)) b(_2(M,N,O)) b(_3(M,N,O))

enum { _ a() MMMM };

#include <stdio.h>

int main(int argc, char** argv)
{
    printf("%d", MMMCMXCIX * MMMCMXCIX);
    return 0;
}

A resposta original (que recebeu as seis primeiras votações anteriores, portanto, se ninguém mais votá-las novamente, não pense que minha solução atualizada recebeu as votações anteriores):

No mesmo espírito de uma resposta anterior, mas feita de uma maneira que deveria ser portátil usando apenas um comportamento definido (embora ambientes diferentes nem sempre concordem em alguns aspectos do pré-processador). Trata alguns parâmetros como opcionais, ignora outros, deve funcionar em pré-processadores que não suportam a __VA_ARGS__macro, incluindo C ++, usa macros indiretas para garantir que os parâmetros sejam expandidos antes da colagem do token e, finalmente, é mais curto e acho mais fácil de ler ( embora ainda seja complicado e provavelmente não seja fácil de ler, apenas mais fácil):

#define g(_,__) _, _##I, _##II, _##III, _##IV, _##V, _##VI, _##VII, _##VIII, _##IX,
#define f(_,__) g(_,)
#define e(_,__) f(_,) f(_##X,) f(_##XX,) f(_##XXX,) f(_##XL,) f(_##L,) f(_##LX,) f(_##LXX,) f(_##LXXX,) f(_##XC,)
#define d(_,__) e(_,)
#define c(_,__) d(_,) d(_##C,) d(_##CC,) d(_##CCC,) d(_##CD,) d(_##D,) d(_##DC,) d(_##DCC,) d(_##DCCC,) d(_##CM,)
#define b(_,__) c(_,)
#define a       b(,) b(M,) b(MM,) b(MMM,)
enum { _ a };

1
+1, mas observe que o uso de argumentos de macro vazios e a vírgula no final de uma lista de enumeradores são novos recursos do C99 e C ++ 11, e ambos são compatíveis com C99 e C ++ 11 __VA_ARGS__.
HVD

Dang, se você não está certo! Acho que tenho visto isso usado como extensões esse tempo todo. Ah bem. :)
CasaDeRobison 15/02

8

Lisp comum

A seguir, uma explicação bastante longa de como eu criei uma macro que você pode usar assim:

(roman-progn
  (+ XIV XXVIII))

Quando uma macro é chamada no Common Lisp, ela basicamente atua como uma função, apenas que os argumentos são recebidos antes de serem avaliados. Na verdade, como no código Common Lisp são apenas dados, o que recebemos é uma lista (aninhada) representando uma árvore de sintaxe não analisada com a qual podemos fazer o que quisermos, e isso é feito em tempo de compilação.

Funções auxiliares

O primeiro passo do plano é pegar essa árvore e verificar se há algo parecido com algarismos romanos. Sendo Lisp e tudo, vamos tentar fazê-lo de alguma forma funcional: Precisamos de uma função que faça uma travessia profunda de uma árvore e retorne todos os objetos para os quais uma função fornecida searchpretorne verdadeira. Este é par (semi) recursivo da cauda.

(defun deep-find-all (searchp tree &optional found)
  (if (null tree)
    found
    (let* ((item (car tree))
           (new-found (if (consp item)
                        (deep-find-all searchp item found)
                        (if (funcall searchp item)
                          (cons item found)
                          found))))

      (deep-find-all searchp (cdr tree) new-found))))

Depois, algum código para analisar os algarismos romanos, cortesia do Código Rosetta :

(defun mapcn (chars nums string)
  (loop as char across string as i = (position char chars) collect (and i (nth i nums))))

(defun parse-roman (R)
  (loop with nums = (mapcn "IVXLCDM" '(1 5 10 50 100 500 1000) R)
        as (A B) on nums if A sum (if (and B (< A B)) (- A) A)))

A macro real

Pegamos a árvore de sintaxe ( body), pesquisamos com nosso procedimento de encontrar todos os detalhes e, de alguma forma, disponibilizamos os algarismos romanos que encontramos.

(defmacro roman-progn (&body body)
  (let* ((symbols (deep-find-all (lambda (sym)
                                   (and
                                     (symbolp sym)
                                     (loop for c across (string sym)
                                           always (find c "IVXLCDM")))) body))
         (parsed-romans (mapcar (lambda (sym)
                                  (list sym (parse-roman (string sym)))) symbols)))

    (if parsed-romans
      `(let (,@parsed-romans)
         ,@body)  
      `(progn
         ,@body))))

Então o que é 1 + 2 + 3 + (4 * (5 + 6)) + 7?

(roman-progn
  (+ I II III (* IV (+ V VI)) VII))
=> 57

E para ver o que realmente aconteceu quando a macro foi chamada:

(macroexpand-1 +)
=> (LET ((VII 7) (VI 6) (V 5) (IV 4) (III 3) (II 2) (I 1))
   (+ I II III (* IV (+ V VI)) VII))

7

Lua

setmetatable(_G, {
    __index = function(_, k)
        if k:match"^[IVXLCDM]*$" then
            local n = 0
            for _, v in ipairs{{IV = 4}, {IX = 9}, {I = 1}, {V = 5}, {XL = 40}, {X = 10}, {XC = 900}, {CD = 400}, {CM = 900}, {C = 100}, {D = 500}, {M = 1000}} do
                local p, q = next(v)
                local r
                k, r = k:gsub(p, "")
                n = n + r * q
            end
            return n
        end
    end
})

Simplesmente um __índice substituto para a tabela global. A conversão real usando gsub ficou muito mais bonita do que eu imaginava.


5

Postscript

Tentei seguir o C, mas não entendi. Então eu fiz assim:

/strcat{
    2 copy length exch length add string % 1 2 3 
    3 2 roll % 2 3 1 
    2 copy 0 exch putinterval % 2 3 1 
    2 copy length % 2 3 1 3 n(1)
    5 4 roll putinterval % 3 1 
    pop 
}def
/S{
    dup length string cvs 
} def 
[
/u {/ /I /II /III /IV /V /VI /VII /VIII /IX}
/t {/ /X /XX /XXX /XL /L /LX /LXX /LXXX /XC}
/h {/ /C /CC /CCC /CD /D /DC /DCC /DCCC /CM}
/m {/ /M /MM /MMM /MMMM}
>>begin
[0
[
[m]{ % M*
    S   
    [h]{ % M* H*
        S
        [t]{ % M* H* T*
            S
            [u]{ % M* H* T* U*
                S
                4 copy
                strcat strcat strcat % M* H* T* U* M*H*T*U*
                5 1 roll % M*H*T*U* M* H* T* U*
                pop % (M*H*T*U*)* M* H* T*
            }forall
            pop % (M*H*T*U*)** M* H*
        }forall
        pop % (M*H*T*U*)*** M*
    }forall
    pop % (M*H*T*U*)****
}forall
]
{exch dup 1 add}forall pop>>end begin

O Postscript não possui, enummas podemos construir um dicionário com valores inteiros seqüenciais e dobrá-los em uma matriz. Isso reduz o problema de gerar todas as seqüências em sequência, o que é feito concatenando em 4 loops aninhados. Por isso, gera todas as strings e intercala cada string com um valor de contador crescente, resultando em uma longa série de pares <string> <int> na pilha que são agrupados em <<... >>para produzir um objeto de dicionário.

O programa constrói e instala um dicionário mapeando todos os nomes dos algarismos romanos para o seu valor correspondente. Portanto, mencionar os nomes no texto de origem chama a pesquisa automática de nomes e gera o valor inteiro na pilha.

II IV MC pstack

impressões

2
4
600

4

Smalltalk (Smalltalk / X) (87/101 caracteres)

é claro que poderíamos modificar facilmente o tokenizer do analisador (como faz parte da biblioteca de classes e, como tal, aberto a modificações e sempre presente), mas um desafio é afetar apenas as avaliações em um determinado contexto, para que o restante do sistema funciona como de costume.

Versão 1:

defina um número de variáveis ​​no espaço para nome da avaliação. Portanto, isso afetará os DoIts interativos (também conhecidos como evals):

(1 to:3999) do:[:n | 
    Workspace workspaceVariables at:(n romanPrintString asUppercase) put:n]

então eu posso fazer (em um doIt, mas não no código compilado):

   |a b|
   a := MMXIV.
   b := V.
   a + b

-> 2019

Nota: os 101 caracteres incluem espaço em branco; na verdade, isso pode ser feito com 87 caracteres.
Observe também que, quando definido no namespace global do Smalltalk, eu veria essas constantes também no código compilado.

Versão 2:

Use um gancho methodWrapper, que permite que qualquer código existente seja quebrado sem recompilar. A seguir, envolve o tokenizador do analisador para procurar um identificador romano a ser verificado e o torna um número inteiro. A parte complicada é detectar dinamicamente se o contexto de chamada é do império romano ou não. Isso é feito usando um sinal de consulta (que é tecnicamente uma exceção possível):

defina a consulta:

InRomanScope := QuerySignal new defaultAnswer:false.

Portanto, podemos solicitar a qualquer momento ("consulta InRomanScope") que fique falso por padrão.

Em seguida, envolva o método checkIdentifier do scanner:

MessageTracer 
    wrapMethod:(Scanner compiledMethodAt:#checkForKeyword:)
    onEntry:nil
    onExit:[:ctx :ret |
        InRomanScope query ifTrue:[
            |scanner string token n|

            scanner := ctx receiver.
            string := ctx argAt:1.
            (n := Integer readFromRomanString:string onError:nil) notNil
            ifTrue:[
                scanner "/ hack; there is no setter for those two
                    instVarNamed:'tokenType' put:#Integer;
                    instVarNamed:'tokenValue' put:n.
                true
            ] ifFalse:[
                ret
            ].
        ] ifFalse:[
            ret
        ]
    ].

Agora, o scanner funciona como de costume, a menos que estejamos no império romano:

InRomanScope answer:true do:[
    (Parser evaluate:'MMDXXV') printCR.
].

-> 2525

podemos até compilar código:

Compiler 
    compile:'
        inTheYear2525
            ^ MMDXXV
    '
    forClass:Integer.

boa tentativa; mas isso falha com um erro de sintaxe (que é exatamente o que queremos). No entanto, no império romano, podemos compilar:

InRomanScope answer:true do:[

    Compiler 
        compile:'
            inTheYear2525
                ^ MMDXXV
        '
        forClass:Integer.
]

e agora, podemos perguntar a qualquer número inteiro (enviando essa mensagem) de dentro e de fora de Roma:

(1000 factorial) inTheYear2525

-> 2525


É bom ver Smalltalk!

4

Haskell, usando metaprogramação no Template Haskell e algarismos romanos :

{-# LANGUAGE TemplateHaskell #-}
import Data.Char (toLower)
import Language.Haskell.TH
import Text.Numeral.Roman

$( mapM (\n -> funD (mkName . map toLower . toRoman $ n)
                    [ clause [] (normalB [| n |]) []]) $ [1..1000 :: Int] )

main = print xlii

Haskell reserva identificadores começando com letras maiúsculas para construtores, então usei letras minúsculas.


4

J - 78 car

Isso varia apenas para MMMCMXCIX = 3999, como nas outras soluções.

(}.,;L:1{M`CDM`XLC`IVX('';&;:(_1,~3#.inv]){' ',[)&.>841,3#79bc5yuukh)=:}.i.4e3

Quebrando-o (o recall J é geralmente lido da direita para a esquerda, a menos que seja substituído por parênteses):

  • M`CDM`XLC`IVX- quatro caixas de cartas. Nós vamos usar matrizes numéricas no índice nessas letras e criar subpalavras de algarismos romanos.
  • 841,3#79bc5yuukh - Estes são os dados numéricos, bem codificados. *
  • (_1,~3#.inv]) - Isso decodificará os dados acima, expandindo no ternário e acrescentando -1.
  • ('';&;:(...){' ',[)&.>- Emparelhar os números à esquerda com as caixas à direita ( &.>), decodificar as matrizes de números e usá-las para indexar as letras. Tratamos 0 como espaço acrescentando um caractere de espaço às listas de letras. Este procedimento cria listas de palavras como I II III IV V VI VII VIII IXe M MM MMM.
  • {- Pegue o produto cartesiano dessas quatro caixas cheias de palavras. Agora temos uma matriz 4D de todos os algarismos romanos.
  • }.,;L:1- Execute tudo isso em uma única lista 1D de algarismos romanos e remova a string vazia na frente, pois isso criaria um erro. ( L:é uma visão rara no golfe J! Geralmente, não existem muitos níveis de boxe envolvidos.)
  • }.i.4e3- Os números inteiros de 0 a 4000, excluindo os pontos finais.
  • Finalmente, reunimos tudo com uma atribuição global =:. J permite que você tenha uma lista de nomes em caixa no LHS, como uma forma de atribuição múltipla calculada, portanto, isso funciona bem.

Agora, o namespace J está cheio de variáveis ​​que representam números romanos.

   XCIX
99
   MMCDLXXVIII
2478
   V * LXIII   NB. 5*63
315
   #4!:1]0     NB. How many variables are now defined in the J namespace?
3999

* Preciso que o número 2933774030998 seja lido mais tarde na base 3. Acontece que posso expressá-lo na base 79 usando dígitos não maiores que 30, o que é bom porque J só consegue entender dígitos de até 35 (0-9 e depois az). Isso economiza 3 caracteres acima do decimal.


3

Python

A idéia é simples como as outras respostas. Mas, para ser limpo e não poluir o espaço para nome global, é usado um gerenciador de contexto. Isso também impõe a restrição, que você precisa declarar com antecedência, a extensão do numérico romano que planeja usar.

Nota Apenas para simplificar, e não para reinventar a roda, eu usei o pacote romano python

Implementação

class Roman(object):
    memo = [0]
    def __init__(self, hi):
        import rome
        if hi <= len(RomanNumericals.memo):
            self.romans = Roman.memo[0:hi]
        else:
            Roman.memo += [str(rome.Roman(i))
                    for i in range(len(Roman.memo),
                            hi+1)]
            self.romans = Roman.memo
    def __enter__(self):
        from copy import copy
        self.saved_globals = copy(globals())
        globals().update((v,k) for k,v in enumerate(self.romans[1:], 1))
    def __exit__(self,*args ):
        globals().clear()
        globals().update(self.saved_globals)

Demo

with Roman(5):
    with Roman(10):
        print X
    print V
    print X


10
5

Traceback (most recent call last):
  File "<pyshell#311>", line 5, in <module>
    print X
NameError: name 'X' is not defined

3

Python

Esta é possivelmente a solução mais simples usando o Python:

ns = [1000,900,500,400,100,90,50,40,10,9,5,4,1]
ls = 'M CM D CD C XC L XL X IX V IV I'.split()
for N in range(1, 4000):
    r=''
    p=N
    for n,l in zip(ns,ls):
        while p>=n:
            r+=l
            p-=n
    exec('%s=%d'%(r,N))


i, j = XIX, MCXIV
print i, j

3
Melhor usar do globals()[var] = valueque exec().
Ramchandra Apte

3

D

usando a avaliação da função de tempo de compilação de D

import std.algorithm;
string numerals(){
    auto s = cartesianProduct(["","I","II","III","IV","V","VI","VII","VIII","IX"], 
                              ["","C","CC","CCC","CD","D","DC","DCC","DCCC","CM"],
                              ["","X","XX","XXX","XL","L","LX","LXX","LXXX","XC"],
                              ["","M","MM","MMM"]);
    s.popFront();//skip first
    char[] result="enum ";
    int i=1;
    foreach(p;s){
        result~=p[3]~p[2]~p[1]~p[0]~"="~i++~",";
    }
    result[$-1]=';';//replace last , with a ;
    return result.idup;
}
mixin(numerals());

3

APL (Dyalog APL) , 77 bytes

Solicita o comprimento máximo do numeral romano e define todas as variáveis.

t'IVXLCDM',⊂⍬
{⍎⍕⍵'←',+/2(⊣ׯ1*<)/0,⍨(∊1 5∘ר10*⍳4)[t⍳⍵]}¨,/t[⍉8⊥⍣¯1⍳¯1+8*⎕]

t←t recebe

'IVXLCDM', Caracteres romanos seguidos por

 um fechado

 lista vazia

t[] Índice t com…

 a transposta (para obter a ordem correta)

8⊥⍣¯1 largura adequada base-oito representação de

 os primeiros n índices, onde n é

¯1+ um a menos que

8*⎕ oito para o poder da entrada numérica

,/ achatar linhas (cada representação)

{...  Aplique a seguinte função anônima em cada representação…

()[t⍳⍵] Correspondendo às posições dos itens do argumento em t , selecione…

   o alistado

  1 5∘ר uma e cinco vezes cada um

  10* dez para o poder de

  ⍳4 zero a três

0,⍨ acrescentar zero

2(…)/ em cada janela deslizante de comprimento e dois, aplique o seguinte trem de funções anônimas…

  ⊣× os tempos do argumento esquerdo

  ¯1* negativo para o poder de

  < se o argumento da esquerda é menor que o argumento da direita

+/ soma

⍵'←', preceda o argumento (o número romano) e uma seta de atribuição

 formato (para nivelar e converter o número em texto)

 execute isso (faz a atribuição fora da função anônima)

Experimente online! (usando o comprimento máximo 5)


2

PHP

Existem várias regras para números romanos válidos

  1. Escreva o maior valor antes dos valores mais baixos

  2. Subtrair apenas [I,X,C] antes dos próximos 2 valores maiores

  3. Subtraia o dobro [I,X,C]antes dos próximos 2 valores maiores

  4. Subtrair o dobro [I,X,C]antes dos valores maiores

  5. Combinar 4 + 5

Versão Online

Etapa 1 Crie as regras

{"M":1000,"IM":999,"IIM":998,"XM":990,"XXM":980,"CM":900,"CCM":800,"D":500,"ID":499,"IID":498,"XD":490,"XXD":480,"CD":400,"C":100,"IC":99,"IIC":98,"XC":90,"XXC":80,"L":50,"IL":49,"IIL":48,"XL":40,"X":10,"IX":9,"IIX":8,"V":5,"IV":4,"I":1}

é a saída JSON para todos os números romanos válidos

$rule_sub_all=$rule_add=$rule_sub_simple=["M"=>1000,"D"=>500,"C"=>100,"L"=>50,"X"=>10,"V"=>5,"I"=>1];
foreach(["I","X","C"]as$roman_letter){
    $rule_sub_simple[$roman_letter.array_search($value=$rule_add[$roman_letter]*5,$rule_add)]=$value-$rule_add[$roman_letter];
    $rule_sub_simple[$roman_letter.array_search($value=$rule_add[$roman_letter]*10,$rule_add)]=$value-$rule_add[$roman_letter];
}
$rule_sub_lower_one=$rule_sub_double=$rule_sub_simple;
foreach(["I","X","C"]as$roman_letter){
    $rule_sub_double[$roman_letter.$roman_letter.array_search($value=$rule_add[$roman_letter]*10,$rule_add)]=$value-2*$rule_add[$roman_letter];

foreach($rule_add as$key=>$value){
    if($value>$rule_add[$roman_letter])$rule_sub_all[$roman_letter.$key]=$value-$rule_add[$roman_letter];
    if($value>5*$rule_add[$roman_letter])$rule_sub_all[$roman_letter.$roman_letter.$key]=$value-2*$rule_add[$roman_letter];
    if($value>10*$rule_add[$roman_letter])$rule_sub_lower_one[$roman_letter.$key]=$value-$rule_add[$roman_letter];
    }
}
arsort($rule_sub_all);
arsort($rule_sub_lower_one);
arsort($rule_sub_double);
arsort($rule_sub_simple);

Etapa 2 Faça listas para todas as regras até 3999

$array_sub_lower_one=$array_sub_double=$array_sub_all=$array_add=$array_sub_simple=[];
for($i=1;$i<4000;$i++){
    $number_sub_all=$number_add=$number_sub_simple=$number_sub_double=$number_sub_lower_one=$i;
    $roman_letter_sub_all=$roman_letter_add=$roman_letter_sub_simple=$roman_letter_sub_double=$roman_letter_sub_lower_one="";
    foreach($rule_sub_all as$key=>$value){
        $roman_letter_sub_all.=str_repeat($key,$d=$number_sub_all/$value^0);$number_sub_all-=$value*$d;
        if(in_array($value,$rule_sub_lower_one)){
        $roman_letter_sub_lower_one.=str_repeat($key,$d=$number_sub_lower_one/$value^0);$number_sub_lower_one-=$value*$d;}    
        if(in_array($value,$rule_add)){
        $roman_letter_add.=str_repeat($key,$d=$number_add/$value^0);$number_add-=$value*$d;}
        if(in_array($value,$rule_sub_simple)){
        $roman_letter_sub_simple.=str_repeat($key,$d=$number_sub_simple/$value^0);$number_sub_simple-=$value*$d;}
        if(in_array($value,$rule_sub_double)){
        $roman_letter_sub_double.=str_repeat($key,$d=$number_sub_double/$value^0);$number_sub_double-=$value*$d;}
     }
    $array_sub_lower_one[$roman_letter_sub_lower_one]=$i;   
    $array_sub_all[$roman_letter_sub_all]=$i;
    $array_add[$roman_letter_add]=$i;
    $array_sub_simple[$roman_letter_sub_simple]=$i;
    $array_sub_double[$roman_letter_sub_double]=$i;
}

Etapa 3 Criar constantes

Combine todas as listas e defina constantes

foreach(array_merge($array_add,$array_sub_simple,$array_sub_lower_one,$array_sub_double,$array_sub_all)as$key=>$value){ define($key,$value); }

Saída

No exemplo, mutiply duas versões válidas do número 8

echo IIX *  VIII;

Legal, mas como eu uso isso? Eu não sou fluente em PHP; Você poderia dar um exemplo de como isso me permite escrever números romanos no código?
Daniero 17/03/19

@Daniero Agora o código deve funcionar
Jörg Hülsermann

Ah, é melhor :)
315 daniero

1

Rebol

Rebol []

roman-dialect: func [
    {Dialect which allows Roman numerals as numbers (integer!)}
    block [block!]
    /local word w m1 m2 when-in-rome rule word-rule block-rule
  ][
    when-in-rome: does [
        if roman? w: to-string word [change/part m1 roman-to-integer w m2]
    ]

    word-rule:  [m1: copy word word! m2: (when-in-rome)]
    block-rule: [m1: any-block! (parse m1/1 rule)]
    rule:       [any [block-rule | word-rule | skip]]

    parse block rule
    do block
]

; couple of helper functions used in above parse rules

roman-to-integer: func [roman /local r eval] [
    r: [IV 4 IX 9 XL 40 XC 90 CD 400 CM 900 I 1 V 5 X 10 L 50 C 100 D 500 M 1000]
    eval: join "0" copy roman
    foreach [k v] r [replace/all eval k join " + " v]
    do replace/all to-block eval '+ '+
]

roman?: func [s /local roman-chars] [
    roman-char: charset "IVXLCDM"
    parse/case s [some roman-char]
]


Exemplo

roman-dialect [
    m: X + V
    print m
    print M + m  ; doesn't confuse the variable m with Roman Numeral M
    if now/year = MMXIV [print "Yes it is 2014"]
    for n I V I [
        print [rejoin [n "+" X "="] n + X]
    ]
]

Saída:

15

1015

Sim é 2014

1 + 10 = 11

2 + 10 = 12

3 + 10 = 13

4 + 10 = 14

5 + 10 = 15


Isenção de responsabilidade: Tenho certeza de que existem outras (e provavelmente melhores!) Maneiras de fazer isso no Rebol também.

PS. Minha roman-to-integerfunção é uma transliteração do bom algoritmo Ruby do histocrata para converter a string do numeral romano em um número. Devolvido com agradecimentos! +1


1

Lua

local g={}
setmetatable(_G,g)
local romans = {I = 1,
                V = 5,
                X = 10,
                L = 50,
                C = 100,
                D = 500,
                M = 1000}
local larger = {    -- Precalced, for faster computing.
                I = "VXLCDM",
                V = "XLCDM",
                X = "LCDM",
                L = "CDM",
                C = "DM",
                D = "M",
                M = " " -- A space will never be found, and it prevents the pattern matcher from erroring.
}
local storedRomans = {}
function g:__index(key)
    if storedRomans[key] then
        return storedRomans[key]
    end
    if key:match"^[IVXLCDM]+$" then
        local n = 0
        local i = 1
        for s in key:gmatch"." do
            local N = romans[s]
            if key:find('['..larger[s]..']',i) then
                n = n - N
            else
                n = n + N
            end
            i = i + 1
        end
        storedRomans[key] = n
        return n
    end
end

Isso afeta a meta-tabela da tabela global, fornecendo uma nova função de índice. Quando uma variável global que contém apenas números romanos é solicitada, por exemplo,XVII , ela a analisa.

Fácil de testar;

print(XVII) -- 42
print(VI)   -- 6
print(III)  -- 3
print(MMM)  -- 3000

Experimente online!


1

VBA, 204 bytes

Uma sub-rotina declarada que não recebe nenhuma entrada e, quando executada, cria o publicly acessível Enum,R que contém todos os valores numerais romanos. Esses valores podem ser usados ​​diretamente, sem fazer referência ao Enum.

Enum mantém valores de 1 a 3999.

Nota: Os terminais "s nas linhas 3 e 7 são incluídos apenas para destaque de sintaxe e não contribuem para o número de bytes

Public Sub i
Set c=ThisWorkbook.VBProject.VBComponents.Add(1).CodeModule
c.AddFromString"Public Enum R"
For n=1To 3999
c.AddFromString WorksheetFunction.Roman(n)&"="&n
Next
c.AddFromString"End Enum"
End Sub

Ungolfed e Explained

Public Sub InitializeRomanNumerals()
    Dim c As CodeModule                                            ''  Dim CodeModule
    Set c=ThisWorkbook.VBProject.VBComponents.Add(1).CodeModule    ''  Create Module
    Let c.Parent.Name = "RomanNumeral_Module"                      ''  Name the Module
    Call c.AddFromString("Public Enum RomanNumerals")              ''  Declare the Enum
        For n=1To 3999Step 1                                       ''  Iter from 1 to 3999
            Call c.AddFromString(WorksheetFunction.Roman(n)&"="&n) ''  Add the Roman
                                                                   ''  Numeral to Enum
        Next n                                                     ''  Loop
    Call c.AddFromString("End Enum")                               ''  End The Enum
End Sub
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.