Romanizacja kodu

33

Wyzwanie polega na tym, aby wszystkie cyfry rzymskie były poprawne w wybranym języku.

Powinny one nie pojawiają się wewnątrz ciągi lub coś podobnego, ale działają podobnie jak wszelkie inne tokeny, literały takich jak ( arabskich ), liczb, znaków lub ciągów; lub identyfikatory zmiennej / metody / funkcji itp.

Na przykład w Javie następujące elementy musiałyby się skompilować i uruchomić tak, jakby ibyły zainicjowane 42:

int i = XLII;

Rzeczywista analiza liczb jest drugorzędna, więc możesz użyć biblioteki, jeśli chcesz, ale jest to konkurs popularności, więc zachęca się do kreatywności.

Nie możesz używać żadnego języka, który faktycznie używa cyfr rzymskich, jeśli istnieje coś takiego.

Powodzenia.

daniero
źródło
1
Więc musimy napisać rozszerzenie do języka, tworząc w ten sposób nowy?
Kendall Frey
4
Będę narzekać, jeśli zechcę, ponieważ języków, których używam, nie można rozszerzać w ten sposób, więc nie mogę nawet wziąć udziału.
Kendall Frey
3
@KendallFrey Źródło musiałoby się skompilować i uruchomić. W przypadku języka Java można napisać „kompilator”, który edytuje źródło, a następnie kompiluje programowo . Jednym ze sposobów takiej kompilacji jest uruchomienieProcess
Justin
1
Wydaje się nudny w większości języków. Na przykład w pythonie po prostu napisałbym skrypt, który używa astdo analizy źródła. Wstaw na górze AST definicję cyfr rzymskich od 1 do 3999. Skompiluj całość i uruchom ją. Pisanie kodu do obsługi procesu jest nudne.
Bakuriu
2
@Bakuriu i twój komentarz też jest nudny. To konkurs popularności, więc powinieneś wymyślić coś fajnego. Myślę, że jest tu kilka fajnych odpowiedzi, które są bardziej pomysłowe (niż kompilacja języka skryptowego).
daniero

Odpowiedzi:

40

do

Jest tylko tyle cyfr rzymskich, ponieważ 4000 i wyższe nie mają standardowej notacji, a preprocesor jest wspaniałym narzędziem do dekompresji, szczególnie jeśli nie masz problemów z faktem, że kod ma nieokreślone zachowanie.

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

To definiuje wszystkie cyfry rzymskie od Ido MMMCMXCIXjako stałe wyliczenia, plus _(który można zastąpić czymkolwiek chcesz) jako zero.

hvd
źródło
12
Absolutnie genialny, zaproponuj dodanie go do następnej wersji C (C2X?) Jako <roman.h>! W formatach% r i% R printf!
3
Myślałem, że wiem, jak korzystać z preprocesora, aż do teraz: | Czy możesz zaktualizować swoją odpowiedź, używając minimalnego przykładu użycia tego wyliczenia?
klingt.net
@YiminRong I scanfteż :) @ klingt.net Nie jestem pewien, jakiego rodzaju przykładu szukasz. Dość prosty byłbyint main() { return MMMCMXCIX - M - M - M - CM - XC - IX; }
hvd
Podoba mi się ten pomysł, ale dlaczego zdecydowałeś się na nieokreślone zachowanie, gdy zdefiniowane zachowanie było dość proste? Nie sądzisz, tylko ciekawi?
CasaDeRobison
1
@CasaDeRobison Głównie dla zabawy. Podoba mi się, ponieważ jest to jeden z niewielu przypadków nieokreślonego zachowania w czasie przetwarzania wstępnego, które nie jest oznaczane jako błąd przez obecne kompilatory i prawdopodobnie nie będzie w przyszłości. Nigdy nie piszę czegoś takiego w kodzie, który ma być przydatny, ale kod zamieszczony tutaj nie ma być przydatny, więc jaką lepszą okazję spróbować?
hvd
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

Wszelkie (wielkie litery) cyfry rzymskie będą teraz analizowane jak ich dziesiętne odpowiedniki. Jedynym problemem jest to, że nadal można je przypisać: możesz to zrobić X = 9, ale nie 10 = 9. Nie sądzę, żeby można to naprawić.

histocrat
źródło
1
To jest doskonałe. Zadanie nie jest problemem; Możesz używać zadań do robienia najgłupszych rzeczy.
daniero
12

JavaScript (ES6)

