รหัสการทำให้เป็นสุริยวรมัน


33

ความท้าทายคือการทำให้รหัสโรมันเป็นรหัสที่ถูกต้องในภาษาที่คุณเลือก

พวกเขาไม่ควรปรากฏภายในสตริงหรือสิ่งอื่นที่คล้ายกัน แต่ทำงานเหมือนกับโทเค็นอื่น ๆตัวอักษรเช่นตัวเลข( อาหรับ ) ตัวละครหรือสตริง หรือตัวระบุตัวแปร / วิธี / ฟังก์ชั่น ฯลฯ

ตัวอย่างเช่นใน Java ต่อไปนี้จะต้องรวบรวมและเรียกใช้ราวกับว่าiได้รับการเริ่มต้น42:

int i = XLII;

การแยกวิเคราะห์ตัวเลขที่แท้จริงเป็นเรื่องรองดังนั้นคุณสามารถใช้ห้องสมุดได้หากคุณต้องการ แต่นี่เป็นการประกวดความนิยมดังนั้นจึงส่งเสริมให้มีความคิดสร้างสรรค์

คุณไม่สามารถใช้ภาษาใดก็ได้ที่ใช้ตัวเลขโรมันหากมีสิ่งนั้น

โชคดี.


1
ดังนั้นเราจำเป็นต้องเขียนส่วนขยายเป็นภาษาดังนั้นการสร้างใหม่หรือไม่
Kendall Frey

4
ฉันจะบ่นถ้าฉันต้องการเพราะภาษาที่ฉันใช้นั้นไม่สามารถขยายได้อย่างนั้นฉันจึงไม่สามารถมีส่วนร่วมได้
Kendall Frey

3
@KendallFrey แหล่งที่มาจะต้องรวบรวมและเรียกใช้ สำหรับ Java คุณสามารถเขียน "คอมไพเลอร์" ที่มีการแก้ไขมาแล้วโปรแกรมรวบรวม วิธีหนึ่งในการรวบรวมดังกล่าวคือการใช้งานProcess
จัสติน

1
ดูเหมือนจะน่าเบื่อในภาษาส่วนใหญ่ เช่นในไพ ธ อนฉันก็แค่เขียนสคริปต์ที่ใช้astในการแยกวิเคราะห์แหล่งที่มา ใส่ที่ด้านบนของ AST นิยามของเลขโรมันตั้งแต่ 1 ถึง 3999 รวบรวมสิ่งทั้งหมดและเรียกใช้ เป็นเรื่องน่าเบื่อที่จะเขียนโค้ดเพื่อจัดการกับกระบวนการ
Bakuriu

2
@Bakuriu และความคิดเห็นของคุณก็น่าเบื่อเช่นกัน นี่คือการประกวดความนิยมดังนั้นคุณควรลองทำสิ่งที่สนุก ฉันคิดว่ามีคำตอบที่ดีที่นี่ซึ่งมีจินตนาการมากกว่า (รวบรวมภาษาสคริปต์)
daniero

คำตอบ:


40

C

มีตัวเลขโรมันจำนวนมากเท่านั้นเนื่องจากตั้งแต่ 4,000 ขึ้นไปไม่มีสัญกรณ์มาตรฐานและตัวประมวลผลล่วงหน้าเป็นเครื่องมือในการคลายการบีบอัดที่ยอดเยี่ยมโดยเฉพาะอย่างยิ่งหากคุณไม่มีปัญหากับความจริงที่ว่าโค้ดมีพฤติกรรมที่ไม่ได้กำหนด

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
ยอดเยี่ยมอย่างยิ่งเสนอให้เพิ่มลงใน C รุ่นถัดไป (C2X?) เป็น <roman.h>! ด้วยรูปแบบ% r และ% R printf!

3
ฉันคิดว่าฉันรู้วิธีใช้ preprocessor จนถึงตอนนี้: | คุณสามารถปรับปรุงคำตอบของคุณด้วยตัวอย่างการใช้งานที่น้อยที่สุดของ enum นี้ได้หรือไม่?
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ฉันไม่คิดว่าจะมีวิธีแก้ไขได้


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 (aka รุ่น 12) ส่งเสียงคำเตือนเกี่ยวกับพารามิเตอร์ที่หายไป แต่ mcpp (ตัวประมวลผลก่อนโอเพนซอร์สที่อ้างว่าปฏิบัติตามมาตรฐานสูง) หรือ gcc 4.8.1 (พร้อม -std = iso9899: 1990 -pedantic-errors สวิตช์) ปล่อย คำเตือนหรือข้อผิดพลาดสำหรับการเรียกใช้แมโครเหล่านั้นด้วยรายการอาร์กิวเมนต์ที่ว่างเปล่าอย่างมีประสิทธิภาพ

