Кодова романізація


33

Завдання полягає в тому, щоб зробити будь-які римські цифри дійсним кодом на обраній вами мові.

Вони не повинні з’являтися всередині рядків або чогось подібного, але працюватимуть так само, як і будь-які інші лексеми, літерали, такі як ( арабські ) числа, символи чи рядки; або ідентифікатори змінної / методу / функції тощо.

Наприклад, у Java, наступне потрібно буде компілювати та запускати так само, як ніби iбуло ініціалізовано 42:

int i = XLII;

Фактичний розбір цифр є другорядним, тому ви можете використовувати бібліотеку, якщо хочете, але це конкурс популярності, тому творчість заохочується.

Ви не можете використовувати будь-яку мову, яка фактично використовує римські цифри, якщо є така річ.

Удачі.


1
Отже, нам потрібно написати розширення до мови, створивши тим самим нову?
Кендалл Фрей

4
Я поскаржусь, якщо хочу, оскільки мови, якими я користуюся, не є розширюваними подібними, тому я навіть не можу брати участь.
Кендалл Фрей

3
@KendallFrey Джерело повинно було скласти та запустити. Для Java ви можете написати "компілятор", який редагує джерело, а потім програмує компіляцію . Один із способів такого складання - через запускProcess
Джастін

1
Здається, на більшості мов нудно. Наприклад, у python я б просто написав сценарій, який використовує astдля розбору джерела. Вставте вгорі AST означення римських цифр від 1 до 3999. Складіть всю справу і запустіть її. Це просто нудно написати код, щоб обробити процес.
Бакуріу

2
@Bakuriu і ваш коментар теж нудний. Це конкурс популярності, тому вам слід спробувати придумати щось цікаве. Я думаю, що тут є кілька приємних відповідей, які є більш образними (ніж складання мови сценаріїв).
daniero

Відповіді:


40

С

Римських цифр існує лише стільки, оскільки 4000 і вище не мають стандартних позначень, а препроцесор - чудовий інструмент декомпресії, особливо якщо у вас немає проблем з тим, що код має невизначене поведінку.

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

Це визначає всі римські цифри від Iдо MMMCMXCIXяк константи перерахування, плюс _(які можна замінити чим завгодно) як нуль.


12
Абсолютно геніально, запропонуйте додати його до наступного випуску C (C2X?) Як <roman.h>! З форматами% r та% R printf!

3
Я думав, що до цих пір знаю, як використовувати препроцесор: | Чи можете ви оновити свою відповідь прикладом мінімального використання цього переліку?
klingt.net

@YiminRong І scanfтеж :) @ klingt.net Я не впевнений, який приклад ти шукаєш. Досить простим було бint main() { return MMMCMXCIX - M - M - M - CM - XC - IX; }
hvd

Мені подобається ідея, але чому ви вирішили піти з невизначеною поведінкою, коли визначена поведінка була досить простою? Не судячи, просто цікаво?
CasaDeRobison

1
@CasaDeRobison В основному для розваги. Мені це подобається, оскільки це один з небагатьох випадків невизначеної поведінки під час попередньої обробки, який не позначається помилкою поточних компіляторів і, швидше за все, не буде в майбутньому. Я ніколи не пишу нічого подібного в код, який мав би бути корисним, але розміщений тут код не означає бути корисним, тож який кращий привід спробувати це?
hvd

15

Рубін

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

Будь-які (великі) римські цифри тепер будуть проаналізовані, як їх десяткові еквіваленти. Єдине питання полягає в тому, що вони все ще призначаються: ви можете робити X = 9, але ні 10 = 9. Я не думаю, що існує спосіб виправити це.


1
Це прекрасно. Справа про призначення не є проблемою; Ви можете використовувати завдання, щоб робити всілякі дурні речі.
daniero

12

JavaScript (ES6)

Використовуйте Proxyдля лову римських цифр.

Тестується в Firefox (останнє) на JSFiddle .
Не перевіряється в Chrome (з Traceur), оскільки Proxyреалізація порушена.

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

Використання:

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

10

C & C ++ (оновлений відповідь)

Як помічено в коментарі, у мого оригінального рішення було дві проблеми:

  1. Необов’язкові параметри доступні лише в C99 та пізніших стандартах мовної сім'ї.
  2. Кінцева кома у визначенні перерахунків характерна також для C99 та пізніших версій.

Оскільки я хотів, щоб мій код був максимально загальним для роботи на старих платформах, я вирішив зробити інший замах на нього. Це довше, ніж було раніше, але він працює над компіляторами та препроцесорами, встановленими в режимі сумісності C89 / C90. Усім макросам передається відповідна кількість аргументів у вихідному коді, хоча іноді ці макроси "розширюються" у ніщо.