Użyj, Proxyaby złapać cyfry rzymskie.

Testowany w Firefoksie (najnowszy) na JSFiddle .
Nie można przetestować w Chrome (z Traceur), ponieważ Proxyimplementacja jest zepsuta.

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

Stosowanie:

with (romanEnv) {
  var i = MIV + XLVIII;
  console.log(i); // 1052
}
Florent
źródło
10

C i C ++ (zaktualizowana odpowiedź)

Jak zauważono w komentarzu, moje oryginalne rozwiązanie miało dwa problemy:

  1. Parametry opcjonalne są dostępne tylko w C99 i późniejszych standardach rodziny języków.
  2. Końcowy przecinek w definicji wyliczenia jest również specyficzny dla C99 i późniejszych.

Ponieważ chciałem, aby mój kod był tak ogólny, jak to tylko możliwe, do pracy na starszych platformach, postanowiłem spróbować go w inny sposób. Jest dłuższy niż wcześniej, ale działa na kompilatorach i preprocesorach ustawionych na tryb zgodności C89 / C90. Wszystkie makra przekazują odpowiednią liczbę argumentów w kodzie źródłowym, choć czasami te makra „rozwijają się” w nicość.

Visual C ++ 2013 (aka wersja 12) emituje ostrzeżenia o brakujących parametrach, ale ani mcpp (preprocesor open source, który twierdzi, że jest zgodny ze standardem), ani gcc 4.8.1 (z przełącznikami -std = iso9899: 1990 -antantic-error) ostrzeżenia lub błędy dla tych wywołań makr ze skutecznie pustą listą argumentów.

Po przejrzeniu odpowiedniej normy (ANSI / ISO 9899-1990, 6.8.3, Makropolecenie) uważam, że istnieje wystarczająca dwuznaczność, że nie należy tego uważać za niestandardowy. „Liczba argumentów w wywołaniu makra podobnego do funkcji musi być zgodna z liczbą parametrów w definicji makra ...”. Wydaje się, że nie wyklucza pustej listy argumentów, dopóki potrzebne są nawiasy (i przecinki w przypadku wielu parametrów) do wywołania makra

Jeśli chodzi o problem z przecinkiem końcowym, ten problem rozwiązuje się poprzez dodanie dodatkowego identyfikatora do wyliczenia (w moim przypadku MMMM, który wydaje się równie rozsądny, jak cokolwiek, aby identyfikator podążał za 3999, nawet jeśli nie przestrzega przyjętych reguł sekwencjonowania cyfr rzymskich dokładnie).

Nieco czystsze rozwiązanie wymagałoby przeniesienia wyliczenia i obsługi makr do osobnego pliku nagłówka, co sugerowano w komentarzu w innym miejscu, i użycie undef nazw makr natychmiast po ich użyciu, aby uniknąć zanieczyszczenia przestrzeni nazw. Bez wątpienia należy również wybrać lepsze nazwy makr, ale jest to wystarczające do danego zadania.

Moje zaktualizowane rozwiązanie, a następnie moje oryginalne rozwiązanie:

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

Oryginalna odpowiedź (która otrzymała pierwsze sześć głosów pozytywnych, więc jeśli nikt nigdy więcej nie głosuje ponownie, nie powinieneś myśleć, że moje zaktualizowane rozwiązanie uzyskało pozytywne głosy):

W tym samym duchu, co wcześniejsza odpowiedź, ale wykonane w sposób, który powinien być przenośny przy użyciu tylko określonych zachowań (chociaż różne środowiska nie zawsze zgadzają się co do niektórych aspektów preprocesora). Traktuje niektóre parametry jako opcjonalne, ignoruje inne, powinien działać na preprocesorach, które nie obsługują __VA_ARGS__makra, w tym C ++, używa makr pośrednich, aby zapewnić, że parametry zostaną rozszerzone przed wklejeniem tokena, a na koniec jest krótszy i myślę, że łatwiej go odczytać ( choć nadal jest trudny i prawdopodobnie niełatwy do odczytania, po prostu łatwiejszy):

#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 };
CasaDeRobison
źródło
1
+1, ale zauważ, że użycie pustych argumentów makr i przecinek na końcu listy modułu wyliczającego są nowymi funkcjami C99 i C ++ 11, a zarówno C99, jak i C ++ 11 obsługują __VA_ARGS__.
hvd
Cholera, jeśli nie masz racji! Wydaje mi się, że przez cały ten czas używałem tego rozszerzenia. Ach tak. :)
CasaDeRobison
8

