ローマ数字計算機を作成する


18

ローマ数字の基本的な計算機を作成します。

必要条件

  • サポート+-*/
  • 入力と出力は、シンボルごとに1つの減算器プレフィックスのみを想定する必要があります(つまり、前にIIV2つあるため、3はできません)IV
  • 10の唯一のパワーは大きな数字から減算されている最小支持現代の標準的な規則、で入力と出力する必要があります。減算原則の取り扱い(例えばIXC必要な減算器はありませんですVLD)と減算を超える数から実行されることはありません減算器の10倍(IXサポートする必要がありますがIC必須ではありません)。
  • 入力と出力は、値が大きい順に開始します(つまり、19 = XIXnot IXX、10は9より大きい)。
  • 左から右、手の計算機を使用しているかのように、演算子の優先順位はありません。
  • 1〜4999の間の正の整数の入力/出力全体をサポート(V̅は不要)
  • ローマ数字の変換を行うライブラリはありません

あなたが決めるために

  • 大文字と小文字の区別
  • 入力にスペースを入れるかスペースを入れない
  • 10進数の出力を取得するとどうなりますか。切り捨て、無回答、エラーなど。
  • 処理できない出力に対して何をすべきか。印刷する負の数または大きい数。
  • 最小要件よりも減算の原則のより自由な使用をサポートするかどうか。

追加クレジット

  • -50-最大99999以上を処理します。シンボルにはビンキュラムを含める必要があります

サンプル入出力

XIX + LXXX                 (19+80)
XCIX

XCIX + I / L * D + IV      (99+1/50*500+4)
MIV

最短のコードが優先されます。


(99 + 1/50 * 500 + 4)=(99 + 10 + 4)= 113ですが、サンプルの入力/出力ではMIV(1004)と表示されています。
ビクターStafusa 14

1
@Victor -右操作に厳密左-ない優先順位規則- ((((99 + 1)/ 50)* 500)+ 4)のように計算されるべきである4 + 99 + 1/50 * 500に

処理番号はIM = 999必須ですか?
ケンドールフレイ14

@KendallFreyあなたが入力できると思いますIM。出力があるかどうIMCMXCIX999のためには、あなた次第です。どちらも要件に適合しています。
ダニー14

2
IMは、現代のローマ数字の使用法では標準ではありません。通常、減算によって行われるのは、各桁(4、9、40、90、400、900など)の4と9のみです。1999年の場合、MCMXCIXはMIMではなく標準的なものになります...その年の映画のクレジットをご覧ください。それ以外の場合、どこで終了しますか?45のVLのような他の非標準の減算もサポートする予定ですか?Cを超えるビンキュラムを持つICは、ボーナスとして99999としてサポートする必要がありますか?
ジョナサンヴァンマトレ14

回答:


9

JavaScript(ES6)、238

c=s=>{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}
n=eval('W='+s.replace(/[\w]+/g,n=>(o=0,n.replace(/[MDLV]|C[MD]?|X[CL]?|I[XV]?/g,d=>o+=X[d]),
o+';W=W')));o='';for(i in X)while(n>=X[i])o+=i,n-=X[i];return o}

使用法:

c("XIX + LXXX")
> "XCIX"
c('XCIX + I / L * D + IV')
> "MIV"

注釈付きバージョン:

/**
 * Process basic calculation for roman numerals.
 * 
 * @param {String} s The calculation to perform
 * @return {String} The result in roman numerals
 */
c = s => {
  // Create a lookup table.
  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
  };
  // Do the calculation.
  // 
  // The evaluated string is instrumented to as below:
  //   99+1/50*500+4 -> W=99;W=W+1;W=W/50;W=W*500;W=W+4;W=W
  //                 -> 1004
  n = eval('W=' + s.replace(
    // Match all roman numerals.
    /[\w]+/g,
    // Convert the roman number into an integer.
    n => (
      o = 0,
      n.replace(
        /[MDLV]|C[MD]?|X[CL]?|I[XV]?/g,
        d => o += X[d]
      ),
      // Instrument number to operate left-side operations.
      o + ';W=W'
    )
  ));

  // Convert the result into roman numerals.
  o = '';
  for (i in X)
    while (n >= X[i])
      o += i,
      n -= X[i];

  // Return calculation result.
  return o
}

