代码罗马化


33

挑战在于使任何罗马数字成为您选择的语言的有效代码。

他们应该不会出现串的内部或任何类似,但工作就像任何其他记号,文字如(阿拉伯语)数字,字符或字符串; 或变量/方法/功能标识符等

例如,在Java中,以下代码必须像i初始化为那样编译并运行42

int i = XLII;

数字的实际解析是次要的,因此您可以根据需要使用库,但这是一场人气竞赛,因此鼓励创造力。

如果存在这种情况,则不能使用任何实际使用罗马数字的语言。

祝好运。


1
那么,我们需要为该语言编写一种扩展,从而创建一种新语言吗?
肯德尔·弗雷

4
如果愿意,我会抱怨,因为我使用的语言无法像这样进行扩展,所以我什至不能参加。
Kendall Frey 2014年

3
@KendallFrey源必须编译并运行。对于Java,您可以编写一个“编译器”来编辑源代码,然后以编程方式进行编译。这种编译的一种方式是通过运行Process
Justin

1
大多数语言似乎无聊。例如,在python中,我只需要编写一个ast用于解析源代码的脚本即可。在AST的顶部插入罗马数字的定义(从1到3999)。编译整个程序并运行它。编写处理该过程的代码很无聊。
Bakuriu 2014年

2
@Bakuriu,您的评论也很无聊。这是一次人气竞赛,因此您应该尝试一些有趣的事情。我认为这里有一些不错的答案,比起编写脚本语言,这些答案更具想象力。
daniero 2014年

Answers:


40

C

罗马数字如此之多,因为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
绝对出色,建议以<roman.h>的形式将其添加到下一个C版本(C2X?)中!使用%r和%R printf格式!

3
我以为我知道如何使用预处理器,直到现在:| 您可以使用此枚举的最小用法示例来更新答案吗?
klingt.net 2014年

@YiminRong还有scanf:) @ klingt.net我不确定您要寻找哪种示例。一个相当简单的方法是int main() { return MMMCMXCIX - M - M - M - CM - XC - IX; }
HV14 2014年

我喜欢这个主意,但是为什么在定义的行为相当简单的情况下为什么选择不定义的行为呢?不判断,只是好奇?
CasaDeRobison

1
@CasaDeRobison主要是为了娱乐。我喜欢它,因为这是在预处理时出现未定义行为的极少数情况之一,当前编译器不会将其标记为错误,并且将来可能不会。我从来没有在本该有用的代码中写过类似的东西,但是在这里发布的代码并不是本来有用的,那么还有什么更好的机会尝试呢?
2014年

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 2014年

12

JavaScript(ES6)

使用Proxy捕捉罗马数字。

可在JSFiddle上的Firefox(最新)中测试
由于Proxy实施已中断,因此无法在Chrome(使用Traceur)中进行测试。

// 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(aka版本12)发出有关缺少参数的警告,但是mcpp(声称高度符合该标准的开源预处理器)或gcc 4.8.1(带有-std = iso9899:1990 -pedantic-errors开关)都不会发出有效参数列表为空的那些宏调用的警告或错误。

在审查了相关标准(ANSI / ISO 9899-1990,6.8.3,宏替换)之后,我认为有足够的歧义,不应将其视为非标准。“在调用类似函数的宏时,参数的数量应与宏定义中的参数的数量一致……”。只要有必要的括号(在多个参数的情况下为逗号)就可以了,它似乎并不排除空的参数列表。

至于尾随逗号问题,可以通过在枚举中添加一个额外的标识符(在我的情况下为MMMM来解决,即使它不遵守公认的罗马数字排序规则,对于所有遵循3999的标识符,MMMM看起来也很合理)究竟)。

稍微干净一点的解决方案将涉及将枚举和支持的宏移到单独的头文件中,如在其他地方的注释中所隐含的那样,并在使用宏名称后立即使用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__
2014年

ang,如果你不对!我想我一直都把它当作扩展。呃,好吧。:)
CasaDeRobison 2014年

8

普通口齿不清

以下是关于如何制作这样的宏的详细说明:

(roman-progn
  (+ XIV XXVIII))

在Common Lisp中调用宏时,它基本上就像一个函数,只是在对参数进行求值之前先将其接收。实际上,由于在Common Lisp代码中只是数据,所以我们收到的是一个(嵌套的)列表,该列表表示一个未解析的语法树,我们可以用它做任何想做的事,并且它是在编译时完成的。

辅助功能

该计划的第一步是拿起这棵树并对其进行扫描,以查找任何看起来像罗马数字的东西。这就是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))

7

a

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

后记没有,enum但是我们可以构造具有连续整数值的字典并将它们折叠成数组。这减少了按顺序生成所有字符串的问题,这是通过串联4个嵌套循环来完成的。因此,它将生成所有字符串,然后使用递增的计数器值对每个字符串进行交织,从而在堆栈中形成一长串的<string> <int>对,这些对被包装在<<>>产生 ...中以生成字典对象。

该程序将构建并安装一个字典,将罗马数字的所有名称映射到它们的对应值。因此,在源文本中提及名称会调用自动名称查找,并在堆栈上产生整数值。

II IV MC pstack

版画

2
4
600

4

Smalltalk(Smalltalk / X)(87/101个字符)

当然,我们可以轻松地修改解析器的标记器(因为它是类库的一部分,并且可以进行修改,并且始终存在),但是面临的挑战是仅影响给定上下文中的评估,以便其余系统照常工作。

版本1:

在评估名称空间中定义多个变量。因此,这将影响交互式doIt(又称评估):

(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


很高兴看到Smalltalk!

4

Haskell,在Template Haskellroman-numerals中使用元编程:

{-# 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 IXM MM MMM
  • {-拿这四个充满单词的盒子的笛卡尔积。现在,我们有了所有罗马数字的4D数组。
  • }.,;L:1-将所有内容运行到罗马数字的一个一维列表中,并删除前面的空字符串,因为这会产生错误。(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

*我需要在以后的基数3中读取数字2933774030998。碰巧我可以在基数79中使用不大于30的数字来表示它,这很好,因为J只能理解不超过35的数字(0-9,然后az)。这样可以节省小数点后3个字符。


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

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()
Ramchandra Apte 2014年

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←t得到

'IVXLCDM', 罗马字符后跟

 封闭的

 空清单

t[… 用… ] 索引t

 转置(以获得正确的顺序)

8⊥⍣¯1 的适当宽度的基八表示

 前n个索引,其中n

¯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的代码应工作
约尔格Hülsermann

嗯,这样更好:)
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


免责声明:我敢肯定,在Rebol中也有其他方法(可能更好!)。

PS。我的roman-to-integer功能是对histocrat的漂亮Ruby算法(用于将罗马数字字符串转换为数字)的音译。谢谢回来!+1


1

a

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字节

声明的子程序它没有任何输入,并运行时,创建publicLY访问EnumR包含所有的罗马数字的值。这些值可以直接使用,而无需引用Enum。

枚举值的保留范围是1到3999。

注意:"第3行和第7行的Terminals 仅用于语法突出显示,并且不影响字节数

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
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.