Common Lisp

Poniżej znajduje się dość długie wyjaśnienie, w jaki sposób utworzyłem makro, którego można użyć w następujący sposób:

(roman-progn
  (+ XIV XXVIII))

Kiedy makro jest wywoływane w Common Lisp, to w zasadzie działa jak funkcja, tyle że argumenty są odbierane wcześniej ich oceną. W rzeczywistości, ponieważ we wspólnym kodzie Lisp są tylko dane, otrzymujemy (zagnieżdżoną) listę reprezentującą nieprzetworzone drzewo składniowe, z którym możemy zrobić, co chcemy, i odbywa się to w czasie kompilacji.

Funkcje pomocnicze

Pierwszym krokiem planu jest wzięcie tego drzewa i zeskanowanie go w poszukiwaniu czegoś, co wygląda jak cyfry rzymskie. Ponieważ jest to Lisp i tak dalej, spróbujmy zrobić to nieco funkcjonalnie: potrzebujemy funkcji, która wykona głębokie przejście do drzewa i zwróci każdy obiekt, dla którego podana funkcja searchpzwraca wartość true. Ten jest nawet (pół) rekurencyjny.

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

Następnie kod do parsowania cyfr rzymskich, dzięki uprzejmości 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)))

Rzeczywiste makro

Bierzemy drzewo składniowe ( body), przeszukujemy go za pomocą naszej procedury głębokiego znajdowania i w jakiś sposób udostępniamy znalezione cyfry rzymskie.

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

Więc co to jest 1 + 2 + 3 + (4 * (5 + 6)) + 7?

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

I aby zobaczyć, co faktycznie się stało, gdy wywołano makro:

(macroexpand-1 +)
=> (LET ((VII 7) (VI 6) (V 5) (IV 4) (III 3) (II 2) (I 1))
   (+ I II III (* IV (+ V VI)) VII))
daniero
źródło
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
})

Po prostu rezerwowy __index dla tabeli globalnej. Rzeczywista konwersja za pomocą gsub okazała się znacznie ładniejsza niż się spodziewałem.

mniip
źródło
5

Postscriptum

Próbowałem podążać za C, ale nie rozumiałem tego. Zrobiłem to w ten sposób:

/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 nie ma, enumale możemy zbudować słownik z sekwencyjnymi wartościami całkowitymi i złożyć je w tablicę. Zmniejsza to problem generowania wszystkich ciągów w sekwencji, co odbywa się poprzez konkatenację w 4 zagnieżdżonych pętlach. Tak więc generuje wszystkie ciągi, a następnie przeplata każdy ciąg ze wzrastającą wartością licznika, w wyniku czego powstaje długa seria par <ciąg> <int> na stosie, które są owinięte w <<...>> celu utworzenia obiektu słownikowego.

Program konstruuje i instaluje słownik mapujący wszystkie nazwy cyfr rzymskich na odpowiadające im wartości. Więc wzmianka o nazwach w tekście źródłowym wywołuje automatyczne wyszukiwanie nazw i zwraca wartość całkowitą na stosie.

II IV MC pstack

odbitki

2
4
600
luser droog
źródło
4

Smalltalk (Smalltalk / X) (87/101 znaków)

oczywiście moglibyśmy łatwo zmodyfikować tokenizer analizatora składni (ponieważ jest on częścią biblioteki klas i jako taki jest otwarty na modyfikacje i zawsze obecny), ale wyzwaniem jest wpływanie tylko na oceny w danym kontekście, tak aby reszta system działa jak zwykle.

Wersja 1:

zdefiniuj liczbę zmiennych w przestrzeni nazw oceny. Wpłynie to na interaktywne doIts (inaczej evals):

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

wtedy mogę zrobić (w doIt, ale nie w skompilowanym kodzie):

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

-> 2019

Uwaga: 101 znaków zawiera białe znaki; w rzeczywistości można to zrobić za pomocą 87 znaków.
Zauważ też, że kiedy zdefiniujesz w globalnej przestrzeni nazw Smalltalk, zobaczyłbym te stałe również w skompilowanym kodzie.

Wersja 2:

Użyj metody hookWrapper, która pozwala na zawijanie dowolnego istniejącego kodu bez ponownej kompilacji. Poniżej otacza tokenizator parsera, aby wyszukać rzymski identyfikator, który ma zostać zeskanowany, i czyni go liczbą całkowitą. Trudną częścią jest dynamiczne wykrywanie, czy kontekst wywołania pochodzi z imperium rzymskiego, czy nie. Odbywa się to za pomocą sygnału zapytania (który technicznie jest wyjątkowym postępowaniem):