9

T-SQL、1974-50 = 1924バイト

SQLでのゴルフは、砂のくさびだけで18ホールをプレーするのと同じことを知っていますが、このチャレンジに満足しました。

これは、入力と出力の両方でビンキュラムをサポートします。末尾のチルダを使用して表記する規則を採用したため、V〜は5000、X〜は10000などです。また、標準の現代ローマ数字の使用法に従って最大399,999までの出力を処理する必要があります。その後、INTがサポートする範囲内のあらゆるものの部分的に非標準のローマ符号化を行います。

すべて整数演算であるため、非整数の結果は暗黙的に丸められます。

DECLARE @i VARCHAR(MAX)
SET @i='I+V*IV+IX*MXLVII+X~C~DCCVI'
SELECT @i

DECLARE @t TABLE(i INT IDENTITY,n VARCHAR(4),v INT)
DECLARE @u TABLE(n VARCHAR(50),v INT)
DECLARE @o TABLE(n INT IDENTITY,v CHAR(1))
DECLARE @r TABLE(n INT IDENTITY,v INT,r VARCHAR(MAX))
DECLARE @s TABLE(v INT,s VARCHAR(MAX))
DECLARE @p INT,@x VARCHAR(4000)='SELECT ',@j INT=1,@m INT,@y INT,@z VARCHAR(2),@q VARCHAR(50)='+-/*~]%'
INSERT @t(n,v) VALUES('i',1),('iv',4),('v',5),('ix',9),('x',10),('xl',50),('l',50),('xc',90),('c',100),('cd',400),('d',500),('cm',900),('m',1000),('mv~',4000),('v~',5000),('mx~',9000),('x~',10000),('x~l~',40000),('l~',50000),('x~c~',90000),('c~',100000)
INSERT @u VALUES('%i[^i'+@q,-2),('%v[^vi'+@q,-10),('%x[^xvi'+@q,-20),('%l[^lxvi'+@q,-100),('%c[^clxvi'+@q,-200),('%d[^dclxvi'+@q,-1000),('%mx~%',-2010),('%x~l~%',-20060),('%x~c~%',-20110)
WHILE PATINDEX('%[+-/*]%', @i)!=0
BEGIN
    SET @p=PATINDEX('%[+-/*]%', @i)
    INSERT @o(v) SELECT SUBSTRING(@i,@p,1)
    INSERT @r(r) SELECT SUBSTRING(@i,1,@p-1)
    SET @i=STUFF(@i,1,@p,'')
END 
INSERT @r(r) SELECT @i
UPDATE r SET v=COALESCE(q.v,0) FROM @r r LEFT JOIN (SELECT r.r,SUM(u.v)v FROM @u u JOIN @r r ON r.r LIKE u.n GROUP BY r.r)q ON q.r=r.r
UPDATE r SET v=r.v+q.v FROM @r r JOIN (SELECT r.n,r.r,SUM((LEN(r.r)-LEN(REPLACE(r.r,t.n,REPLICATE(' ',LEN(t.n)-1))))*t.v) v FROM @r r JOIN @t t ON CHARINDEX(t.n,r.r) != 0 AND (LEN(t.n)=1 OR (LEN(t.n)=2 AND RIGHT(t.n,1)='~')) GROUP BY r.n,r.r) q ON q.r=r.r AND q.n = r.n
SELECT @m=MAX(n) FROM @o
SELECT @x=@x+REPLICATE('(',@m)+CAST(v AS VARCHAR) FROM @r WHERE n=1
WHILE @j<=@m
BEGIN
    SELECT @x=@x+o.v+CAST(r.v AS VARCHAR)+')'
    FROM @o o JOIN @r r ON r.n=o.n+1 WHERE o.n=@j
    SET @j=@j+1
