Romanización del Código


33

El desafío es hacer que cualquier código romano sea válido en el idioma elegido.

Deben no aparecer dentro de cadenas o cualquier cosa similar, pero funcionan igual que cualquier otro fichas, literales tales como ( árabe ) números, caracteres o cadenas; o identificadores de variable / método / función, etc.

Por ejemplo, en Java, lo siguiente debería compilarse y ejecutarse como si ise hubiera inicializado a 42:

int i = XLII;

El análisis real de los números es secundario, por lo que puede usar una biblioteca si lo desea, pero este es un concurso de popularidad, por lo que se fomenta la creatividad.

No puede usar ningún idioma que realmente use números romanos, si existe tal cosa.

Buena suerte.


1
Entonces, ¿necesitamos escribir una extensión del lenguaje, creando así una nueva?
Kendall Frey

44
Me quejaré si quiero, porque los idiomas que uso no son extensibles así, así que ni siquiera puedo participar.
Kendall Frey

3
@KendallFrey La fuente tendría que compilar y ejecutar. Para Java, podría escribir un "compilador" que edite la fuente, luego compila mediante programación . Una forma de tal compilación es mediante la ejecución deProcess
Justin

1
Parece aburrido en la mayoría de los idiomas. Por ejemplo, en Python, simplemente escribiría un script que se usa astpara analizar la fuente. Inserte en la parte superior del AST la definición de los números romanos del 1 al 3999. Compile todo y ejecútelo. Es aburrido escribir el código para manejar el proceso.
Bakuriu

2
@Bakuriu y tu comentario también es aburrido. Este es un concurso de popularidad, por lo que debes intentar crear algo divertido. Creo que hay algunas buenas respuestas aquí que son más imaginativas (que compilar un lenguaje de script).
daniero

Respuestas:


40

do

Hay solo unos pocos números romanos, ya que 4000 y superiores no tienen notación estándar, y el preprocesador es una herramienta de descompresión maravillosa, especialmente si no tiene problemas con el hecho de que el código tiene un comportamiento 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))
};

Esto define todos los números romanos desde Ihasta MMMCMXCIXcomo constantes de enumeración, más _(que puede ser reemplazado por cualquier cosa que desee) como cero.


12
¡Absolutamente brillante, proponga agregarlo a la próxima versión de C (C2X?) Como <roman.h>! ¡Con los formatos% r y% R printf!

3
Pensé que sé cómo usar el preprocesador, hasta ahora: | ¿Podría actualizar su respuesta con un ejemplo de uso mínimo de esta enumeración?
klingt.net

@YiminRong Y scanftambién :) @ klingt.net No estoy seguro de qué tipo de ejemplo estás buscando. Una bastante simple seríaint main() { return MMMCMXCIX - M - M - M - CM - XC - IX; }
hvd

Me gusta la idea, pero ¿por qué optaste por un comportamiento indefinido cuando el comportamiento definido era bastante simple? Sin juzgar, solo curioso?
CasaDeRobison

1
@CasaDeRobison Por diversión, principalmente. Me gusta porque es uno de los pocos casos de comportamiento indefinido en el tiempo de preprocesamiento que no está marcado como un error por los compiladores actuales, y probablemente no lo será en el futuro. Nunca escribo algo así en el código que está destinado a ser útil, pero el código publicado aquí no está destinado a ser útil, entonces, ¿qué mejor ocasión para probarlo?
hvd

15

Rubí

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 los números romanos (mayúsculas) ahora se analizarán como sus equivalentes decimales. El único problema es que todavía son asignables: puedes hacerlo X = 9, pero no 10 = 9. No creo que haya una manera de arreglar eso.


1
Esto es perfecto. Lo de la tarea no es un problema; Puedes usar tareas para hacer todo tipo de estupideces.
daniero

12

JavaScript (ES6)

Úselo Proxypara atrapar números romanos.

Probable en Firefox (más reciente) en JSFiddle .
No es comprobable en Chrome (con Traceur) ya que la Proxyimplementación no funciona.

// 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 y C ++ (respuesta actualizada)

Como se observó en un comentario, mi solución original tenía dos problemas:

  1. Los parámetros opcionales solo están disponibles en C99 y estándares posteriores de la familia de idiomas.
  2. La coma final en la definición de enumeración también es específica de C99 y posterior.

Como quería que mi código fuera lo más genérico posible para trabajar en plataformas más antiguas, decidí darle otra puñalada. Es más largo que antes, pero funciona en compiladores y preprocesadores configurados en modo de compatibilidad C89 / C90. Todas las macros pasan un número apropiado de argumentos en el código fuente, aunque a veces esas macros se "expanden" en nada.

