Code-Romanisierung


33

Die Herausforderung besteht darin, alle römischen Ziffern in der von Ihnen gewählten Sprache als gültigen Code zu kennzeichnen.

Sie sollten nicht in Strings oder Ähnlichem vorkommen, sondern wie alle anderen Token, Literale wie ( arabische ) Zahlen, Zeichen oder Strings funktionieren . oder Variablen- / Methoden- / Funktionsbezeichner usw.

In Java müsste beispielsweise Folgendes so kompiliert und ausgeführt werden, als ob ies initialisiert worden wäre 42:

int i = XLII;

Das eigentliche Parsen der Ziffern ist zweitrangig, sodass Sie eine Bibliothek verwenden können, wenn Sie möchten. Dies ist jedoch ein Beliebtheitswettbewerb, sodass Kreativität gefördert wird.

Sie können keine Sprache verwenden, die tatsächlich römische Ziffern verwendet, wenn es so etwas gibt.

Viel Glück.


1
Müssen wir also eine Erweiterung für die Sprache schreiben und dadurch eine neue erstellen?
Kendall Frey

4
Ich werde mich beschweren, wenn ich will, weil die Sprachen, die ich verwende, so nicht erweiterbar sind, dass ich nicht einmal teilnehmen kann.
Kendall Frey

3
@KendallFrey Die Quelle müsste kompiliert und ausgeführt werden. Für Java können Sie einen "Compiler" schreiben, der die Quelle bearbeitet und dann programmgesteuert kompiliert . Eine Möglichkeit zum Kompilieren besteht darin, einenProcess
Justin

1
Scheint in den meisten Sprachen langweilig. Zum Beispiel würde ich in Python einfach ein Skript schreiben, mit astdem die Quelle analysiert wird. Fügen Sie am oberen Rand des AST die Definition der römischen Ziffern von 1 bis 3999 ein. Kompilieren Sie das Ganze und führen Sie es aus. Es ist nur langweilig, den Code zu schreiben, um den Prozess zu handhaben.
Bakuriu

2
@ Bakuriu und dein Kommentar ist auch langweilig. Dies ist ein Beliebtheitswettbewerb, also solltest du versuchen, etwas zu erfinden, das Spaß macht. Ich denke, es gibt hier einige nette Antworten, die einfallsreicher sind (als das Kompilieren einer Skriptsprache).
Daniero

Antworten:


40

C

Es gibt nur so viele römische Ziffern, da 4000 und höher keine Standardnotation haben, und der Präprozessor ist ein wunderbares Dekomprimierungswerkzeug, besonders wenn Sie keine Probleme damit haben, dass der Code undefiniertes Verhalten aufweist.

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

Dies definiert alle römischen Ziffern von Ibis MMMCMXCIXals Aufzählungskonstanten plus _(die durch beliebiges ersetzt werden können) als Null.


12
Absolut genial, schlagen Sie vor, es der nächsten C-Veröffentlichung (C2X?) Als <roman.h> hinzuzufügen! Mit den Formaten% r und% R printf!

3
Ich dachte, ich wüsste, wie man den Präprozessor benutzt, bis jetzt: | Könnten Sie Ihre Antwort mit einem minimalen Verwendungsbeispiel dieser Aufzählung aktualisieren?
klingt.net

@YiminRong Und scanfauch :) @ klingt.net Ich bin nicht sicher, was für ein Beispiel Sie suchen. Ein ziemlich einfaches wäreint main() { return MMMCMXCIX - M - M - M - CM - XC - IX; }
hvd

Ich mag die Idee, aber warum hast du dich für undefiniertes Verhalten entschieden, als definiertes Verhalten ziemlich einfach war? Nicht urteilen, nur neugierig?
CasaDeRobison

1
@CasaDeRobison Hauptsächlich zum Spaß. Ich mag es, weil es einer der wenigen Fälle von undefiniertem Verhalten zur Vorverarbeitungszeit ist, der von aktuellen Compilern nicht als Fehler gemeldet wird und wahrscheinlich auch nicht in der Zukunft sein wird. Ich schreibe so etwas nie in Code, der nützlich sein soll, aber der hier veröffentlichte Code ist nicht nützlich. Welche bessere Gelegenheit gibt es, es zu versuchen?
HDV

15

Rubin

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

Alle (Groß-) römischen Ziffern werden nun wie ihre Dezimaläquivalente analysiert. Das einzige Problem ist, dass sie noch zuweisbar sind: Sie können es tun X = 9, aber nicht 10 = 9. Ich glaube nicht, dass es einen Weg gibt, das zu beheben.