END 
INSERT @s(v,s) EXEC(@x+',''''')
UPDATE @s SET s=s+CAST(v AS VARCHAR(MAX))+' = '
SET @j=21
WHILE @j>0
BEGIN
    SELECT @y=v,@z=n FROM @t WHERE i = @j
    WHILE @y<=(SELECT v FROM @s)
    BEGIN
        UPDATE @s SET v=v-@y,s=s+@z
    END  
    SET @j=@j-1
END
SELECT @x+' = '+UPPER(s) FROM @s

バイトカウントを減らし、慣用的なSQLのよりエレガントな例になる可能性のあるWHILEループの一部を置き換えるためのセットベースのソリューションをいじっています。また、テーブルエイリアスの使用を最小限に抑えることで得られるバイトもいくつかあります。しかし、この言語では基本的に勝てないので、私はほとんどここにいるだけで、ドン・キホーテの衣装を誇示します。:)

上部のSELECT @iは入力を繰り返します。

I+V*IV+IX*MXLVII+X~C~DCCVI

そして最後のSELECTは以下を返します:

SELECT (((((1+5)*4)+9)*1047)+90706) = 125257 = C~X~X~V~CCLVII

そして、あなたはこのSQLFiddleで自分でテストできます

そして、私はそれがどのように機能するかについてのコメントを追加するために戻ってきます。なぜなら、あなたが教育的価値のためにそれを悪用するつもりがないのに、明らかに失った答えを投稿するからです。


2

Javascript- 482 476文字

String.prototype.m=String.prototype.replace;eval("function r(a){return a>999?'Mk1e3j899?'CMk900j499?'Dk500j399?'CDk400j99?'Ck100j89?'XCk90j49?'Lk50j39?'XLk40j9?'Xk10j8?'IX':a>4?'Vk5j3?'IV':a>0?'Ik1):''}".m(/k/g,"'+r(a-").m(/j/g,"):a>"));s=prompt();h=s.match(/\w+/gi);for(k in h)s=s.m(h[k],eval(eval("'0'+h[k].m(/IVu4pIXu9pXLu40pXCu90pCDu400pCMu900pMu1000pDu500pCu100pLu50pXu10pVu5pIu1')".m(/u/g,"/g,'+").m(/p/g,"').m(/")))+")");for(k in h)s="("+s;alert(r(Math.floor(eval(s))))

サンプルの入出力は機能します。

XIX + LXXX -> XCIX
XCIX + I / L * D + IV -> MIV

それも大きな数をうまく処理しません:

MMM+MMM -> MMMMMM
M*C -> MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM

また、スペースも受け入れますが、必要ではありません。

しかし、私はゴルフをしていたので、いくつかの問題があります。

  • 入力の形式が正しいかどうかは検証されません。入力が整形式でない場合、動作は未定義です(実際には非常に奇妙で奇妙です)。
  • 出力の小数を切り捨てます(ただし、小数を使用して中間計算を実行できます)。
  • それは実際に評価関数を乱用します。
  • 負の数を処理しようとしません。
  • 大文字と小文字が区別されます。

この代替バージョンは、5000から99999までの数字を処理しますが、600 598 584文字です。

String.prototype.m=String.prototype.replace;eval("function r(a){return a>8zz?'XqCqk9e4j4zz?'Lqk5e4j3zz?'XqLqk4e4jzz?'Xqk1e4j89z?'IqXqk9e3j49z?'Vqk5e3j9z?'Mk1e3j8z?'CMk900j4z?'Dk500j3z?'CDk400jz?'Ck100j89?'XCk90j49?'Lk50j39?'XLk40j9?'Xk10j8?'IX':a>4?'Vk5j3?'IV':a>0?'Ik1):''}".m(/k/g,"'+r(a-").m(/j/g,"):a>").m(/q/g,"\u0305").m(/z/g,"99"));s=prompt();h=s.match(/\w+/gi);for(k in h)s=s.m(h[k],eval(eval("'0'+h[k].m(/IVu4pIXu9pXLu40pXCu90pCDu400pCMu900pMu1000pDu500pCu100pLu50pXu10pVu5pIu1')".m(/u/g,"/g,'+").m(/p/g,"').m(/")))+")");for(k in h)s="("+s;console.log(r(Math.floor(eval(s))))

-20が当てはまるとは思わない:vinculum
SeanC 14

ここで@SeanCheshireに同意します。より大きな数字を扱う場合、数字の上にビンキュラムを追加して、通常の値の1000倍にすることを意図しています。たぶん、-20よりも大きくなければならないので、試してみる価値はあります。
ダニー14

1
@Dannyビンキュラスを処理するバージョンを追加しましたが、コードが116文字増加します。
ビクターStafusa 14

2

Javascriptの 479 361 348 278 253

303文字-最大100万の数字をサポートする場合は50、vinculumのサポート付き:

function p(s){s=s[r](/(^|[*\/+-])/g,"0;s$1=");for(i in v){f=R("\\b"+i);while(f.test(s))s=s[r](f,v[i]+"+")}eval(s+"0");h="";for(i in v)while(s>=v[i]){h+=i;s-=v[i]}return h}v={M̅:1e6,D̅:5e5,C̅:1e5,L̅:5e4,X̅:1e4,V̅:5e3,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};r="replace";R=RegExp

使用法:p(text)、例えば、p('XIX + LXXX')リターンXCIX

説明コメント付きのコード:

// Array mapping characters to values
v={M¯:1e6,D¯:5e5,C¯:1e5,L¯:5e4,X¯:1e4,V¯:5e3,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};
// Shortcut for String.replace
r='replace';
R=RegExp;

// The heart of the program
function p(s) {
    // Replace operators with ";s+=", ";s-=", and so on
    s=s[r](/(^|[*\/+-])/g,'0;s$1=');
    // Loop over the character map and replace all letters with numbers
    for(i in v){
        f=R('\\b'+i);
        while(f.test(s))
            s=s[r](f, v[i]+'+')
    }
    eval(s+'0');
    // Set up our return string
    h='';
    // Replace digits with characters
    for(i in v)
        while(s>=v[i]) {
            h+=i;
            s-=v[i];
        }
    return h;
}

これは、指定されたサンプルと、私が試した他のすべてのサンプルで機能します。例:

XIX + LXXX = XCIX
XCIX + I / L * D + IV = MIV
XL + IX/VII + II * XIX = CLXXI
CD + C + XL + X + I = DLI
M̅ + I = M̅I
MMMM + M = V̅

2

Ruby 2.1、353(および他の多くの反復)、295-50 = 245

vinculum処理では、23文字まで追加されます。

これは、入力の「IL」または「VM」を処理し、負数(高整数になる)、小数(切り捨て)、または任意のスペースでエラーなしで失敗します。負の最初の数値も処理するようになりました(ただし、合計が負の場合でも失敗します)。*または/で始まる場合、または結果が400万以上の場合も失敗します。

「手動計算機」機能にObject#sendを使用します。

m=%w{I V X L C D M V̅ X̅ L̅ C̅ D̅ M̅};n=m.zip((0..12).map{|v|(v%2*4+1)*10**(v/2)}).to_h
d=0
gets.scan(/([-+*\/])?([A-Z̅]+)/){|o,l|f=t=0
l.scan(/.̅?/){t-=2*f if f<v=n[$&]
t+=f=v}
d=d.send o||:+,t}
7.downto(1){|v|z=10**v
y=(d%z)*10/z
q,w,e=m[v*2-2,3]
$><<(y>8?q+e : y<4?q*y : y<5?q+w : w+q*(y-5))}

ゴルフをしていない:

m=%w{I V X L C D M V̅ X̅ L̅ C̅ D̅ M̅} # roman numerals
n=m.zip((0..12).map{|v|(v%2*4+1)*10**(v/2)}).to_h # map symbols to values
d=0
gets. # get input and...
  scan(/([-+*\/])?([A-Z̅]+)/) { |l,o|  # for each optional operator plus number
    f=t=0
    l.scan(/.̅?/){                           # read the number in one letter at a time
      t -= 2 * f if f < (v=n[$&])           # if the number's greater than the prev, subtract the prev twice since you already added it
      t += (f = v)                          # add this, and set prev to this number
    }
    d = d.send((o || :+), t)                # now that we've built our number, "o" it to the running total (default to +)
}
7.upto(1) { |v|                        # We now print the output string from left to right
  z = 10**v                            # z = [10, 100, 1000, etc.]
  y = (d%z)*10/z                       # if d is 167 and z is 100, y = 67/10 = 6 
  q,w,e = m[v*2-2,3]                   # q,w,e = X, L, C
  $><< (                               # print: 
    y>8 ? q+e :                        # if y==9,    XC
      y<4 ? q*y :                      # if y<4,     X*y
        y>3 ? q+w :                    # if y==4,    XL
          q*(y-5)                      # else,       L + X*(y-5)
  )
}

2

パイソン2 - 427 418 404 401 396 395 392文字

標準入力から読み取ります。大文字のみを処理し(大文字と小文字を区別せず、8文字余分に追加できます)、スペースが必要です。検証を行いません。さまざまなケースでどのように破損するかをテストしていません。ただし、VC = 95などの数値を処理します。

N=['?M','DC','LX','VI'];t=0;o='+'
for q in raw_input().split():
 if q in"+-*/":o=q;continue
 n=s=0;X=1
 for l in q:
  x=''.join(N).find(l);v=(5-x%2*4)*10**(3-x/2)
  if X<x:n+=s;s=v;X=x
  elif X>x:n+=v-s;s=0
  else:n+=v+s;s=0
 exec"t"+o+"=n+s"
r=t/1000*'M'
for p,d in enumerate("%04d"%(t%1e3)):
 i="49".find(d);g=N[p]
 if i<0:
  if'4'<d:r+=g[0]
  r+=int(d)%5*g[1]
 else:r+=g[1]+N[p-i][i]
print r

そして、無料版:

# Numerals grouped by powers of 10
N = ['?M','DC','LX','VI']
# Start with zero plus whatever the first number is
t = 0
o = '+'
for q in raw_input().split():
    if q in "+-*/":
        # An operator; store it and skip to the next entry
        o = q
        continue
    # n holds the converted Roman numeral, s is a temp storage variable
    n = s = 0
    # X stores our current index moving left-to-right in the string '?MDCLXVI'
    X = 1
    for l in q:
        # x is the index of the current letter in '?MDCLXVI'
        x = ''.join(N).find(l)
        # Calculate the value of this letter based on x
        v = (5 - x%2 * 4) * 10 ** (3 - x/2)
        if X < x:
            # We're moving forward in the list, e.g. CX
            n += s      # Add in any previously-stored value
            s = v       # Store this value in case we have something like CXL
            X = x       # Advance the index
        elif X > x:
            # Moving backward, e.g. XC
            n += v - s  # Add the current value and subtract the stored one
            s=0
        else:
            # Same index as before, e.g. XX
            n += v + s  # Add the current value and any stored one
            s = 0
    # Update total using operator and value (including leftover stored value
    # if any)
    exec "t" + o + "=n+s"

# Now convert the answer back to Roman numerals
# Special-case the thousands digit
r = t / 1000 * 'M'
# Loop over the number mod 1000, padded with zeroes to four digits (to make
# the indices come out right)
for p, d in enumerate("%04d" % (t % 1e3)):
    i = "49".find(d)
    g = N[p]
    if i < 0:
        # i == -1, thus d isn't '4' or '9'
        if '4' < d:
            # >= 5, so add the 5's letter
            r += g[0]
        # ... plus (digit % 5) copies of the 1's letter
        r += int(d) % 5 * g[1]
    else:
        # If it's a 4 or 9, add the 1's letter plus the appropriate
        # larger-valued letter
        r += g[1] + N[p-i][i]
print r

Perlの方が良かったのではないかと感じていますが、十分な知識はありません。しかし、コードゴルフでの最初の突き刺しについては、これについてかなりいい気分です。


1

PHP — 549 525 524 520バイト

あまり革新的ではありません:演算子を正規化して左から右への優先順位を確保し、ローマ字を10進数に変換evalし、ステートメントを実行します。たとえば、XCIX + I / L * D + IVreturn(((((+90 +9) +(+1))/(+50))*(+500))+(+4)); 、その後、小数をローマ字に変換します。

  • 最終結果は切り捨てられます
  • 1未満の回答は空白になります
  • 誤った入力が与えられた場合、結果は未定義です
$f='str_replace';$g='str_split';$c=array('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);$j='['.$f(array('+','-','*','/'),array('])+[','])-[','])*[','])/['), $argv[1]).'])';$j=str_repeat('(',substr_count($j,')')).$j;$j=$f('[','(',$j);$j=$f(']',')',$j);foreach($g('IVIXXLXCCDCM',2)as$w)$j=$f($w,'+'.$c[$w],$j);foreach($g('IVXLCDM')as$w)$j=$f($w,'+'.$c[$w],$j);$k=eval('return '.$j.';');$l='';foreach($c as$a=>$b){while($k>=$b){$l.=$a;$k-=$b;}}print$l."\n";

例えば

$ php roman.php 'XCIX + I / L * D + IV' — test case
MIV                                     — 1004

$ php roman.php 'XXXII * LIX'           — 32 × 59
MDCCCLXXXVIII                           — 1888

0

Python-446バイト

これはかなり改善される可能性があります。Pythonを使用して最初のスイングを取る必要があると感じました。最初のパスで3つのことを行います

  1. 数字と演算子をトークン化する
  2. 数値を評価し、シンボルテーブルxを拡大して、検出されたすべての可能な組み合わせを含めます(使用されていない場合でも)。例えば、一方ではXIX、の部分値をレクサー処理され"X":10"XI":11そして"XIX":19シンボル・テーブルに追加されます
  3. ネストされた括弧を挿入して、左から右への評価を強制します

最後evalに、元の文字列(追加のかっこを除く)を呼び出し、シンボルテーブルを提供します。

その後、整数にローマを変換するための既知の解決策を貼り付けました。これは十分に長い間取り組んできたためです...気軽に改善して、何か新しいことを学んでください:)

m = zip((1000,900,500,400,100,90,50,40,10,9,5,4,1)、
(「M」、「CM」、「D」、「CD」、「C」、「XC」、「L」、「XL」、「X」、「IX」、「V」、「IV」、「私'))
def doit(s):
 x = {'M':1e3、 'D':500、 'C':100、 'L':50、 'X':10、 'V':5、 'I':1}; y = [] ; z = ''; a = '0'; s = '+' + s
 s.upper()のcの場合:
  xのcの場合:
   z + = c; y.append(x [c])
   len(y)> 1かつy [-1]> y [-2]:y [-2] * =-1の場合
   x [z] = sum(y)
  elif c in "+ / *-":a = '(' + a + z + ')' + c; y = []; z = ''
 a + = z; i = eval(a、x); r = ''
 n、c for m:d = int(i / n); r + = c * d; i- = n * d
 リターンr


印刷doit( "XIX + LXXX")
print doit( "XCIX + I / L * D + IV")
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.