Codice romanizzazione


33

La sfida è rendere validi i numeri romani nella lingua scelta.

Dovrebbero non apparire all'interno di stringhe o qualcosa di simile, ma il lavoro proprio come qualsiasi altro token, letterali , come ( arabo ) numeri, caratteri o stringhe; o identificatori di variabili / metodi / funzioni, ecc.

Ad esempio, in Java, quanto segue dovrebbe essere compilato ed eseguito come se ifosse stato inizializzato su 42:

int i = XLII;

L'analisi effettiva dei numeri è secondaria, quindi puoi usare una libreria se vuoi, ma questa è una gara di popolarità, quindi la creatività è incoraggiata.

Non è possibile utilizzare alcuna lingua che utilizza effettivamente numeri romani, se esiste una cosa del genere.

In bocca al lupo.


1
Quindi, dobbiamo scrivere un'estensione per la lingua, creando così una nuova?
Kendall Frey,

4
Mi lamenterò se voglio, perché le lingue che uso non sono estensibili in questo modo, quindi non posso nemmeno partecipare.
Kendall Frey,

3
@KendallFrey Il sorgente dovrebbe compilare ed eseguire. Per Java, si potrebbe scrivere un "compilatore" che pubblica la fonte, quindi compila con programmazione . Un modo per compilare questo è attraverso l'esecuzione di unProcess
Justin

1
Sembra noioso nella maggior parte delle lingue. Ad esempio in Python scriverei semplicemente uno script che usa astper analizzare la fonte. Inserire nella parte superiore dell'AST la definizione dei numeri romani da 1 a 3999. Compilare il tutto ed eseguirlo. È solo noioso scrivere il codice per gestire il processo.
Bakuriu,

2
@Bakuriu e anche il tuo commento è noioso. Questo è un concorso di popolarità, quindi dovresti provare a trovare qualcosa di divertente. Penso che ci siano alcune belle risposte qui che sono più fantasiose (rispetto alla compilazione di un linguaggio di scripting).
daniero,

Risposte:


40

C

Ci sono solo così tanti numeri romani, dal 4000 in poi non hanno una notazione standard, e il preprocessore è uno strumento di decompressione meraviglioso, specialmente se non hai problemi con il fatto che il codice ha un comportamento indefinito.

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

Questo definisce tutti i numeri romani da Ia MMMCMXCIXcome costanti di enumerazione, più _(che può essere sostituito da qualsiasi cosa tu voglia) come zero.


12
Assolutamente geniale, proponi di aggiungerlo alla prossima versione C (C2X?) Come <roman.h>! Con i formati printf% r e% R!

3
Pensavo di sapere come usare il preprocessore, fino ad ora: | Potresti aggiornare la tua risposta con un esempio di utilizzo minimo di questa enum?
klingt.net,

@YiminRong E scanfanche :) @ klingt.net Non sono sicuro del tipo di esempio che stai cercando. Sarebbe abbastanza sempliceint main() { return MMMCMXCIX - M - M - M - CM - XC - IX; }
hvd il

Mi piace l'idea, ma perché hai optato per un comportamento indefinito quando il comportamento definito era abbastanza semplice? Non giudicare, solo curioso?
CasaDeRobison,

1
@CasaDeRobison Per divertimento, principalmente. Mi piace perché è uno dei pochissimi casi di comportamento indefinito in fase di preelaborazione che non viene segnalato come errore dagli attuali compilatori e probabilmente non lo sarà in futuro. Non scrivo mai niente del genere nel codice che dovrebbe essere utile, ma il codice pubblicato qui non è pensato per essere utile, quindi quale occasione migliore per provarlo?
hvd,

15

Rubino

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

Qualsiasi numero romano (maiuscolo) verrà ora analizzato come i loro equivalenti decimali. L'unico problema è che sono ancora assegnabili: puoi farlo X = 9, ma non 10 = 9. Non penso che ci sia un modo per risolverlo.


