Mã hóa La Mã


33

Thách thức là làm cho bất kỳ mã số La Mã hợp lệ trong ngôn ngữ bạn chọn.

Chúng không nên xuất hiện bên trong chuỗi hoặc bất cứ thứ gì tương tự, nhưng hoạt động giống như bất kỳ mã thông báo nào khác, nghĩa đen như số ( tiếng Ả Rập ), ký tự hoặc chuỗi; hoặc định danh biến / phương thức / hàm, v.v.

Chẳng hạn, trong Java, những thứ sau đây sẽ phải biên dịch và chạy như thể iđược khởi tạo thành 42:

int i = XLII;

Việc phân tích cú pháp thực tế của các chữ số là thứ yếu, vì vậy bạn có thể sử dụng thư viện nếu bạn muốn, nhưng đây là một cuộc thi phổ biến, vì vậy sự sáng tạo được khuyến khích.

Bạn không thể sử dụng bất kỳ ngôn ngữ nào thực sự sử dụng chữ số La Mã, nếu có một điều như vậy.

Chúc may mắn.


1
Vì vậy, chúng ta cần phải viết một phần mở rộng cho ngôn ngữ, từ đó tạo ra một phần mới?
Kendall Frey

4
Tôi sẽ phàn nàn nếu tôi muốn, vì ngôn ngữ tôi sử dụng không thể mở rộng như thế, nên tôi thậm chí không thể tham gia.
Kendall Frey

3
@KendallFrey Nguồn sẽ phải biên dịch và chạy. Đối với Java, bạn có thể viết một "trình biên dịch" chỉnh sửa nguồn, sau đó biên dịch theo chương trình . Một cách biên dịch như vậy là thông qua việc chạyProcess
Justin

1
Có vẻ nhàm chán trong hầu hết các ngôn ngữ. Ví dụ trong python tôi chỉ đơn giản là viết một đoạn script sử dụng astđể phân tích nguồn. Chèn ở đầu AST định nghĩa các chữ số La Mã từ 1 đến 3999. Biên dịch toàn bộ và chạy nó. Thật nhàm chán khi viết mã để xử lý quá trình.
Bakuriu

2
@Bakuriu và bình luận của bạn cũng nhàm chán. Đây là một cuộc thi phổ biến, vì vậy bạn nên cố gắng đưa ra một cái gì đó vui vẻ. Tôi nghĩ rằng có một số câu trả lời hay ở đây có nhiều trí tưởng tượng (hơn là biên dịch một ngôn ngữ kịch bản).
daniero

Câu trả lời:


40

C

Chỉ có rất nhiều chữ số La Mã, từ 4000 trở lên không có ký hiệu chuẩn và bộ tiền xử lý là một công cụ giải nén tuyệt vời, đặc biệt nếu bạn không gặp vấn đề gì với thực tế là mã có hành vi không xác định.

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ều này xác định tất cả các chữ số La Mã từ Iđến MMMCMXCIXlà hằng số liệt kê, cộng _(có thể được thay thế bằng bất cứ thứ gì bạn thích) là số không.


12
Hoàn toàn xuất sắc, đề xuất thêm nó vào bản phát hành C tiếp theo (C2X?) Dưới dạng <roman.h>! Với định dạng printf% r và% R!

3
Tôi nghĩ rằng tôi biết làm thế nào để sử dụng bộ tiền xử lý, cho đến bây giờ: | Bạn có thể cập nhật câu trả lời của bạn với một ví dụ sử dụng tối thiểu của enum này không?
klingt.net

@YiminRong Và scanfcũng vậy :) @ klingt.net Tôi không chắc bạn đang tìm kiếm loại ví dụ nào. Một điều khá đơn giản sẽ làint main() { return MMMCMXCIX - M - M - M - CM - XC - IX; }
hvd

Tôi thích ý tưởng này, nhưng tại sao bạn lại chọn thực hiện hành vi không xác định khi hành vi được xác định khá đơn giản? Không phán xét, chỉ tò mò?
CasaDeRobison

