T-SQL, 1974-50 = 1924 bajtów
Wiem, że gra w golfa w SQL jest równoznaczna z graniem w 18 dołków bez piasku, ale podoba mi się to wyzwanie i myślę, że udało mi się zrobić kilka interesujących rzeczy metodologicznie.
To obsługuje vinculum zarówno dla wejścia jak i wyjścia. Przyjąłem konwencję używania tylnego tylda do reprezentowania go, więc V ~ to 5000, X ~ to 10000 itp. Powinien on również obsługiwać wyjścia do 399,999 zgodnie ze standardowym nowoczesnym użyciem cyfr rzymskich. Następnie wykona częściowo niestandardowe rzymskie kodowanie czegokolwiek w obsługiwanym zakresie INT.
Ponieważ jest to matematyka w liczbach całkowitych, wyniki nie będące liczbami całkowitymi są domyślnie zaokrąglane.
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
Nadal majstruję przy rozwiązaniu opartym na zestawie, aby zastąpić część pętli WHILE, która może zmniejszyć liczbę bajtów i być bardziej eleganckim przykładem idiomatycznego SQL. Istnieją również pewne bajty, które można uzyskać, ograniczając użycie aliasów tabeli do absolutnego minimum. Ale ponieważ w tym języku jest to w zasadzie niemożliwe do wygrania, jestem głównie tutaj, aby pochwalić się moim strojem Don Kichota. :)
SELECT @i u góry powtarza dane wejściowe:
I+V*IV+IX*MXLVII+X~C~DCCVI
A SELECT na końcu zwraca:
SELECT (((((1+5)*4)+9)*1047)+90706) = 125257 = C~X~X~V~CCLVII
I możesz to przetestować sam na tym SQLFiddle
Wrócę, aby dodać komentarz na temat tego, jak to działa, ponieważ po co publikować oczywistą przegraną odpowiedź, jeśli nie zamierzasz wykorzystać jej dla wartości edukacyjnej?