1
Questo è perfetto. La cosa del compito non è un problema; Puoi usare i compiti per fare ogni sorta di cose stupide.
daniero,

12

JavaScript (ES6)

Utilizzare Proxyper catturare numeri romani.

Testabile in Firefox (più recente) su JSFiddle .
Non testabile in Chrome (con Traceur) poiché l' Proxyimplementazione è interrotta.

// 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 ++ (risposta aggiornata)

Come osservato in un commento, la mia soluzione originale aveva due problemi:

  1. I parametri opzionali sono disponibili solo in C99 e negli standard successivi della famiglia linguistica.
  2. La virgola finale nella definizione dell'enum è anche specifica per C99 e successive.

Dato che volevo che il mio codice fosse il più generico possibile per funzionare su piattaforme più vecchie, ho deciso di provarlo ancora. È più lungo di prima, ma funziona su compilatori e preprocessori impostati sulla modalità di compatibilità C89 / C90. A tutte le macro viene passato un numero appropriato di argomenti nel codice sorgente, sebbene a volte quelle macro "si espandano" in nulla.

Visual C ++ 2013 (aka versione 12) emette avvisi sui parametri mancanti, ma né mcpp (un preprocessore open source che rivendica un'elevata conformità allo standard) né gcc 4.8.1 (con -std = iso9899: 1990 -pedantic-errori switch) emettono avvertimenti o errori per quelle invocazioni di macro con un elenco di argomenti effettivamente vuoto.

Dopo aver esaminato lo standard pertinente (ANSI / ISO 9899-1990, 6.8.3, sostituzione macro), ritengo che vi sia un'ambiguità sufficiente che questo non debba essere considerato non standard. "Il numero di argomenti in un'invocazione di una macro simile a una funzione deve corrispondere al numero di parametri nella definizione di macro ...". Non sembra precludere un elenco di argomenti vuoto fintanto che le parentesi necessarie (e le virgole nel caso di più parametri) sono in atto per invocare la macro

Per quanto riguarda il problema della virgola finale, che viene risolto aggiungendo un identificatore extra all'enumerazione (nel mio caso, MMMM che sembra ragionevole come qualsiasi cosa per l'identificatore seguire 3999 anche se non obbedisce alle regole accettate del sequenziamento numerico romano Esattamente).

Una soluzione leggermente più pulita implicherebbe lo spostamento dell'enum e il supporto delle macro in un file di intestazione separato, come era implicito in un commento altrove, e l'utilizzo di undef dei nomi delle macro subito dopo che sono stati utilizzati in modo da evitare l'inquinamento dello spazio dei nomi. Indubbiamente dovrebbero essere scelti anche nomi di macro migliori, ma questo è adeguato per l'attività da svolgere.

La mia soluzione aggiornata, seguita dalla mia soluzione originale:

#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 risposta originale (che ha ricevuto i primi sei voti, quindi se nessuno lo vota mai più, non dovresti pensare che la mia soluzione aggiornata abbia ottenuto i voti):

Nello stesso spirito di una risposta precedente, ma fatto in un modo che dovrebbe essere portatile usando solo comportamenti definiti (anche se ambienti diversi non sempre concordano su alcuni aspetti del preprocessore). Tratta alcuni parametri come facoltativi, ne ignora altri, dovrebbe funzionare su preprocessori che non supportano la __VA_ARGS__macro, incluso C ++, utilizza macro indirette per garantire che i parametri siano espansi prima dell'incollaggio dei token, e infine è più breve e penso più facile da leggere ( anche se è ancora difficile e probabilmente non facile da leggere, solo più facile):

#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, ma si noti che l'uso di argomenti macro vuoti e la virgola alla fine di un elenco di enumeratori, sono entrambe nuove funzionalità di C99 e C ++ 11 e supportano sia C99 che C ++ 11 __VA_ARGS__.
hvd,