1
Dies ist perfekt. Die Zuweisungssache ist kein Problem; Sie können Zuweisungen verwenden, um alle möglichen dummen Dinge zu erledigen.
Daniero

12

JavaScript (ES6)

Verwenden Sie Proxy, um römische Ziffern zu fangen.

Testbar in Firefox (aktuell) auf JSFiddle .
In Chrome (mit Traceur) nicht testbar, da die ProxyImplementierung fehlerhaft ist.

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

Verwendung:

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

10

C & C ++ (aktualisierte Antwort)

Wie in einem Kommentar festgestellt, hatte meine ursprüngliche Lösung zwei Probleme:

  1. Optionale Parameter sind nur in C99 und späteren Standards der Sprachfamilie verfügbar.
  2. Das nachfolgende Komma in der Enum-Definition gilt auch für C99 und höher.

Da ich wollte, dass mein Code so allgemein wie möglich ist, um auf älteren Plattformen zu arbeiten, beschloss ich, es noch einmal zu versuchen. Es ist länger als zuvor, funktioniert jedoch auf Compilern und Präprozessoren, die auf den Kompatibilitätsmodus C89 / C90 eingestellt sind. Allen Makros wird eine angemessene Anzahl von Argumenten im Quellcode übergeben, obwohl diese Makros manchmal zu nichts "expandieren".

Visual C ++ 2013 (auch bekannt als Version 12) gibt Warnungen über fehlende Parameter aus, aber weder mcpp (ein Open-Source-Präprozessor, der hohe Übereinstimmung mit dem Standard behauptet) noch gcc 4.8.1 (mit -std = iso9899: 1990 -pedantic-errors-Schaltern) Warnungen oder Fehler für diese Makroaufrufe mit einer effektiv leeren Argumentliste.

Nach Durchsicht der relevanten Norm (ANSI / ISO 9899-1990, 6.8.3, Macro Replacement) besteht meines Erachtens hinreichende Unklarheit, dass dies nicht als Nicht-Norm angesehen werden sollte. "Die Anzahl der Argumente in einem Aufruf eines funktionsähnlichen Makros muss mit der Anzahl der Parameter in der Makrodefinition übereinstimmen ...". Es scheint eine leere Argumentliste nicht auszuschließen, solange die erforderlichen Klammern (und Kommas bei mehreren Parametern) vorhanden sind, um das Makro aufzurufen

Das Problem mit dem nachstehenden Komma wird gelöst, indem der Aufzählung ein zusätzlicher Bezeichner hinzugefügt wird (in meinem Fall MMMM, der für den Bezeichner so sinnvoll wie alles andere zu sein scheint, als 3999 zu folgen, auch wenn er nicht den anerkannten Regeln der römischen Ziffernfolge entspricht genau).

Eine etwas sauberere Lösung würde darin bestehen, die Aufzählung und die unterstützenden Makros in eine separate Header-Datei zu verschieben, wie in einem Kommentar an anderer Stelle impliziert, und die Undef der Makronamen unmittelbar nach ihrer Verwendung zu verwenden, um eine Verschmutzung des Namespace zu vermeiden. Zweifellos sollten auch bessere Makronamen gewählt werden, dies ist jedoch für die jeweilige Aufgabe angemessen.

Meine aktualisierte Lösung, gefolgt von meiner ursprünglichen Lösung:

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

Die ursprüngliche Antwort (die die ersten sechs Upvotes erhalten hat, falls dies also noch einmal von niemandem bewertet wird, sollten Sie nicht glauben, dass meine aktualisierte Lösung die Upvotes erhalten hat):

Im gleichen Sinne wie eine frühere Antwort, jedoch auf eine Weise, die nur mit definiertem Verhalten portierbar sein sollte (obwohl sich unterschiedliche Umgebungen in einigen Aspekten des Präprozessors nicht immer einig sind). Behandelt einige Parameter als optional, ignoriert andere, sollte auf Präprozessoren funktionieren, die das __VA_ARGS__Makro nicht unterstützen , einschließlich C ++. Verwendet indirekte Makros, um sicherzustellen, dass die Parameter vor dem Einfügen des Tokens erweitert werden. es ist zwar immer noch schwierig und wahrscheinlich nicht leicht zu lesen, aber einfacher):

#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, aber beachten Sie, dass die Verwendung leerer Makroargumente und das Komma am Ende einer Enumeratorliste neue Funktionen von C99 und C ++ 11 sind und sowohl C99 als auch C ++ 11 unterstützen __VA_ARGS__.
HDV