Visual C ++ 2013 (también conocido como versión 12) emite advertencias sobre parámetros faltantes, pero ni mcpp (un preprocesador de código abierto que afirma un alto cumplimiento del estándar) ni gcc 4.8.1 (con -std = iso9899: 1990 -conmutadores de errores pedagógicos) emiten advertencias o errores para esas invocaciones macro con una lista de argumentos efectivamente vacía.

Después de revisar el estándar relevante (ANSI / ISO 9899-1990, 6.8.3, Reemplazo de macros), creo que hay suficiente ambigüedad para que esto no se considere no estándar. "El número de argumentos en una invocación de una macro similar a una función debe coincidir con el número de parámetros en la definición de macro ...". No parece excluir una lista de argumentos vacía siempre que los paréntesis necesarios (y las comas en el caso de múltiples parámetros) estén en su lugar para invocar la macro

En cuanto al problema de coma final, eso se resuelve agregando un identificador adicional a la enumeración (en mi caso, MMMM que parece tan razonable como cualquier cosa para que el identificador siga 3999 incluso si no obedece las reglas aceptadas de secuenciación de números romanos exactamente).

Una solución ligeramente más limpia implicaría mover la enumeración y admitir macros a un archivo de encabezado separado, como se implica en un comentario en otro lugar, y usar undef de los nombres de macro inmediatamente después de que se usaron para evitar contaminar el espacio de nombres. Indudablemente, también deberían elegirse mejores nombres de macro, pero esto es adecuado para la tarea en cuestión.

Mi solución actualizada, seguida de mi solución 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;
}

La respuesta original (que recibió los primeros seis votos a favor, por lo que si nadie vuelve a votar esto, no debería pensar que mi solución actualizada obtuvo los votos a favor):

En el mismo espíritu que una respuesta anterior, pero de una manera que debería ser portátil utilizando solo un comportamiento definido (aunque los diferentes entornos no siempre coinciden en algunos aspectos del preprocesador). Trata algunos parámetros como opcionales, ignora otros, debería funcionar en preprocesadores que no admitan la __VA_ARGS__macro, incluido C ++, utiliza macros indirectos para garantizar que los parámetros se expandan antes de pegar el token, y finalmente es más corto y creo que es más fácil de leer ( aunque todavía es complicado y probablemente no sea fácil de leer, solo que más 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, pero tenga en cuenta que el uso de argumentos de macro vacíos y la coma al final de una lista de enumeradores son características nuevas de C99 y C ++ 11, y C99 y C ++ 11 son compatibles __VA_ARGS__.
hvd

¡Maldita sea si no tienes razón! Supongo que he visto esto usado como extensiones todo este tiempo. Ah bueno. :)
CasaDeRobison

8

Lisp común

La siguiente es una explicación bastante extensa de cómo hice una macro que puedes usar así:

(roman-progn
  (+ XIV XXVIII))

Cuando se llama a una macro en Common Lisp, básicamente actúa como una función, solo que los argumentos se reciben antes de ser evaluados. En realidad, dado que en Common Lisp el código es solo información, lo que recibimos es una lista (anidada) que representa un árbol de sintaxis sin analizar con el que podemos hacer lo que queramos, y se realiza en tiempo de compilación.

Funciones de ayuda

El primer paso del plan es tomar este árbol y escanearlo en busca de algo que se parezca a los números romanos. Siendo Lisp y todo, intentemos hacerlo de manera funcional: necesitamos una función que haga un recorrido profundo de un árbol y devuelva cada objeto para el que una función proporcionada searchpdevuelve verdadero. Este es incluso (semi) recursivo de cola.

(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))))

Luego, un código para analizar los números romanos, cortesía de Rosetta Code :

(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)))

La macro real

Tomamos el árbol de sintaxis ( body), lo buscamos con nuestro procedimiento de búsqueda profunda y de alguna manera hacemos que los números romanos que encontramos estén disponibles.

(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))))

Entonces que es 1 + 2 + 3 + (4 * (5 + 6)) + 7?

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

Y para ver qué sucedió realmente cuando se invocó la macro:

(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
})

Simplemente un __index alternativo para la tabla global. La conversión real usando gsub resultó mucho más bonita de lo que imaginé.


5

Posdata

Traté de seguir el C pero no lo entendí. Entonces lo hice de esta manera:

/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