Visual C ++ 2013 (він же версія 12) не випромінює попередження про відсутні параметри, але не видає ні mcpp (препроцесор з відкритим кодом, який вимагає високої відповідності стандарту), ні gcc 4.8.1 (з -std = iso9899: 1990 -перемикачами-помилки) попередження або помилки для тих макро-викликів із фактично порожнім списком аргументів.

Переглянувши відповідний стандарт (ANSI / ISO 9899-1990, 6.8.3, Заміна макросів), я думаю, що існує достатня двозначність, що це не слід вважати нестандартним. "Кількість аргументів у виклику функціонального макросу повинна відповідати кількості параметрів у визначенні макросу ...". Схоже, це не виключає порожній список аргументів, поки потрібні дужки (і коми у випадку кількох параметрів) є для виклику макросу

Що стосується проблеми з комою в кінці, то це вирішується шляхом додавання до перерахунку додаткового ідентифікатора (в моєму випадку MMMM, який видається таким же розумним, як і все, для того, щоб ідентифікатор наслідував 3999, навіть якщо він не підкоряється прийнятим правилам послідовності римських цифр точно).

Трохи більш чисте рішення передбачає переміщення enum та підтримку макросів до окремого файлу заголовка, як це було передбачено в коментарі в іншому місці, та використання undef імен макросів відразу після їх використання, щоб уникнути забруднення простору імен. Безперечно, слід також вибирати кращі назви макросів, але це адекватно для задачі, що знаходиться в розпорядженні.

Моє оновлене рішення з подальшим оригінальним рішенням:

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

Оригінальна відповідь (яка отримала перші шість змін, тому якщо ніхто ніколи не підтверджує це знову, не варто думати, що моє оновлене рішення отримало зміни):

У тому ж дусі, що і в попередній відповіді, але зроблено таким чином, що він повинен бути переносним, використовуючи лише певну поведінку (хоча різні середовища не завжди узгоджують деякі аспекти препроцесора). Трактує деякі параметри як необов'язкові, ігнорує інші, він повинен працювати над препроцесорами, які не підтримують __VA_ARGS__макрос, включаючи C ++; він використовує непрямі макроси для забезпечення розширення параметрів перед вставкою токенів, і нарешті він коротший, і я думаю, що його легше читати ( хоча це все ще хитро і, мабуть, не просто читати, просто простіше):

#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, але зауважте, що використання порожніх макроаргументів і кома в кінці списку нумераторів - це нові функції C99 і C ++ 11, і C99 і C ++ 11 підтримують __VA_ARGS__.
hvd

Данг, якщо ти не правий! Я думаю, я весь цей час бачив це як розширення. Ну добре. :)
CasaDeRobison

8

Звичайний Лисп

Далі йде досить тривале пояснення того, як я створив макрос, який ви можете використовувати так:

(roman-progn
  (+ XIV XXVIII))

Коли макрос викликається в Common Lisp, він в основному діє як функція, лише те, що аргументи отримуються ще до їх оцінки. Насправді, оскільки звичайний код Lisp - це лише дані, то ми отримуємо (вкладений) список, що представляє собою нерозбірливе дерево синтаксису, з яким ми можемо робити все, що завгодно, і це робиться за час компіляції.

Функції помічників

Перший крок плану - взяти це дерево і відсканувати його на те, що схоже на римські цифри. Це Ліпп і все, давайте спробуємо зробити це дещо функціонально: нам потрібна функція, яка зробить глибокий обхід дерева і поверне кожен об'єкт, для якого надана функція searchpповертає істину. Цей рівномірний (напів) хвостовий-рекурсивний.

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

Тоді деякий код для розбору римських цифр, люб’язно наданий 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)))

Фактичний макрос

Ми беремо синтаксичне дерево ( body), шукаємо його за допомогою нашої процедури глибокого пошуку і якось робимо доступними римські цифри, які ми знаходимо.

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

Отже, що таке 1 + 2 + 3 + (4 * (5 + 6)) + 7?

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

А щоб побачити, що насправді сталося, коли викликався макрос:

(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

Луа

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

Просто резервний __index для глобальної таблиці. Фактична конверсія за допомогою gsub вийшла набагато красивішою, ніж я уявляв.


5

Постскрипт

Я намагався слідувати за С, але не розумів цього. Тому я зробив це так:

/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 не має, enumале ми можемо побудувати словник із послідовними цілими значеннями та скласти їх у масив. Це зменшує проблему з генерацією всіх рядків послідовно, що робиться об'єднанням у 4 вкладені петлі. Таким чином, він генерує всі рядки, потім перемежовує кожну рядок зі збільшенням лічильного значення, в результаті чого на стеку виходить довга серія пар <string> <int>, які загорнуті у <<... >>для створення об’єкта словника.

Програма будує та встановлює словник, який відображає всі імена римських цифр до відповідного їм значення. Тому згадування імен у вихідному тексті викликає автоматичний пошук імен та дає ціле значення на стеку.

II IV MC pstack

відбитки

2
4
600

4

Smalltalk (Smalltalk / X) (87/101 символів)

звичайно, ми могли б легко змінити токенізатор парсера (оскільки він є частиною бібліотеки класів і як такий відкритий для модифікації і завжди присутній), але проблема полягає в тому, щоб впливати лише на оцінки в даному контексті, так що решта система працює як завжди.

Версія 1:

визначити ряд змінних у просторі імен оцінювання. Таким чином, це вплине на інтерактивні doIts (інакше evals):

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

тоді я можу зробити (у doIt, але не у складеному коді):

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

-> 2019 рік

Зверніть увагу: 101 знак включає пробіл; насправді це можна зробити за допомогою 87 символів.
Також зауважте, коли визначають у глобальному просторі імен Smalltalk, я бачу ці константи також у складеному коді.

Версія 2:

Використовуйте метод гачок методу, який дозволяє обмотати будь-який існуючий код без перекомпіляції. Далі обгортається токенізатор Парсера, щоб шукати римський ідентифікатор, який потрібно сканувати, і робить його цілим числом. Складна частина полягає в тому, щоб динамічно визначати, чи викликає контекст походження від римської імперії чи ні. Це робиться за допомогою сигналу запиту (який технічно є винятковим винятком):

визначте запит:

InRomanScope := QuerySignal new defaultAnswer:false.

Тож ми можемо в будь-який час попросити ("Запит InRomanScope") отримати помилкове значення за замовчуванням.

Потім оберніть метод перевірки ідентифікатора сканера:

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

Тепер сканер працює як завжди, якщо ми не знаходимося в римській імперії:

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

-> 2525

ми можемо навіть скласти код:

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

хороша спроба; але це не вдається із синтаксичною помилкою (саме цього ми хочемо). Однак у Римській імперії ми МОЖЕМ складати:

InRomanScope answer:true do:[

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

і тепер ми можемо запитати будь-яке ціле число (надсилаючи це повідомлення) зсередини та за межами Риму:

(1000 factorial) inTheYear2525

-> 2525


Приємно бачити Smalltalk!

4

Haskell, використовуючи метапрограмування в шаблоні Haskell і римськими цифрами :

{-# 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 резервує ідентифікатори, починаючи з великих літер для конструкторів, тому я використовував малі регістри.


4

J - 78 char

Це стосується лише MMMCMXCIX = 3999, як і для інших рішень.

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

Розбиття його (нагадуємо, J зазвичай читається справа наліво, якщо це не замінено в дужках):

  • M`CDM`XLC`IVX- Чотири коробки листів. Ми будемо використовувати числові масиви для індексації цих букв і будувати підслови римських цифр.
  • 841,3#79bc5yuukh - Це числові дані, щільно закодовані. *
  • (_1,~3#.inv]) - Це розшифрує вищезазначені дані, розширивши у потрійному та додавши -1.
  • ('';&;:(...){' ',[)&.>- Спарювання чисел ліворуч із полями праворуч ( &.>), декодуйте масиви чисел та використовуйте їх для вказівки на літери. Ми вважаємо 0 як пробілом, попередньо додаючи пробіл до літерних списків. Ця процедура будує списки слів як I II III IV V VI VII VIII IXі M MM MMM.
  • {- Візьміть декартовий продукт цих чотирьох коробок, наповнених словами. Тепер у нас є 4D масив усіх римських цифр.
  • }.,;L:1- Запустіть усе це в єдиний 1D список римських цифр і видаліть порожній рядок на передній частині, оскільки це призведе до помилки. ( L:це рідкісне видовище в J-гольфі! Зазвичай боксу не так багато).
  • }.i.4e3- Цілі числа від 0 до 4000, виключаючи кінцеві точки.
  • Нарешті, ми склали все разом із глобальним завданням =:. J дозволяє мати список імен у LHS, як форму обчислюваного множинного призначення, тому це прекрасно працює.

Тепер простір імен J переповнений змінними, що представляють римські цифри.

   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

* Мені потрібно число 2933774030998, щоб згодом його прочитати в базі 3. Так трапляється, що я можу виразити це в базі 79, використовуючи цифри не більше 30, що добре, тому що J може розуміти цифри до 35 (0-9 і потім az). Це зберігає 3 символи над десятковими.


3

Пітон

Ідея проста, як інші відповіді. Але просто щоб бути акуратним і не забруднювати глобальний простір імен, використовується контекстний менеджер. Це також накладає обмеження, яке потрібно оголосити заздалегідь, обсяг римських числових цифр, які ви плануєте використовувати.

Примітка Для того, щоб зробити це просто і не винаходити колесо, я використав пакет римського пітона

Впровадження

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)

Демо

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:

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
Краще використовувати, globals()[var] = valueніж exec().
Рамчандра Апте

3

D

використовуючи оцінку функції часу компіляції 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 байт

Підкаже максимальна довжина римської цифри та визначає всі змінні.

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

t←т отримує

'IVXLCDM', Римські чари, за якими

 укладений

 порожній список

t[] Індекс t з…

 перевезений (щоб отримати правильне замовлення)

8⊥⍣¯1 відповідна ширина основи вісім зображень

 перші п індексів, де п є

¯1+ один менше, ніж

8*⎕ вісім до сили числового вводу

,/ зрівняти рядки (кожне подання)

{ Застосувати наступну анонімну функцію до кожного представлення…

()[t⍳⍵] Відповідні позиції елементів аргументу в t , виберіть із…

   зарахований

  1 5∘ר один і п’ять разів кожен

  10* десять до влади

  ⍳4 нуль через три

0,⍨ додати нуль

2(…)/ на кожне розсувне вікно довжиною-два застосуйте наступний анонімний потяг функцій…

  ⊣× ліві рази аргументу

  ¯1* негативний на владу

  < чи лівий аргумент менший, ніж правий аргумент

+/ сума

⍵'←', додайте аргумент (римська цифра) та стрілку призначення

 формат (для вирівнювання та перетворення числа в текст)

 виконати це (робить призначення поза анонімною функцією)

Спробуйте в Інтернеті! (з використанням максимальної довжини 5)


2

PHP

Існує кілька правил для дійсних римських чисел

  1. Напишіть найбільше значення для нижчих значень

  2. Віднімайте лише [I,X,C]перед наступними 2 більшими значеннями

  3. Віднімаємо подвійне [I,X,C]перед наступними 2 більшими значеннями

  4. Віднімаємо вдвічі [I,X,C]більше великих значень

  5. Поєднайте 4 + 5

Інтернет-версія

Крок 1 Створіть правила

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

- вихід JSON для всіх дійсних римських чисел

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

Крок 2 Складіть списки для всіх правил до 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;
}

Крок 3 Створіть константи

Поєднайте всі списки та визначте константи

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

Вихідні дані

У прикладі безглуздо дві дійсні версії числа 8

echo IIX *  VIII;

Добре, але як я цим користуюся? Я не вільно володію PHP; Скажіть, будь ласка, приклад того, як це дозволяє мені записати римські цифри в код?
daniero

@Daniero Тепер код повинен працювати
Йорг Хюльсерманн

Ах, так краще :)
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]
]


Приклад

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

Вихід:

15

1015 рік

Так, це 2014 рік

1 + 10 = 11

2 + 10 = 12

3 + 10 = 13

4 + 10 = 14

5 + 10 = 15


Відмова: Я впевнений, що в Реболі є й інші (і, мабуть, кращі!) Способи зробити це.

PS. Моя roman-to-integerфункція транслітерація histocrat хорошого «s алгоритм Рубінового для перетворення римської цифри рядка в число. Повернувся з подякою! +1


1

Луа

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

Це впливає на метатабель глобальної таблиці, надаючи їй нову функцію індексу. Коли запитується глобальна змінна, яка містить лише римські цифри, наприклад XVII, вона аналізує її.

Легкий для тестування;

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

Спробуйте в Інтернеті!


1

VBA, 204 байти

Заявлений підпрограму , яка не приймає ніякого введення, і при запуску, створює publicний доступ Enum, R, який містить всі значення римське. Ці значення можуть використовуватися безпосередньо, не посилаючись на Enum.

Значення утримування Enum від 1 до 3999.

Примітка: Термінал "s у рядках 3 та 7 включений лише для підсвічування синтаксису та не вносить внесок у рахунок рахунку

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

Необов’язаний і пояснений

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
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.