zdefiniuj zapytanie:

InRomanScope := QuerySignal new defaultAnswer:false.

Możemy więc w dowolnym momencie poprosić („zapytanie InRomanScope”) o domyślną wartość false.

Następnie zawiń metodę checkIdentifier skanera:

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

Teraz skaner działa jak zwykle, chyba że jesteśmy w imperium rzymskim:

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

-> 2525

możemy nawet skompilować kod:

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

Niezła próba; ale kończy się to błędem składniowym (dokładnie tego chcemy). Jednak w imperium rzymskim MOŻEMY skompilować:

InRomanScope answer:true do:[

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

a teraz możemy zapytać o dowolną liczbę całkowitą (wysyłającą tę wiadomość) z wewnątrz i spoza Rzymu:

(1000 factorial) inTheYear2525

-> 2525

blabla999
źródło
Miło widzieć Smalltalk!
4

Haskell, używając metaprogramowania w szablonie Haskell i cyfrach rzymskich :

{-# 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 rezerwuje identyfikatory zaczynające się od wielkich liter dla konstruktorów, więc użyłem małych liter.

Petr Pudlák
źródło
4

J - 78 znaków

To idzie tylko do MMMCMXCIX = 3999, jak w przypadku innych rozwiązań.

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

Podział (pamiętaj, że J jest zwykle odczytywany od prawej do lewej, chyba że zostanie zastąpiony nawiasami):

  • M`CDM`XLC`IVX- Cztery skrzynki z literami. Użyjemy tablic numerycznych do indeksowania tych liter i zbudujemy podmowy cyfr rzymskich.
  • 841,3#79bc5yuukh - To są dane numeryczne, ściśle zakodowane. *
  • (_1,~3#.inv]) - To zdekoduje powyższe dane, rozszerzając trójskładnikowe i dodając -1.
  • ('';&;:(...){' ',[)&.>- Parując liczby po lewej stronie z polami po prawej stronie ( &.>), odkoduj tablice liczb i użyj ich do indeksowania liter. 0 traktujemy jako spację, dodając znak spacji do list liter. Ta procedura tworzy listy słów takich jak I II III IV V VI VII VIII IXi M MM MMM.
  • {- Weź kartezjański produkt z tych czterech pudeł pełnych słów. Teraz mamy tablicę 4D wszystkich cyfr rzymskich.
  • }.,;L:1- Uruchom to wszystko na pojedynczą listę 1D cyfr rzymskich i usuń pusty ciąg z przodu, ponieważ spowoduje to błąd. ( L:jest rzadkim widokiem w golfie J! Zwykle nie ma tak wielu poziomów boksu.)
  • }.i.4e3- Liczba całkowita od 0 do 4000, z wyłączeniem punktów końcowych.
  • Wreszcie, wszystko zestawiliśmy razem z zadaniem globalnym =:. J pozwala ci mieć listę LHS z nazwami w ramkach, jako formę obliczonego wielokrotnego przypisania, więc to działa dobrze.

Teraz przestrzeń nazw J jest pełna zmiennych reprezentujących cyfry rzymskie.

   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

* Potrzebuję numeru 2933774030998, aby później odczytać go w bazie 3. Zdarza się, że mogę wyrazić go w bazie 79 za pomocą cyfr nie większych niż 30, co jest dobre, ponieważ J może zrozumieć cyfry do 35 (0-9, a następnie az). To oszczędza 3 znaki po przecinku.

algorytmshark
źródło
3

Pyton

Pomysł jest prosty, podobnie jak inne odpowiedzi. Aby jednak zachować porządek i nie zanieczyszczać globalnej przestrzeni nazw, używany jest menedżer kontekstu. Nakłada to również ograniczenie, które należy wcześniej zadeklarować, zakres cyfr rzymskich, którego planujesz użyć.

Uwaga Aby zachować prostotę i nie wymyślać na nowo koła, skorzystałem z pakietu Roman Python

Realizacja

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)

Próbny

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
Abhijit
źródło
3

Pyton

Jest to prawdopodobnie najprostsze rozwiązanie z użyciem Pythona:

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
Dhara
źródło
3
Lepszy w użyciu globals()[var] = valueniż exec().
Ramchandra Apte
3

re

za pomocą oceny funkcji czasu kompilacji 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());
maniak zapadkowy
źródło
3

APL (Dyalog APL) , 77 bajtów

Monituje o maksymalną długość cyfr rzymskich i definiuje wszystkie zmienne.

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

t←t dostaje

'IVXLCDM', Rzymskie znaki, po których następuje

 zamknięty

 pusta lista

t[] Indeks t z…

 transponowane (aby uzyskać właściwą kolejność)

8⊥⍣¯1 odpowiednia szerokość podstawowa ósemkowa reprezentacja

 pierwsze n wskaźników, gdzie n jest

¯1+ jeden mniej niż

8*⎕ osiem do mocy wprowadzania numerycznego

,/ spłaszcz wiersze (każda reprezentacja)

{ Zastosuj następującą anonimową funkcję do każdej reprezentacji…

()[t⍳⍵] Odpowiadające pozycjom argumentów wt , wybierz z…

   zaciągnął się

  1 5∘ר jeden i pięć razy każdy

  10* dziesięć do potęgi

  ⍳4 od zera do trzech

0,⍨ dołącz zero

2(…)/ na każdym przesuwanym oknie o długości dwóch zastosuj następujący ciąg funkcji anonimowych…

  ⊣× czasy lewych argumentów

  ¯1* negatywny do potęgi

  < czy lewy argument jest mniejszy niż prawy argument

+/ suma

⍵'←', wstaw argument (cyfra rzymska) i strzałkę przypisania

 format (spłaszczyć i przekonwertować liczbę na tekst)

 wykonaj to (powoduje przypisanie poza funkcję anonimową)

Wypróbuj online! (przy użyciu maksymalnej długości 5)

Adám
źródło
2

PHP

Istnieje kilka zasad prawidłowych liczb rzymskich

  1. Napisz największą wartość przed niższymi wartościami

  2. Odejmuj tylko [I,X,C]przed następnymi 2 większymi wartościami

  3. Odejmij dwukrotnie [I,X,C]przed następnymi 2 większymi wartościami

  4. Odejmij dwukrotnie [I,X,C]przed większymi wartościami

  5. Połącz 4 + 5

Wersja online

Krok 1 Utwórz reguły

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

jest wyjściem JSON dla wszystkich prawidłowych liczb rzymskich

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

Krok 2 Zrób listę wszystkich reguł do 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;
}

Krok 3 Utwórz stałe

Połącz wszystkie listy i zdefiniuj stałe

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

Wydajność

W przykładzie wzajemnie dwie poprawne wersje liczby 8

echo IIX *  VIII;
Jörg Hülsermann
źródło
Fajnie, ale jak mam to wykorzystać? Nie znam się biegle w PHP; Czy możesz podać przykład, w jaki sposób pozwala mi to pisać cyfry rzymskie w kodzie?
daniero
@Daniero Teraz kod powinien działać
Jörg Hülsermann
Ach, to lepiej :)
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]
]


Przykład

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

Wydajność:

15

1015

Tak, to 2014 rok

1 + 10 = 11

2 + 10 = 12

3 + 10 = 13

4 + 10 = 14

5 + 10 = 15


Oświadczenie: Jestem pewien, że istnieją inne (i prawdopodobnie lepsze!) Sposoby, aby to zrobić również w Rebol.

PS. Moją roman-to-integerfunkcją jest transliteracja ładnego algorytmu Ruby od histocrata do konwersji łańcucha cyfr rzymskich na liczbę. Wrócił z podziękowaniami! +1

draegtun
źródło
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

Wpływa to na metatable tabeli globalnej, nadając jej nową funkcję indeksu. Kiedy jest wymagana zmienna globalna, która zawiera tylko cyfry rzymskie, np XVII. Analizuje ją.

Łatwy do przetestowania;

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

Wypróbuj online!

ATaco
źródło
1

VBA, 204 bajty

Zadeklarowany podprogram, który nie przyjmuje danych wejściowych, a po uruchomieniu tworzy publicdostępny Enum,R , który zawiera wszystkie wartości rzymska. Wartości tych można użyć bezpośrednio, bez odwoływania się do Enum.

Wartości zatrzymania wyliczenia wynoszą od 1 do 3999.

Uwaga: Zaciski "w liniach 3 i 7 zostały uwzględnione wyłącznie w celu wyróżnienia składni i nie mają wpływu na liczbę bajtów

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

Nieoznakowany i wyjaśniony

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
Taylor Scott
źródło