Dang se non hai ragione! Immagino di averlo visto usato come estensioni per tutto questo tempo. Ah bene. :)
CasaDeRobison,

8

Lisp comune

Di seguito è una spiegazione piuttosto lunga di come ho creato una macro che è possibile utilizzare in questo modo:

(roman-progn
  (+ XIV XXVIII))

Quando una macro viene chiamata in Common Lisp, sostanzialmente agisce come una funzione, solo che gli argomenti vengono ricevuti prima che vengano valutati. In realtà, poiché nel codice Common Lisp sono solo dati, ciò che riceviamo è un elenco (nidificato) che rappresenta un albero di sintassi non analizzato che possiamo fare con ciò che vogliamo, ed è fatto in fase di compilazione.

Funzioni di supporto

Il primo passo del piano è prendere questo albero e scansionarlo per qualsiasi cosa che assomigli a numeri romani. Essendo Lisp e tutto il resto, proviamo a farlo in qualche modo funzionalmente: abbiamo bisogno di una funzione che farà un attraversamento profondo di un albero e restituisca ogni oggetto per il quale una funzione fornita searchpritorna vera. Questo è persino (semi) ricorsivo di coda.

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

Quindi alcuni codici per l'analisi dei numeri romani, per gentile concessione del codice 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)))

La macro attuale

Prendiamo l'albero della sintassi ( body), lo cerchiamo con la nostra procedura deep-find-all e in qualche modo rendiamo disponibili i numeri romani che troviamo.

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

Allora, cos'è 1 + 2 + 3 + (4 * (5 + 6)) + 7?

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

E per vedere cosa è effettivamente accaduto quando è stata invocata 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
})

Semplicemente un fallback __index per la tabella globale. La vera conversione usando gsub si è rivelata molto più bella di quanto immaginassi.


5

poscritto

Ho provato a seguire quello C ma non l'ho capito. Quindi l'ho fatto in questo modo:

/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 non ha enumma possiamo costruire un dizionario con valori interi sequenziali e piegarli in un array. Ciò riduce il problema alla generazione di tutte le stringhe in sequenza, che viene eseguita concatenando in 4 cicli nidificati. Quindi genera tutte le stringhe, quindi interlaccia ogni stringa con un valore di contatore crescente, risultando in una lunga serie di coppie <stringa> <int> che sono racchiuse in <<... >>e racchiuse in ... per produrre un oggetto dizionario.

Il programma costruisce e installa un dizionario mappando tutti i nomi per i numeri romani sul loro valore corrispondente. Quindi, menzionando i nomi nel testo di origine, viene richiamata la ricerca automatica del nome e si ottiene il valore intero nello stack.

II IV MC pstack

stampe

2
4
600

4

Smalltalk (Smalltalk / X) (87/101 caratteri)

ovviamente potremmo facilmente modificare il tokenizer del parser (in quanto fa parte della libreria di classi, e come tale aperto per modifiche e sempre presente), ma una sfida è influenzare solo le valutazioni in un dato contesto, in modo che il resto del il sistema funziona come al solito.

Versione 1:

definire un numero di variabili nello spazio dei nomi di valutazione. Quindi ciò influirà sulle azioni interattive (anche come valutazione):

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

allora posso fare (in un doIt, ma non nel codice compilato):

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

-> 2019

Avviso: i 101 caratteri includono spazi bianchi; in realtà può essere fatto con 87 caratteri.
Notare anche che, quando definito nello spazio dei nomi globale di Smalltalk, vedrei quelle costanti anche nel codice compilato.

Versione 2:

Utilizzare un hook methodWrapper, che consente il wrapping di qualsiasi codice esistente senza ricompilare. Quanto segue avvolge il tokenizer del Parser per cercare un identificatore romano da scansionare e lo rende un numero intero. La parte difficile è rilevare dinamicamente se il contesto chiamante proviene o meno dall'impero romano. Questo viene fatto usando un segnale di query (che è tecnicamente un'eccezione procedibile):