PostScript no tiene, enumpero podemos construir un diccionario con valores enteros secuenciales y plegarlos en una matriz. Esto reduce el problema a generar todas las cadenas en secuencia, lo que se hace concatenando en 4 bucles anidados. Por lo tanto, genera todas las cadenas, luego intercala cada cadena con un valor de contador creciente, lo que resulta en una larga serie de pares <string> <int> en la pila que se envuelven en <<... >>para producir un objeto de diccionario.

El programa construye e instala un diccionario que asigna todos los nombres de los números romanos a su valor correspondiente. Entonces, mencionar los nombres en el texto de origen invoca la búsqueda automática de nombres y produce el valor entero en la pila.

II IV MC pstack

huellas dactilares

2
4
600

4

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

por supuesto, podríamos modificar fácilmente el tokenizer del analizador (ya que es parte de la biblioteca de clases, y como tal está abierto a modificaciones, y siempre está presente), pero un desafío es afectar solo las evaluaciones en un contexto dado, de modo que el resto del El sistema funciona como siempre.

Versión 1:

definir una serie de variables en el espacio de nombres de evaluación. Esto afectará a los doIts interactivos (también conocidos como evals):

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

entonces puedo hacer (en un doIt, pero no en el código compilado):

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

-> 2019

Aviso: los 101 caracteres incluyen espacios en blanco; en realidad se puede hacer con 87 caracteres.
También tenga en cuenta que, cuando se define en el espacio de nombres Smalltalk global, vería esas constantes también en el código compilado.

Versión 2:

Use un gancho methodWrapper, que permite envolver cualquier código existente sin volver a compilar. Lo siguiente envuelve el tokenizador del analizador para buscar un identificador romano para escanear y lo convierte en un número entero. La parte difícil es detectar dinámicamente si el contexto de llamada es del imperio romano o no. Esto se hace usando una señal de consulta (que técnicamente es una excepción procesable):

define la consulta:

InRomanScope := QuerySignal new defaultAnswer:false.

Por lo tanto, podemos solicitar en cualquier momento ("consulta InRomanScope") que sea falso por defecto.

Luego, envuelva el método checkIdentifier del escáner:

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
        ]
    ].

Ahora el escáner funciona como de costumbre, a menos que estemos en el imperio romano:

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

-> 2525

incluso podemos compilar código:

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

buen intento; pero esto falla con un error de sintaxis (que es exactamente lo que queremos). Sin embargo, en el imperio romano, podemos compilar:

InRomanScope answer:true do:[

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

y ahora, podemos preguntarle a cualquier número entero (enviando ese mensaje) desde dentro y fuera de Roma:

(1000 factorial) inTheYear2525

-> 2525


Es bueno ver Smalltalk!

4

Haskell, utilizando metaprogramación en Template Haskell y números 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 que comienzan con letras mayúsculas para los constructores, por lo que utilicé minúsculas.


4

J - 78 char

Esto solo sube a MMMCMXCIX = 3999, como con las otras soluciones.

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

Desglosándolo (recordar J generalmente se lee de derecha a izquierda, a menos que sea reemplazado por paréntesis):

  • M`CDM`XLC`IVX- Cuatro cajas de letras. Vamos a utilizar matrices numéricas en el índice de estas letras y construir subpalabras de números romanos.
  • 841,3#79bc5yuukh - Estos son los datos numéricos, fuertemente codificados. *
  • (_1,~3#.inv]) - Esto decodificará los datos anteriores, expandiéndolos en ternario y agregando -1.
  • ('';&;:(...){' ',[)&.>- Emparejando los números de la izquierda con los cuadros de la derecha ( &.>), decodifica los conjuntos de números y úsalos para indexar las letras. Tratamos 0 como espacio anteponiendo un carácter de espacio a las listas de letras. Este procedimiento crea listas de palabras como I II III IV V VI VII VIII IXy M MM MMM.
  • {- Tome el producto cartesiano de estas cuatro cajas llenas de palabras. Ahora tenemos una matriz 4D de todos los números romanos.
  • }.,;L:1- Ejecute todo eso en una sola lista 1D de números romanos y elimine la cadena vacía en la parte delantera porque crearía un error. (¡ L:Es una vista rara en J golf! Por lo general, no hay tantos niveles de boxeo involucrados).
  • }.i.4e3- Los enteros de 0 a 4000, excluyendo los puntos finales.
  • Finalmente, ponemos todo junto con una tarea global =:. J le permite tener una lista encuadrada de nombres en el LHS, como una forma de asignación múltiple calculada, por lo que esto funciona bien.

Ahora el espacio de nombres J está lleno de variables que representan 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

* Necesito que el número 2933774030998 se lea más tarde en la base 3. Sucede que puedo expresarlo en la base 79 usando dígitos no mayores que 30, lo cual es bueno porque J solo puede entender dígitos hasta 35 (0-9 y luego Arizona). Esto ahorra 3 caracteres sobre el decimal.


3

Pitón

La idea es simple como las otras respuestas. Pero solo para ser ordenado y no contaminar el espacio de nombres global, se utiliza un administrador de contexto. Esto también impone la restricción, que necesita declarar de antemano, la extensión del número romano que planea usar.

Nota Solo para que sea simple y no para reinventar la rueda, he utilizado el paquete romano de pitón

Implementación

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)

Manifestación

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

Pitón

Esta es posiblemente la solución más simple usando 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
Mejor usar globals()[var] = valueque exec().
Ramchandra Apte

3

re

utilizando la evaluación de la función de tiempo de compilación 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 la longitud máxima del número romano y define todas las variables.

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

t←t consigue

'IVXLCDM', Caracteres romanos seguidos de

 un cerrado

 lista vacía

t[... ] índice t con ...

 la transpuesta (para obtener el orden correcto)

8⊥⍣¯1 ancho apropiado base-ocho representación de

 los primeros n índices, donde n es

¯1+ uno menos que

8*⎕ ocho a la potencia de entrada numérica

,/ aplanar filas (cada representación)

{...  aplique la siguiente función anónima en cada representación ...

(... )[t⍳⍵] correspondiente a las posiciones de los elementos del argumento en t , seleccione entre ...

   el alistado

  1 5∘ר una y cinco veces cada uno de

  10* diez al poder de

  ⍳4 cero a tres

0,⍨ agregar cero

2(…)/ en cada ventana deslizante de longitud dos, aplique el siguiente tren de funciones anónimo ...

  ⊣× los tiempos de discusión de la izquierda

  ¯1* uno negativo al poder de

  < si el argumento izquierdo es menor que el argumento derecho

+/ suma

⍵'←', anteponer el argumento (el número romano) y una flecha de asignación

 formato (para aplanar y convertir el número a texto)

 ejecutar eso (realiza la asignación fuera de la función anónima)

Pruébalo en línea! (usando max-length 5)


2

PHP

Hay varias reglas para números romanos válidos

  1. Escriba el mayor valor antes de los valores más bajos.

  2. Restar solo [I,X,C]antes de los siguientes 2 valores mayores

  3. Reste doble [I,X,C]antes de los siguientes 2 valores mayores

  4. Restar doble [I,X,C]antes de los valores mayores

  5. Combina 4 + 5

Versión en línea

Paso 1 Crea las reglas

{"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}

es la salida JSON para todos los 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);

Paso 2 Haga listas para todas las reglas hasta 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;
}

Paso 3 Crear constantes

Combina todas las listas y define 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); }

Salida

En el ejemplo, dos versiones válidas del número 8

echo IIX *  VIII;

Bien, pero ¿cómo uso esto? No soy fluido en PHP; ¿Podría dar un ejemplo de cómo esto me permite escribir números romanos en el código?
daniero

@Daniero Ahora el código debería funcionar
Jörg Hülsermann

Ah, eso es mejor :)
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]
]


Ejemplo

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]
    ]
]

Salida:

15

1015

Si es 2014

1 + 10 = 11

2 + 10 = 12

3 + 10 = 13

4 + 10 = 14

5 + 10 = 15


Descargo de responsabilidad: estoy seguro de que hay otras formas (¡y probablemente mejores!) De hacerlo también en Rebol.

PD. Mi roman-to-integerfunción es una transcripción del bonito algoritmo Ruby de histocrat para convertir la cadena de números romanos en un número. Volvió con gracias! +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

Esto afecta la metatabla de la tabla global, dándole una nueva función de índice. Cuando se solicita una variable global que solo contiene números romanos, por ejemplo XVII, la analiza.

Fácil de probar

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

Pruébalo en línea!


1

VBA, 204 bytes

Una subrutina declarada que no toma ninguna entrada y, cuando se ejecuta, crea la publicly accesible Enum,R que contiene todos los valores de número romano. Estos valores se pueden usar directamente, sin hacer referencia a Enum.

Enum hold valores del 1 al 3999.

Nota: Los terminales "s en las líneas 3 y 7 se incluyen solo para resaltar la sintaxis y no contribuyen al recuento 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

No golfista y explicado

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
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.