Mist, wenn du nicht Recht hast! Ich schätze, ich habe das die ganze Zeit als Erweiterung gesehen. Ah, gut. :)
CasaDeRobison

8

Common Lisp

Das Folgende ist eine ziemlich lange Erklärung, wie ich ein Makro erstellt habe, das Sie so verwenden können:

(roman-progn
  (+ XIV XXVIII))

Wenn ein Makro in Common Lisp aufgerufen wird, verhält es sich im Grunde genommen wie eine Funktion, nur dass die Argumente empfangen werden, bevor sie ausgewertet werden. Da es sich bei Common Lisp-Code nur um Daten handelt, erhalten wir eine (verschachtelte) Liste, die einen nicht analysierten Syntaxbaum darstellt, mit dem wir alles tun können, was wir wollen, und dies geschieht in der Kompilierungszeit.

Hilfsfunktionen

Der erste Schritt des Plans besteht darin, diesen Baum auf alles zu scannen, was wie römische Ziffern aussieht. Da dies Lisp und alles ist, wollen wir versuchen, es etwas funktionaler zu machen: Wir brauchen eine Funktion, die einen Baum tief durchläuft und jedes Objekt searchpzurückgibt, für das eine bereitgestellte Funktion true zurückgibt. Dieser ist sogar (halb-) schwanzrekursiv.

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

Dann ein Code zum Parsen der römischen Ziffern, mit freundlicher Genehmigung von 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)))

Das eigentliche Makro

Wir nehmen den Syntaxbaum ( body), durchsuchen ihn mit unserem Deep-Find-All-Verfahren und stellen die gefundenen römischen Ziffern irgendwie zur Verfügung.

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

Also, was ist das 1 + 2 + 3 + (4 * (5 + 6)) + 7?

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

Und um zu sehen, was tatsächlich passiert ist, als das Makro aufgerufen wurde:

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

Einfach ein Fallback-Index für die globale Tabelle. Die eigentliche Konvertierung mit gsub fiel viel hübscher aus, als ich es mir vorgestellt hatte.


5

Nachsatz

Ich habe versucht, dem C zu folgen, aber ich habe es nicht verstanden. Also habe ich es so gemacht:

/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 hat keine, enumaber wir können ein Wörterbuch mit sequentiellen Ganzzahlwerten erstellen und diese in ein Array falten. Dies reduziert das Problem, alle Zeichenfolgen nacheinander zu generieren, indem 4 verschachtelte Schleifen verkettet werden. Es generiert also alle Zeichenfolgen und verschachtelt jede Zeichenfolge mit einem ansteigenden Zählerwert. Dies führt zu einer langen Reihe von <string> <int> -Paaren auf dem Stapel, die in <<... eingebunden sind >>, um ein Dictionary-Objekt zu erstellen.

Das Programm erstellt und installiert ein Wörterbuch, in dem alle Namen der römischen Ziffern dem entsprechenden Wert zugeordnet sind. Das Erwähnen der Namen im Quelltext ruft also die automatische Namenssuche auf und liefert den ganzzahligen Wert auf dem Stapel.

II IV MC pstack

druckt

2
4
600

4

Smalltalk (Smalltalk / X) (87/101 Zeichen)

Natürlich könnten wir den Tokenizer des Parsers leicht modifizieren (da er Teil der Klassenbibliothek ist und als solcher für Modifikationen offen und immer vorhanden ist), aber eine Herausforderung besteht darin, nur Evaluierungen in einem gegebenen Kontext zu beeinflussen, so dass der Rest des Parsers System funktioniert wie gewohnt.

Version 1:

Definieren Sie eine Reihe von Variablen im Auswertungs-Namespace. Dies wirkt sich also auf interaktive Aufgaben (auch bekannt als evals) aus:

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

dann kann ich (in einem doIt, aber nicht in kompiliertem Code) tun:

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

-> 2019

Hinweis: Die 101 Zeichen enthalten Leerzeichen. Eigentlich kann es mit 87 Zeichen gemacht werden.
Beachten Sie auch, dass beim Definieren im globalen Smalltalk-Namespace diese Konstanten auch in kompiliertem Code angezeigt werden.

Version 2:

Verwenden Sie einen methodWrapper-Hook, mit dem vorhandener Code ohne Neukompilierung umbrochen werden kann. Der folgende Befehl umschließt den Tokenizer des Parsers, um nach einer römischen ID zu suchen, die gescannt werden soll, und macht sie zu einer Ganzzahl. Der schwierige Teil besteht darin, dynamisch zu erkennen, ob der aufrufende Kontext aus dem römischen Reich stammt oder nicht. Dies geschieht mit einem Abfragesignal (was technisch eine verfahrensfähige Ausnahme darstellt):

Definieren Sie die Abfrage:

InRomanScope := QuerySignal new defaultAnswer:false.

Daher können wir jederzeit ("InRomanScope-Abfrage") darum bitten, standardmäßig false zu erhalten.

Wickeln Sie dann die checkIdentifier-Methode des Scanners ein:

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

Jetzt funktioniert der Scanner wie gewohnt, es sei denn, wir befinden uns im römischen Reich:

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

-> 2525

Wir können sogar Code kompilieren:

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

netter Versuch; Dies schlägt jedoch mit einem Syntaxfehler fehl (was genau das ist, was wir wollen). Im römischen Reich KÖNNEN wir jedoch kompilieren:

InRomanScope answer:true do:[

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

und jetzt können wir eine beliebige Ganzzahl (die diese Nachricht sendet) von innerhalb und außerhalb Roms fragen:

(1000 factorial) inTheYear2525

-> 2525


Schön, Smalltalk zu sehen!

4

Haskell mit Meta-Programmierung in Template Haskell und römischen Ziffern :

{-# 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 reserviert Bezeichner, die mit Großbuchstaben beginnen, für Konstruktoren, daher habe ich Kleinbuchstaben verwendet.


4

J - 78 Zeichen

Dies gilt wie bei den anderen Lösungen nur für MMMCMXCIX = 3999.

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

Zerlegen (Rückruf J wird normalerweise von rechts nach links gelesen, sofern nicht durch Klammern ersetzt):

  • M`CDM`XLC`IVX- Vier Briefkästen. Wir werden numerische Arrays als Index für diese Buchstaben verwenden und Unterwörter aus römischen Ziffern aufbauen.
  • 841,3#79bc5yuukh - Dies sind die numerischen Daten, fest codiert. *
  • (_1,~3#.inv]) - Dies dekodiert die obigen Daten, indem es ternär erweitert und -1 angehängt wird.
  • ('';&;:(...){' ',[)&.>- Kombinieren Sie die Zahlen auf der linken Seite mit den Feldern auf der rechten Seite ( &.>), dekodieren Sie die Zahlenfelder und verwenden Sie sie, um die Buchstaben zu indizieren. Wir behandeln 0 als Leerzeichen, indem wir den Buchstabenlisten ein Leerzeichen voranstellen. Diese Prozedur erstellt Listen mit Wörtern wie I II III IV V VI VII VIII IXund M MM MMM.
  • {- Nehmen Sie das kartesische Produkt dieser vier Schachteln voller Wörter. Jetzt haben wir ein 4D Array aller römischen Ziffern.
  • }.,;L:1- Führen Sie all dies in einer einzigen 1D-Liste mit römischen Ziffern aus und entfernen Sie die leere Zeichenfolge an der Vorderseite, da dies zu einem Fehler führen würde. ( L:ist ein seltener Anblick in J Golf! Normalerweise sind nicht so viele Levels des Boxens involviert.)
  • }.i.4e3- Die Ganzzahlen von 0 bis 4000, ohne die Endpunkte.
  • Schließlich haben wir alles mit einem globalen Auftrag zusammengestellt =:. Mit J können Sie eine Box-Liste mit Namen auf der LHS als eine Form der berechneten Mehrfachzuweisung haben, damit dies gut funktioniert.

Jetzt ist der J-Namespace voll mit Variablen, die römische Ziffern darstellen.

   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

* Ich brauche die Nummer 2933774030998, um sie später in der Basis 3 lesen zu können. Es kommt vor, dass ich sie in der Basis 79 mit Ziffern von maximal 30 ausdrücken kann az). Dadurch werden 3 Zeichen über dem Komma gespeichert.


3

Python

Die Idee ist einfach wie die anderen Antworten. Aber nur um ordentlich zu sein und den globalen Namespace nicht zu verschmutzen, wird ein Kontextmanager verwendet. Dies beinhaltet auch die Einschränkung, dass Sie vorab den Umfang der römischen Zahlen, die Sie verwenden möchten, deklarieren müssen.

Hinweis Um es einfach zu halten und das Rad nicht neu zu erfinden, habe ich das römische Python-Paket verwendet

Implementierung

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

Dies ist möglicherweise die einfachste Lösung mit 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
Besser zu benutzen globals()[var] = valueals exec().
Ramchandra Apte

3

D

unter Verwendung der Auswertung der Kompilierzeitfunktion von 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

Fordert zur Eingabe der maximalen Länge der römischen Zahl auf und definiert alle Variablen.

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

t←t bekommt

'IVXLCDM', Römische Zeichen gefolgt von

 eine geschlossene

 leere Liste

t[] Index t mit…

 die transponierte (um die richtige Reihenfolge zu bekommen)

8⊥⍣¯1 geeignete Breite Basis-Acht-Darstellung von

 die ersten n Indizes, wobei n ist

¯1+ einer weniger als

8*⎕ acht hoch numerische Eingabe

,/ Zeilen abflachen (jede Darstellung)

{...  wende die folgende anonyme Funktion auf jede Darstellung an ...

()[t⍳⍵] Entsprechend den Positionen der Argumente in t wählen Sie aus…

   die eingetragenen

  1 5∘ר jeweils ein- und fünfmal

  10* zehn hoch

  ⍳4 null bis drei

0,⍨ Null anhängen

2(…)/ Wenden Sie auf jedes Schiebefenster der Länge 2 den folgenden anonymen Funktionszug an…

  ⊣× das linke argument mal

  ¯1* negativ hoch hoch

  < ob das linke Argument kleiner als das rechte Argument ist

+/ Summe

⍵'←', Stellen Sie das Argument (die römische Ziffer) und einen Zuweisungspfeil voran

 Formatieren (um die Zahl zu reduzieren und in Text umzuwandeln)

 führe das aus (macht die Zuweisung außerhalb der anonymen Funktion)

Probieren Sie es online! (mit maximaler Länge 5)


2

PHP

Es gibt mehrere Regeln für gültige römische Zahlen

  1. Schreiben Sie den größten Wert vor die niedrigeren Werte

  2. Subtrahieren Sie nur [I,X,C]vor den nächsten 2 größeren Werten

  3. Subtrahieren Sie double [I,X,C]vor den nächsten 2 größeren Werten

  4. Subtrahieren Sie double [I,X,C]vor den größeren Werten

  5. Kombiniere 4 + 5

Online Version

Schritt 1 Erstellen Sie die Regeln

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

ist die JSON-Ausgabe für alle gültigen römischen Zahlen

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

Schritt 2 Erstellen Sie Listen für alle Regeln bis 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;
}

Schritt 3 Erstellen Sie Konstanten

Kombinieren Sie alle Listen und definieren Sie Konstanten

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

Ausgabe

Im Beispiel multiplizieren Sie zwei gültige Versionen der Nummer 8

echo IIX *  VIII;

Schön, aber wie verwende ich das? Ich spreche nicht fließend PHP. Könnten Sie bitte ein Beispiel geben, wie ich damit römische Ziffern in den Code schreiben kann?
Daniero

@Daniero Jetzt sollte der Code funktionieren
Jörg Hülsermann

Ah, das ist besser :)
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]
]


Beispiel

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

Ausgabe:

15

1015

Ja, es ist 2014

1 + 10 = 11

2 + 10 = 12

3 + 10 = 13

4 + 10 = 14

5 + 10 = 15


Haftungsausschluss: Ich bin sicher, dass es auch in Rebol andere (und wahrscheinlich bessere!) Möglichkeiten gibt, dies zu tun.

PS. Meine roman-to-integerFunktion ist eine Transliteration des netten Ruby-Algorithmus von Histocrat zum Umwandeln einer Zeichenfolge mit römischen Zahlen in eine Zahl. Mit Dank zurückgekehrt! +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

Dies wirkt sich auf die Metatabelle der globalen Tabelle aus und gibt ihr eine neue Indexfunktion. Wenn eine globale Variable abgefragt wird, die nur römische Ziffern enthält XVII, analysiert sie sie beispielsweise.

Einfach zu testen;

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

Probieren Sie es online!


1

VBA, 204 Bytes

Ein erklärtes Unterprogramm , das keine Eingabe erfolgt, und wenn ausführen, erstellt das publicly zugänglich Enum, R, die alle der römischen Ziffer Werte enthält. Diese Werte können direkt verwendet werden, ohne auf die Enumeration zu verweisen.

Aufzählungshaltewerte von 1 bis 3999.

Hinweis: Die Terminals "in den Zeilen 3 und 7 dienen nur der Hervorhebung der Syntax und tragen nicht zum Bytecount bei

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 und erklärt

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
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.