definire la query:

InRomanScope := QuerySignal new defaultAnswer:false.

Quindi possiamo chiedere in qualsiasi momento ("query InRomanScope") di diventare falsi per impostazione predefinita.

Quindi avvolgere il metodo checkIdentifier dello 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
        ]
    ].

Ora lo scanner funziona come al solito, a meno che non ci troviamo nell'impero romano:

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

-> 2525

possiamo anche compilare il codice:

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

bel tentativo; ma questo non riesce con un errore di sintassi (che è esattamente quello che vogliamo). Tuttavia, nell'impero romano, possiamo compilare:

InRomanScope answer:true do:[

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

e ora, possiamo chiedere qualsiasi numero intero (inviando quel messaggio) dall'interno e dall'esterno di Roma:

(1000 factorial) inTheYear2525

-> 2525


Bello vedere Smalltalk!

4

Haskell, usando la meta-programmazione in Template Haskell e numeri romani :

{-# 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 riserva gli identificatori che iniziano con lettere maiuscole per i costruttori, quindi ho usato lettere minuscole.


4

J - 78 caratteri

Questo vale solo per MMMCMXCIX = 3999, come con le altre soluzioni.

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

Abbattendolo (il richiamo J viene di solito letto da destra a sinistra, a meno che non sia sostituito da parentesi):

  • M`CDM`XLC`IVX- Quattro scatole di lettere. Useremo le matrici numeriche per indicizzarle in queste lettere e costruiremo le parole chiave dei numeri romani.
  • 841,3#79bc5yuukh - Questi sono i dati numerici, strettamente codificati. *
  • (_1,~3#.inv]) - Questo decodificherà i dati di cui sopra, espandendosi in ternario e aggiungendo -1.
  • ('';&;:(...){' ',[)&.>- Associare i numeri a sinistra con le caselle a destra ( &.>), decodificare le matrici di numeri e usarle per indicizzare le lettere. Consideriamo 0 come spazio anteponendo un carattere spazio agli elenchi di lettere. Questa procedura crea elenchi di parole come I II III IV V VI VII VIII IXe M MM MMM.
  • {- Prendi il prodotto cartesiano di queste quattro scatole piene di parole. Ora abbiamo un array 4D di tutti i numeri romani.
  • }.,;L:1- Esegui tutto ciò in un unico elenco 1D di numeri romani e rimuovi la stringa vuota nella parte anteriore perché creerebbe un errore. ( L:è uno spettacolo raro nel golf di J! Di solito non ci sono molti livelli di boxe coinvolti.)
  • }.i.4e3- I numeri interi da 0 a 4000, esclusi gli endpoint.
  • Infine, mettiamo tutto insieme con un incarico globale =:. J ti permette di avere un elenco in scatola di nomi su LHS, come una forma di assegnazione multipla calcolata, quindi questo funziona bene.

Ora lo spazio dei nomi J è pieno di variabili che rappresentano numeri romani.

   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

* Ho bisogno che il numero 2933774030998 venga letto in seguito nella base 3. Succede che posso esprimerlo nella base 79 usando cifre non maggiori di 30, il che è positivo perché J può capire solo cifre fino a 35 (0-9 e poi az). Ciò consente di salvare 3 caratteri oltre i decimali.


3

Pitone

L'idea è semplice come le altre risposte. Ma solo per essere pulito e non inquinare lo spazio dei nomi globale, viene utilizzato un gestore di contesto. Ciò impone anche la restrizione, che è necessario dichiarare in anticipo, l'estensione del numerico romano che si intende utilizzare.

Nota Solo per mantenerlo semplice e non reinventare la ruota, ho utilizzato il pacchetto romano in pitone

Implementazione

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)

dimostrazione

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

Pitone

Questa è probabilmente la soluzione più semplice 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
Meglio usare globals()[var] = valuedi exec().
Ramchandra Apte,

3

D

usando la valutazione della funzione del tempo di compilazione di 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 byte

Richiede la lunghezza massima del numero romano e definisce tutte le variabili.

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

t←t ottiene

'IVXLCDM', Caratteri romani seguiti da

 un chiuso

 elenco vuoto

t[... ] indice t con ...

 il trasposto (per ottenere il giusto ordine)

8⊥⍣¯1 larghezza appropriata base-otto rappresentazione di

 i primi n indici, dove n è

¯1+ uno in meno di

8*⎕ otto alla potenza dell'input numerico

,/ appiattire le righe (ogni rappresentazione)

{...  applica la seguente funzione anonima su ogni rappresentazione ...

(... )[t⍳⍵] corrispondente alle posizioni degli elementi dell'argomento in t , selezionare da ...

   gli arruolati

  1 5∘ר uno e cinque volte ciascuno di

  10* dieci al potere di

  ⍳4 zero attraverso tre

0,⍨ aggiungere zero

2(…)/ su ciascuna finestra scorrevole di lunghezza due, applica il seguente treno di funzioni anonime ...

  ⊣× i tempi argomento sinistro

  ¯1* uno negativo al potere di

  < se l'argomento sinistro è inferiore all'argomento destro

+/ somma

⍵'←', anteponi l'argomento (il numero romano) e una freccia di assegnazione

 formato (per appiattire e convertire il numero in testo)

 eseguire quello (rende il compito al di fuori della funzione anonima)

Provalo online! (utilizzando la lunghezza massima 5)


2

PHP

Ci sono diverse regole per numeri romani validi

  1. Scrivi il valore più grande prima dei valori più bassi

  2. Sottrai solo [I,X,C]prima dei successivi 2 valori maggiori

  3. Sottrai il doppio [I,X,C]prima dei successivi 2 valori maggiori

  4. Sottrai il doppio [I,X,C]prima dei valori maggiori

  5. Combina 4 + 5

Versione online

Passaggio 1 Creare le regole

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

è l'output JSON per tutti i numeri romani validi

$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);

Passaggio 2 Creare elenchi per tutte le regole fino al 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;
}

Passaggio 3 Creare costanti

Combina tutti gli elenchi e definisce le costanti

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

Produzione

Nell'esempio mutiply due versioni valide del numero 8

echo IIX *  VIII;

Bello, ma come posso usarlo? Non parlo fluentemente PHP; Potresti per favore fare un esempio di come questo mi consente di scrivere numeri romani nel codice?
daniero,

@Daniero Ora il codice dovrebbe funzionare
Jörg Hülsermann

Ah, va meglio :)
daniero il

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


Esempio

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

Produzione:

15

1015

Sì, è il 2014

1 + 10 = 11

2 + 10 = 12

3 + 10 = 13

4 + 10 = 14

5 + 10 = 15


Disclaimer: sono sicuro che ci sono altri (e probabilmente migliori!) Modi per farlo anche in Rebol.

PS. La mia roman-to-integerfunzione è una traslitterazione del simpatico algoritmo Ruby dell'istocratico per convertire una stringa numerica romana in un numero. Siamo tornati con grazie! +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

Ciò influisce sulla metatabilità della tabella globale, dandole una nuova funzione di indice. Quando viene richiesta una variabile globale che contiene solo numeri romani, ad esempio XVII, la analizza.

Facile da testare;

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

Provalo online!


1

VBA, 204 byte

Una subroutine che prende nessun ingresso dichiarato, e quando viene eseguito, crea il publicLY accessibili Enum, Rche contiene tutti i valori in numeri romani. Questi valori possono essere utilizzati direttamente, senza fare riferimento a Enum.

Enum contiene valori compresi tra 1 e 3999.

Nota: i terminali "sulle linee 3 e 7 sono inclusi solo per l'evidenziazione della sintassi e non contribuiscono al conteggio secondario

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 spiegato

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
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.