Романизация кода

33

Задача состоит в том, чтобы сделать любые римские цифры действительным кодом на выбранном вами языке.

Они не должны появляться внутри строк или чего-либо подобного, а работать так же, как любые другие токены, литералы, такие как ( арабские ) числа, символы или строки; или идентификаторы переменной / метода / функции и т. д.

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

int i = XLII;

Фактический анализ чисел является второстепенным, поэтому вы можете использовать библиотеку, если хотите, но это конкурс популярности, поэтому творческий подход поощряется.

Вы не можете использовать любой язык, который на самом деле использует римские цифры, если есть такая вещь.

Удачи.

daniero
источник
1
Итак, нам нужно написать расширение для языка, создавая тем самым новое?
Кендалл Фрей,
4
Я буду жаловаться, если захочу, потому что языки, которые я использую, не являются такими расширяемыми, поэтому я даже не могу участвовать.
Кендалл Фрей,
3
@KendallFrey Источник должен был бы скомпилировать и запустить. Для Java вы можете написать «компилятор», который редактирует исходный код, а затем программно компилирует . Одним из способов такой компиляции является запускProcess
Джастин
1
Кажется скучным на большинстве языков. Например, в Python я бы просто написал скрипт, который использует astдля разбора источника. Вставьте в верхней части AST определение римских цифр от 1 до 3999. Скомпилируйте все и запустите. Просто скучно писать код для управления процессом.
Бакуриу
2
@Bakuriu, и ваш комментарий тоже скучный. Это конкурс популярности, поэтому вы должны попытаться придумать что-нибудь интересное. Я думаю, что здесь есть несколько хороших ответов, которые более изобретательны (чем компиляция языка сценариев).
Даниеро

Ответы:

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как константы перечисления, плюс _(который может быть заменен на что угодно) как ноль.

HVD
источник
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. Я не думаю, что есть способ это исправить.

histocrat
источник
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. Конечная запятая в определении enum также относится к C99 и более поздним версиям.

Поскольку я хотел, чтобы мой код был как можно более универсальным для работы на старых платформах, я решил сделать еще один удар. Это длиннее, чем было раньше, но оно работает на компиляторах и препроцессорах, настроенных на режим совместимости C89 / C90. Всем макросам передается соответствующее количество аргументов в исходном коде, хотя иногда эти макросы «расширяются» в ничто.

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

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

Что касается запаздывающей запятой, это решается добавлением дополнительного идентификатора к перечислению (в моем случае MMMM, который кажется таким же разумным, как что-либо для идентификатора, чтобы следовать 3999, даже если он не подчиняется принятым правилам римской последовательности чисел в точку).

Немного более чистое решение будет включать перемещение перечисления и поддерживающих макросов в отдельный заголовочный файл, как это подразумевается в комментарии в другом месте, и использование 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 };
CasaDeRobison
источник
1
+1, но обратите внимание, что использование пустых аргументов макроса и запятая в конце списка перечислителя являются новыми функциями C99 и C ++ 11, и C99 и C ++ 11 поддерживают их __VA_ARGS__.
HVd
Черт, если ты не прав! Я думаю, я все это время видел это как расширение. Ах хорошо. :)
CasaDeRobison
8

Common Lisp

Ниже приводится довольно длинное объяснение того, как я создал макрос, который вы можете использовать следующим образом:

(roman-progn
  (+ XIV XXVIII))

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

Вспомогательные функции

Первый шаг плана - взять это дерево и отсканировать его на предмет чего-либо, похожего на римские цифры. Это Лисп и все, давайте попробуем сделать это несколько функционально: нам нужна функция, которая будет выполнять глубокий обход дерева и возвращать каждый объект, для которого предоставленная функция searchpвозвращает true. Это четная (полу) хвостовая рекурсия.

(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))
daniero
источник
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
})

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

МНИИП
источник
5

постскриптум

Я пытался следовать C, но я не понял этого. Итак, я сделал это так:

/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:

определить количество переменных в пространстве имен оценки. Так что это повлияет на интерактивные точки (ака 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:

Используйте хук methodWrapper, который позволяет переносить любой существующий код без перекомпиляции. Следующее оборачивает токенизатор парсера для поиска римского идентификатора для сканирования и делает его целым числом. Задача состоит в том, чтобы динамически определить, является ли вызывающий контекст римской империей или нет. Это делается с помощью сигнала запроса (который технически является допустимым исключением):

определить запрос:

InRomanScope := QuerySignal new defaultAnswer:false.

Таким образом, мы можем запросить в любое время («запрос InRomanScope») значение false по умолчанию.

Затем оберните метод checkIdentifier сканера:

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

blabla999
источник
Приятно видеть Smalltalk!
4

Haskell, используя метапрограммирование в Template 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 символов

Это только до 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- Запустите все это в один одномерный список римских цифр и удалите пустую строку в начале, потому что это приведет к ошибке. ( L:это редкое зрелище в J golf! Обычно не так много уровней бокса).
  • }.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, а затем аз). Это сохраняет 3 символа над десятичной.

algorithmshark
источник
3

питон

Идея проста, как и другие ответы. Но чтобы быть аккуратным и не загрязнять глобальное пространство имен, используется менеджер контекста. Это также накладывает ограничение на то, что вы должны заранее указать, какой объем римской цифры вы планируете использовать.

Примечание. Чтобы упростить задачу и не изобретать велосипед, я использовал пакет Roman Python

Реализация

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
Abhijit
источник
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[... ] индекс т с ...

 транспонированный (чтобы получить правильный порядок)

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 Теперь код должен работать
Jörg Hülsermann
Ах, это лучше :)
Даниеро
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


Отказ от ответственности: я уверен, что есть и другие (и, возможно, лучше!) Способы сделать это и в Rebol.

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

draegtun
источник
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

Это влияет на метатабельность глобальной таблицы, давая ей новую функцию индекса. Когда запрашивается глобальная переменная, которая содержит только римские цифры, напримерXVII , она анализирует ее.

Легко проверить;

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

Попробуйте онлайн!

Ataco
источник
1

VBA, 204 байта

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

Enum содержит значения от 1 до 3999.

Примечание. Терминалы "s в строках 3 и 7 включены только для подсветки синтаксиса и не вносят вклад в bytecount.

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
Тейлор Скотт
источник