หลังจากตรวจสอบมาตรฐานที่เกี่ยวข้อง (ANSI / ISO 9899-1990, 6.8.3, การแทนที่มาโคร) ฉันคิดว่ามีความกำกวมเพียงพอที่จะไม่ถือว่าเป็นมาตรฐาน "จำนวนอาร์กิวเมนต์ในการภาวนาของมาโครคล้ายฟังก์ชันจะเห็นด้วยกับจำนวนพารามิเตอร์ในนิยามแมโคร ... " ดูเหมือนจะไม่ตัดทอนรายการอาร์กิวเมนต์ที่ว่างเปล่าตราบใดที่วงเล็บที่ต้องการ (และเครื่องหมายจุลภาคในกรณีของพารามิเตอร์หลายตัว) อยู่ในตำแหน่งเพื่อเรียกใช้แมโคร

สำหรับปัญหาเครื่องหมายจุลภาคต่อท้ายที่ได้รับการแก้ไขโดยการเพิ่มตัวระบุพิเศษลงในการแจงนับ (ในกรณีของฉัน MMMM ซึ่งดูเหมือนว่าสมเหตุสมผลตามที่ตัวบ่งชี้ทำตาม 3999 แม้ว่าจะไม่ปฏิบัติตามกฎที่ยอมรับของลำดับเลขโรมัน ตรง)

วิธีแก้ปัญหาที่สะอาดกว่าเล็กน้อยจะเกี่ยวข้องกับการย้าย enum และมาโครที่สนับสนุนไปยังไฟล์ส่วนหัวแยกตามที่ระบุไว้ในความคิดเห็นที่อื่นและใช้ 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;
}

คำตอบเดิม (ซึ่งได้รับหก upvotes แรกดังนั้นหากไม่มีใคร upvotes นี้อีกครั้งคุณไม่ควรคิดว่าโซลูชันที่อัปเดตของฉันจะได้รับ upvotes):

ในจิตวิญญาณเดียวกันกับคำตอบก่อนหน้านี้ แต่ทำในลักษณะที่ควรพกพาโดยใช้พฤติกรรมที่กำหนดเท่านั้น (แม้ว่าสภาพแวดล้อมที่แตกต่างกันไม่เห็นด้วยเสมอในบางแง่มุมของตัวประมวลผลล่วงหน้า) ใช้พารามิเตอร์บางอย่างเป็นตัวเลือกละเว้นผู้อื่นควรทำงานกับตัวประมวลผลล่วงหน้าที่ไม่สนับสนุน__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__ทำสนับสนุน
hvd

แดงถ้าคุณไม่ถูกต้อง! ฉันเดาว่าฉันเคยเห็นสิ่งนี้ใช้เป็นส่วนขยายตลอดเวลา อ่า :)
CasaDeRobison

8

เสียงกระเพื่อมสามัญ

ต่อไปนี้เป็นคำอธิบายที่ค่อนข้างยาวเกี่ยวกับวิธีที่ฉันสร้างแมโครที่คุณสามารถใช้ดังนี้

(roman-progn
  (+ XIV XXVIII))

เมื่อแมโครถูกเรียกใน Common LISP นั้นโดยทั่วไปจะทำหน้าที่เหมือนฟังก์ชันเท่านั้นที่จะได้รับอาร์กิวเมนต์ก่อนที่จะได้รับการประเมิน ที่จริงแล้วในรหัส Common LISP เป็นข้อมูลเพียงอย่างเดียวสิ่งที่เราได้รับคือรายการ (ซ้อน) ที่แสดงแผนภูมิต้นไม้ที่ไม่มีการแยกวิเคราะห์ที่เราสามารถทำสิ่งที่เราต้องการได้และมันก็ทำในเวลารวบรวม

ฟังก์ชั่นตัวช่วย

ขั้นตอนแรกของแผนคือการเอาต้นไม้นี้และสแกนเพื่อหาสิ่งที่ดูเหมือนเลขโรมัน นี่คือเสียงกระเพื่อมและทั้งหมดลองทำค่อนข้างฟังก์ชั่น: เราต้องการฟังก์ชั่นที่จะทำการสำรวจเชิงลึกของต้นไม้และส่งคืนวัตถุทุกชิ้นที่ฟังก์ชันที่ให้มาsearchpคืนค่าเป็นจริง อันนี้เป็นแบบหาง (กึ่ง) ซ้ำ

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

(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

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 ตัวอักษร)

แน่นอนว่าเราสามารถปรับเปลี่ยน tokenizer ของ parser ได้ง่าย (เนื่องจากเป็นส่วนหนึ่งของไลบรารีคลาสและเปิดให้มีการแก้ไขและนำเสนออยู่เสมอ) แต่ความท้าทายคือการส่งผลกระทบเฉพาะการประเมินในบริบทที่กำหนดเพื่อให้ส่วนที่เหลือของ ระบบทำงานได้ตามปกติ

รุ่น 1:

กำหนดจำนวนของตัวแปรในเนมสเปซการประเมิน ดังนั้นสิ่งนี้จะส่งผลต่อ doIts เชิงโต้ตอบ (aka 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:

ใช้เมธอดตะขอตะขอซึ่งอนุญาตให้มีการห่อโค้ดที่มีอยู่โดยไม่ต้องคอมไพล์ใหม่ ต่อไปนี้จะตัดโทเค็นของ Parser เพื่อค้นหาตัวระบุแบบโรมันที่จะสแกนและทำให้เป็นจำนวนเต็ม ส่วนที่ยุ่งยากคือการตรวจสอบแบบไดนามิกว่าบริบทการโทรนั้นมาจากอาณาจักรโรมันหรือไม่ สิ่งนี้ทำโดยใช้สัญญาณคิวรี (ซึ่งเป็นข้อยกเว้นทางเทคนิค):

กำหนดแบบสอบถาม:

InRomanScope := QuerySignal new defaultAnswer:false.

ดังนั้นเราสามารถถามได้ตลอดเวลา ("InRomanScope แบบสอบถาม") เพื่อรับค่าเท็จโดยค่าเริ่มต้น

จากนั้นห่อเมธอด 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 โดยใช้การเขียนโปรแกรมเมตาในเทมเพลต 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- ตัวอักษรสี่กล่อง เราจะใช้อาร์เรย์ตัวเลขเป็นดัชนีในตัวอักษรเหล่านี้และสร้าง subwords ของตัวเลขโรมัน
  • 841,3#79bc5yuukh - นี่คือข้อมูลตัวเลขที่เข้ารหัสอย่างแน่นหนา *
  • (_1,~3#.inv]) - สิ่งนี้จะถอดรหัสข้อมูลข้างต้นโดยการขยายแบบไตรภาคและต่อท้าย -1
  • ('';&;:(...){' ',[)&.>- จับคู่ตัวเลขทางด้านซ้ายกับกล่องทางด้านขวา ( &.>) ถอดรหัสอาร์เรย์ของตัวเลขและใช้เพื่อจัดทำดัชนีเป็นตัวอักษร เราถือว่า 0 เป็นช่องว่างโดยการใส่อักขระช่องว่างกับรายการตัวอักษร ขั้นตอนนี้จะสร้างรายการของคำเหมือนและI II III IV V VI VII VIII IXM MM MMM
  • {- นำผลิตภัณฑ์คาร์ทีเซียนของกล่องทั้งสี่นี้มาเต็มไปด้วยคำ ตอนนี้เรามีอาเรย์สี่มิติของตัวเลขโรมันทั้งหมด
  • }.,;L:1- เรียกใช้ทั้งหมดที่อยู่ในรายการ 1D เดียวของตัวเลขโรมันและลบสตริงว่างที่ด้านหน้าเพราะมันจะสร้างข้อผิดพลาด ( L:เป็นสิ่งที่เห็นได้ยากในเจกอล์ฟโดยปกติแล้วจะมีไม่กี่ระดับของมวยที่เกี่ยวข้อง)
  • }.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 แล้ว) AZ) วิธีนี้จะช่วยประหยัดอักขระได้มากกว่า 3 ตัว


3

หลาม

ความคิดนั้นง่ายเหมือนคำตอบอื่น ๆ แต่เพียงเพื่อให้เรียบร้อยและไม่สร้างมลภาวะเนมสเปซส่วนกลางให้ใช้ตัวจัดการบริบท สิ่งนี้ยังกำหนดข้อ จำกัด ที่คุณต้องประกาศก่อนมือขอบเขตของตัวเลขโรมันที่คุณวางแผนที่จะใช้

หมายเหตุเพียงเพื่อให้ง่ายและไม่ต้องคิดค้นล้อใหม่ฉันได้ใช้แพ็คเกจไพ ธ อนโรมัน

การดำเนินงาน

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

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[... ] ดัชนีเสื้อด้วย ...

 transposed (เพื่อให้ได้คำสั่งที่ถูกต้อง)

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 สำหรับหมายเลข roman ที่ถูกต้องทั้งหมด

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

เอาท์พุต

ในตัวอย่าง mutiply สองเวอร์ชันที่ถูกต้องของหมายเลข 8

echo IIX *  VIII;

เยี่ยมมาก แต่ฉันจะใช้มันได้อย่างไร ฉันไม่ชำนาญภาษา PHP คุณช่วยยกตัวอย่างวิธีการนี้ทำให้ฉันสามารถเขียนเลขโรมันในรหัสได้หรือไม่?
daniero

@Daniero ตอนนี้รหัสควรใช้งานได้
JörgHü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ฟังก์ชั่นของฉันคือการทับศัพท์ของอัลกอริธึม Ruby ดีของฮิสโทแคตสำหรับการแปลงสตริงตัวเลขโรมันให้เป็นตัวเลข กลับมาพร้อมกับขอบคุณ! +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

สิ่งนี้มีผลต่อ metatable ของตารางส่วนกลางทำให้เป็นฟังก์ชันดัชนีใหม่ เมื่อมีการถามตัวแปรโกลบอลที่มีเฉพาะตัวเลขโรมันเช่นXVIIมันจะแยกวิเคราะห์

ทดสอบง่าย

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

ลองออนไลน์!


1

VBA, 204 ไบต์

subroutine ที่ประกาศซึ่งจะไม่มีการป้อนข้อมูลและเมื่อทำงานสร้างpublicเข้าถึง Ly 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

Ungolfed และอธิบาย

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
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.