1
@CasaDeRobison Cho vui, chủ yếu. Tôi thích nó bởi vì đây là một trong số rất ít trường hợp hành vi không xác định tại thời điểm tiền xử lý không bị gắn cờ là lỗi của trình biên dịch hiện tại và có thể sẽ không có trong tương lai. Tôi không bao giờ viết bất cứ điều gì như thế trong mã có nghĩa là hữu ích, nhưng mã được đăng ở đây không có nghĩa là hữu ích, vậy còn dịp nào tốt hơn để thử nó?
hvd

15

Hồng ngọc

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

Bất kỳ chữ số La Mã nào (chữ hoa) bây giờ sẽ được phân tích cú pháp giống như số thập phân của chúng. Vấn đề duy nhất là chúng vẫn có thể được gán: bạn có thể làm X = 9, nhưng không 10 = 9. Tôi không nghĩ có cách nào khắc phục điều đó.


1
Đây là hoàn hảo. Điều phân công không phải là một vấn đề; Bạn có thể sử dụng các bài tập để làm tất cả các loại điều ngu ngốc.
daniero

12

JavaScript (ES6)

Sử dụng Proxyđể bắt chữ số La Mã.

Có thể kiểm tra trong Firefox (mới nhất) trên JSFiddle .
Không thể kiểm tra trong Chrome (với Traceur) do Proxyviệc triển khai bị hỏng.

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

Sử dụng:

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

10

C & C ++ (Trả lời cập nhật)

Theo quan sát trong một bình luận, giải pháp ban đầu của tôi có hai vấn đề:

  1. Các tham số tùy chọn chỉ có sẵn trong C99 và các tiêu chuẩn mới hơn của họ ngôn ngữ.
  2. Dấu phẩy dấu phẩy trong định nghĩa enum cũng đặc trưng cho C99 trở lên.

Vì tôi muốn mã của mình càng chung chung càng tốt để hoạt động trên các nền tảng cũ hơn, tôi đã quyết định thực hiện một cú đâm khác vào nó. Nó dài hơn so với trước đây, nhưng nó hoạt động trên các trình biên dịch và tiền xử lý được đặt thành chế độ tương thích C89 / C90. Tất cả các macro được truyền một số lượng đối số thích hợp trong mã nguồn, mặc dù đôi khi các macro đó "mở rộng" thành không có gì.

Visual C ++ 2013 (còn gọi là phiên bản 12) phát ra các cảnh báo về các tham số bị thiếu, nhưng cả mcpp (bộ tiền xử lý nguồn mở yêu cầu tuân thủ cao với tiêu chuẩn) cũng như gcc 4.8.1 (với -std = iso9899: 1990 -pedantic-error) cảnh báo hoặc lỗi cho các yêu cầu vĩ mô đó với một danh sách đối số trống có hiệu quả.

Sau khi xem xét tiêu chuẩn có liên quan (ANSI / ISO 9899-1990, 6.8.3, Thay thế vĩ mô), tôi nghĩ có đủ sự mơ hồ rằng điều này không nên được coi là không chuẩn. "Số lượng đối số trong lệnh gọi macro giống như hàm sẽ đồng ý với số lượng tham số trong định nghĩa macro ...". Nó dường như không loại trừ một danh sách đối số trống miễn là các dấu ngoặc đơn cần thiết (và dấu phẩy trong trường hợp có nhiều tham số) được đặt ra để gọi macro

Đối với vấn đề dấu phẩy, được giải quyết bằng cách thêm một số nhận dạng bổ sung vào bảng liệt kê (trong trường hợp của tôi, MMMM có vẻ hợp lý như bất cứ điều gì để định danh tuân theo 3999 ngay cả khi nó không tuân theo các quy tắc được chấp nhận của trình tự chữ số La Mã chính xác).

Một giải pháp sạch hơn một chút sẽ liên quan đến việc di chuyển enum và các macro hỗ trợ sang một tệp tiêu đề riêng như được ngụ ý trong một nhận xét ở nơi khác và sử dụng undef của các tên macro ngay sau khi chúng được sử dụng để tránh gây ô nhiễm không gian tên. Tên macro tốt hơn chắc chắn nên được chọn là tốt, nhưng điều này là đủ cho nhiệm vụ trong tay.

Giải pháp cập nhật của tôi, theo sau là giải pháp ban đầu của tôi:

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

Câu trả lời ban đầu (đã nhận được sáu lần nâng cấp đầu tiên, vì vậy nếu không ai nhận được điều này một lần nữa, bạn không nên nghĩ rằng giải pháp cập nhật của tôi đã nhận được các upvote):

Với tinh thần tương tự như một câu trả lời trước đó, nhưng thực hiện theo một cách mà nên được cầm tay sử dụng hành vi chỉ định (mặc dù môi trường khác nhau không phải lúc nào đồng ý trên một số khía cạnh của tiền xử lý). Xử lý một số tham số là tùy chọn, bỏ qua các tham số khác, nó nên hoạt động trên các bộ tiền xử lý không hỗ trợ __VA_ARGS__macro, bao gồm C ++, nó sử dụng macro gián tiếp để đảm bảo các tham số được mở rộng trước khi dán mã thông báo và cuối cùng nó ngắn hơn và tôi nghĩ dễ đọc hơn ( mặc dù nó vẫn còn khó và có thể không dễ đọc, chỉ dễ hơn):

#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, nhưng lưu ý rằng việc sử dụng các đối số macro trống và dấu phẩy ở cuối danh sách liệt kê, cả hai tính năng mới của C99 và C ++ 11, và cả C99 và C ++ 11 đều hỗ trợ __VA_ARGS__.
hvd

Dang nếu bạn không đúng! Tôi đoán rằng tôi đã thấy điều này được sử dụng như phần mở rộng tất cả các thời gian này. À :)
CasaDeRobison

8

Lisp thường gặp

Sau đây là một lời giải thích khá dài về cách tôi tạo một macro mà bạn có thể sử dụng như thế này:

(roman-progn
  (+ XIV XXVIII))

Khi một macro được gọi trong Common Lisp, về cơ bản nó hoạt động như một hàm, chỉ có các đối số được nhận trước khi chúng được đánh giá. Trên thực tế, vì trong mã Lisp thông thường chỉ là dữ liệu, những gì chúng ta nhận được là một danh sách (lồng nhau) đại diện cho một cây cú pháp chưa được chỉnh sửa mà chúng ta có thể làm bất cứ điều gì chúng ta muốn và nó được thực hiện trong thời gian biên dịch.

Chức năng trợ giúp

Bước đầu tiên của kế hoạch là lấy cây này và quét nó để tìm bất cứ thứ gì trông giống như Chữ số La Mã. Đây là Lisp và tất cả, chúng ta hãy cố gắng thực hiện phần nào chức năng: Chúng ta cần một hàm sẽ thực hiện một giao dịch sâu của cây và trả về mọi đối tượng mà hàm được cung cấp searchptrả về đúng. Điều này là thậm chí (bán) đệ quy đuôi.

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

Sau đó, một số mã để phân tích các chữ số La Mã, lịch sự của Mã Rosetta :

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

Macro thực tế

Chúng tôi lấy cây cú pháp ( body), tìm kiếm nó với thủ tục tìm sâu tất cả của chúng tôi và bằng cách nào đó làm cho các chữ số La Mã mà chúng tôi tìm thấy, có sẵn.

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

Vậy, là 1 + 2 + 3 + (4 * (5 + 6)) + 7gì?

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

Và để xem những gì thực sự đã xảy ra khi macro được gọi:

(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

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

Đơn giản chỉ là một dự phòng __index cho bảng toàn cầu. Việc chuyển đổi thực tế bằng gsub hóa ra đẹp hơn nhiều so với tôi tưởng tượng.


5

Bản thảo

Tôi đã cố làm theo C nhưng tôi không hiểu. Vì vậy, tôi đã làm theo cách này:

/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

Postcript không có enumnhưng chúng ta có thể xây dựng một từ điển với các giá trị nguyên liên tiếp và gấp chúng thành một mảng. Điều này làm giảm vấn đề tạo ra tất cả các chuỗi theo trình tự, được thực hiện bằng cách nối trong 4 vòng lặp lồng nhau. Vì vậy, nó tạo ra tất cả các chuỗi, sau đó xen kẽ mỗi chuỗi với giá trị bộ đếm tăng dần, dẫn đến một chuỗi dài các cặp <chuỗi> <int> trên ngăn xếp được bọc trong <<... >>để tạo ra một đối tượng từ điển.

Chương trình xây dựng và cài đặt một từ điển ánh xạ tất cả các tên cho các chữ số La Mã thành giá trị tương ứng của chúng. Vì vậy, việc đề cập đến các tên trong văn bản nguồn sẽ gọi tra cứu tên tự động và mang lại giá trị nguyên trên ngăn xếp.

II IV MC pstack

in

2
4
600

4

Smalltalk (Smalltalk / X) (ký tự 87/101)

tất nhiên chúng ta có thể dễ dàng sửa đổi mã thông báo của trình phân tích cú pháp (vì nó là một phần của thư viện lớp và mở như vậy để sửa đổi và luôn có mặt), nhưng một thách thức là chỉ ảnh hưởng đến các đánh giá trong một ngữ cảnh cụ thể, do đó phần còn lại của hệ thống hoạt động như bình thường.

Phiên bản 1:

xác định một số biến trong không gian tên đánh giá. Vì vậy, điều này sẽ ảnh hưởng đến doIts tương tác (còn gọi là evals):

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

sau đó tôi có thể làm (trong một doIt, nhưng không phải trong mã được biên dịch):

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

-> 2019

Lưu ý: 101 ký tự bao gồm khoảng trắng; thực sự nó có thể được thực hiện với 87 ký tự.
Cũng lưu ý, khi định nghĩa trong không gian tên Smalltalk toàn cầu, tôi sẽ thấy các hằng số đó cũng trong mã được biên dịch.

Phiên bản 2:

Sử dụng hook phương thứcWrapper, cho phép mọi mã hiện có được gói mà không cần biên dịch lại. Phần sau đây kết thúc mã thông báo của Trình phân tích cú pháp để tìm mã định danh La Mã được quét và biến nó thành số nguyên. Phần khó khăn là tự động phát hiện xem bối cảnh gọi có phải từ đế chế La Mã hay không. Điều này được thực hiện bằng cách sử dụng tín hiệu truy vấn (về mặt kỹ thuật là ngoại lệ có thể tiến hành):

xác định truy vấn:

InRomanScope := QuerySignal new defaultAnswer:false.

Vì vậy, chúng tôi có thể yêu cầu bất cứ lúc nào ("truy vấn InRomanScope") để nhận sai theo mặc định.

Sau đó bọc phương thức checkIdentifier của máy quét:

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

Bây giờ máy quét hoạt động như bình thường, trừ khi chúng ta ở trong đế chế La Mã:

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

-> 2525

chúng tôi thậm chí có thể biên dịch mã:

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

cố gắng tốt đẹp; nhưng điều này không thành công với một lỗi cú pháp (đó chính xác là những gì chúng ta muốn). Tuy nhiên, trong đế chế La Mã, chúng ta CÓ THỂ biên dịch:

InRomanScope answer:true do:[

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

và bây giờ, chúng ta có thể yêu cầu bất kỳ số nguyên nào (gửi tin nhắn đó) từ bên trong và bên ngoài Rome:

(1000 factorial) inTheYear2525

-> 2525


Rất vui được gặp Smalltalk!

4

Haskell, sử dụng lập trình meta trong Mẫu Haskellchữ số La Mã :

{-# 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 dự trữ định danh bắt đầu bằng chữ in hoa cho các nhà xây dựng, vì vậy tôi đã sử dụng chữ thường.


4

J - 78 char

Điều này chỉ đi lên đến MMMCMXCIX = 3999, như với các giải pháp khác.

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

Phá vỡ nó (nhớ lại J thường được đọc từ phải sang trái, trừ khi được thay thế bởi dấu ngoặc đơn):

  • M`CDM`XLC`IVX- Bốn hộp chữ cái. Chúng ta sẽ sử dụng các mảng số thành chỉ mục vào các chữ cái này và xây dựng các từ con của chữ số La Mã.
  • 841,3#79bc5yuukh - Đây là dữ liệu số, được mã hóa chặt chẽ. *
  • (_1,~3#.inv]) - Điều này sẽ giải mã dữ liệu trên, bằng cách mở rộng về phía trước và nối thêm -1.
  • ('';&;:(...){' ',[)&.>- Ghép các số ở bên trái với các hộp bên phải ( &.>), giải mã các mảng số và sử dụng chúng để lập chỉ mục thành các chữ cái. Chúng tôi coi 0 là khoảng trắng bằng cách thêm một ký tự khoảng trắng vào danh sách chữ cái. Thủ tục này xây dựng danh sách các từ như I II III IV V VI VII VIII IXM MM MMM.
  • {- Lấy sản phẩm của Cartesian trong bốn hộp đầy chữ. Bây giờ chúng ta có một mảng 4D của tất cả các chữ số La Mã.
  • }.,;L:1- Chạy tất cả những thứ đó vào một danh sách các chữ số La Mã 1D duy nhất và xóa chuỗi trống ở phía trước vì nó sẽ tạo ra lỗi. ( L:là một cảnh hiếm thấy trong golf J! Thông thường không có nhiều cấp độ quyền anh liên quan.)
  • }.i.4e3- Các số nguyên từ 0 đến 4000, không bao gồm các điểm cuối.
  • Cuối cùng, chúng tôi đặt mọi thứ cùng với một nhiệm vụ toàn cầu =:. J cho phép bạn có một danh sách các tên được đóng hộp trên LHS, dưới dạng một phép gán nhiều phép tính, vì vậy điều này hoạt động tốt.

Bây giờ không gian tên J có đầy đủ các biến đại diện cho chữ số La Mã.

   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

* Tôi cần số 2933774030998 để sau này được đọc ở cơ sở 3. Điều đó xảy ra là tôi có thể diễn đạt nó trong cơ sở 79 bằng cách sử dụng các chữ số không lớn hơn 30, điều này tốt vì J chỉ có thể hiểu các chữ số lên đến 35 (0-9 và sau đó az). Điều này tiết kiệm 3 ký tự trên thập phân.


3

Con trăn

Ý tưởng đơn giản như các câu trả lời khác. Nhưng chỉ để gọn gàng và không gây ô nhiễm không gian tên toàn cầu, một trình quản lý bối cảnh được sử dụng. Điều này cũng áp đặt các hạn chế, mà bạn cần phải khai báo trước khi bàn tay, phạm vi số La Mã bạn đang dự định sử dụng.

Lưu ý Chỉ để đơn giản và không phát minh lại bánh xe, tôi đã sử dụng gói trăn La Mã

Thực hiện

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)

Bản giới thiệu

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

Con trăn

Đây có thể là giải pháp đơn giản nhất bằng 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
Sử dụng globals()[var] = valuetốt hơn exec().
Ramowderra Apte

3

D

sử dụng đánh giá hàm thời gian biên dịch của 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 byte

Nhắc về độ dài tối đa của chữ số La Mã và xác định tất cả các biến.

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

t←t được

'IVXLCDM', Chars La Mã theo sau

 một bao vây

 danh sách trống

t[... ] chỉ số t với ...

 chuyển đổi (để có được thứ tự đúng)

8⊥⍣¯1 chiều rộng cơ sở tám đại diện thích hợp của

n chỉ số  đầu tiên , trong đó n

¯1+ ít hơn một

8*⎕ tám đến sức mạnh của đầu vào số

,/ làm phẳng hàng (mỗi đại diện)

{Sự  áp dụng chức năng ẩn danh sau đây trên mỗi đại diện

(... )[t⍳⍵] tương ứng với vị trí các hạng mục của lập luận trong t , lựa chọn tuyệt vời ...

   người nhập ngũ

  1 5∘ר một và năm lần mỗi

  10* mười đến sức mạnh của

  ⍳4 từ 0 đến 3

0,⍨ nối số 0

2(…)/ trên mỗi cửa sổ trượt hai chiều dài, áp dụng hàm đào tạo hàm ẩn danh sau

  ⊣× thời gian đối số bên trái

  ¯1* tiêu cực với sức mạnh của

  < đối số bên trái có nhỏ hơn đối số bên phải không

+/ tổng

⍵'←', thêm vào đối số (chữ số La Mã) và một mũi tên gán

 định dạng (để làm phẳng và chuyển đổi số thành văn bản)

 thực hiện điều đó (thực hiện chuyển nhượng bên ngoài chức năng ẩn danh)

Hãy thử trực tuyến! (sử dụng độ dài tối đa 5)


2

PHP

Có một số quy tắc cho các số La Mã hợp lệ

  1. Viết giá trị lớn nhất cho các giá trị thấp hơn

  2. Chỉ trừ [I,X,C]trước 2 giá trị lớn hơn tiếp theo

  3. Trừ đi gấp đôi [I,X,C]trước 2 giá trị lớn hơn tiếp theo

  4. Trừ đi gấp đôi [I,X,C]trước các giá trị lớn hơn

  5. Kết hợp 4 + 5

Phiên bản trực tuyến

Bước 1 Tạo quy tắc

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

là đầu ra JSON cho tất cả các số La Mã hợp lệ

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

Bước 2 Lập danh sách cho tất cả các quy tắc cho đến 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;
}

Bước 3 Tạo hằng số

Kết hợp tất cả các danh sách và xác định hằng số

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

Đầu ra

Trong ví dụ này, hai phiên bản hợp lệ của số 8

echo IIX *  VIII;

Đẹp, nhưng làm thế nào để tôi sử dụng này? Tôi không rành về PHP; Bạn có thể vui lòng cho một ví dụ về cách điều này cho phép tôi viết các chữ số La Mã trong mã không?
daniero 17/03/2017

@Daniero Bây giờ mã sẽ hoạt động
Jörg Hülsermann 17/03/2017

Ah, đó là tốt hơn :)
daniero

1

Nổi loạn

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


Thí dụ

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

Đầu ra:

15

1015

Vâng, đó là năm 2014

1 + 10 = 11

2 + 10 = 12

3 + 10 = 13

4 + 10 = 14

5 + 10 = 15


Tuyên bố miễn trừ trách nhiệm: Tôi chắc chắn cũng có những cách khác (và có lẽ tốt hơn!) Để thực hiện điều này trong Rebol.

Tái bút My roman-to-integerchức năng là một phiên âm của histocrat 's thoải mái thuật toán của Ruby để chuyển đổi Roman Numeral chuỗi thành một số. Trở về với lời cảm ơn! +1


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

Điều này ảnh hưởng đến khả năng hiển thị của bảng toàn cầu, tạo cho nó một hàm chỉ mục mới. Khi một biến toàn cục chỉ chứa các chữ số La Mã được yêu cầu, ví dụ XVII, nó phân tích cú pháp.

Dễ kiểm tra;

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

Hãy thử trực tuyến!


1

VBA, 204 byte

Một tuyên bố chương trình con mà mất không có đầu vào, và khi chạy, tạo ra các publicly tiếp cận Enum, Rcó chứa tất cả các giá trị La Mã số. Các giá trị này có thể được sử dụng trực tiếp mà không cần tham khảo Enum.

Enum giữ các giá trị từ 1 đến 3999.

Lưu ý: Các thiết bị đầu cuối "trên dòng 3 và 7 chỉ được bao gồm để làm nổi bật cú pháp và không đóng góp cho 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

Ungolfed và giải thích

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
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.