Kode Romanisasi

33

Tantangannya adalah untuk membuat kode angka Romawi yang valid dalam bahasa yang Anda pilih.

Mereka seharusnya tidak muncul di dalam string atau sesuatu yang serupa, tetapi berfungsi seperti token lainnya, literal seperti angka, karakter atau string ( Arab ); atau pengenal variabel / metode / fungsi, dll.

Misalnya, di Jawa, berikut ini harus dikompilasi dan dijalankan seperti jika idiinisialisasi ke 42:

int i = XLII;

Penguraian angka yang sebenarnya adalah sekunder, sehingga Anda dapat menggunakan perpustakaan jika Anda mau, tetapi ini adalah kontes popularitas, jadi kreativitas dianjurkan.

Anda tidak dapat menggunakan bahasa apa pun yang benar-benar menggunakan angka romawi, jika ada hal seperti itu.

Semoga berhasil.

daniero
sumber
1
Jadi, kita perlu menulis ekstensi ke bahasa, sehingga membuat yang baru?
Kendall Frey
4
Saya akan mengeluh jika saya mau, karena bahasa yang saya gunakan tidak bisa diperluas seperti itu, jadi saya bahkan tidak bisa berpartisipasi.
Kendall Frey
3
@KendallFrey Sumber harus mengkompilasi dan menjalankan. Untuk Java, Anda bisa menulis "kompiler" yang mengedit sumbernya, lalu mengkompilasi secara program . Salah satu cara kompilasi tersebut adalah dengan menjalankanProcess
Justin
1
Tampaknya membosankan di sebagian besar bahasa. Sebagai contoh dalam python saya hanya akan menulis skrip yang digunakan astuntuk mem-parsing sumber. Masukkan di bagian atas AST definisi angka romawi dari 1 hingga 3999. Kompilasi semuanya dan jalankan. Membosankan hanya menulis kode untuk menangani proses.
Bakuriu
2
@ Bakuriu dan komentar Anda juga membosankan. Ini adalah kontes popularitas, jadi Anda harus mencoba membuat sesuatu yang menyenangkan. Saya pikir ada beberapa jawaban yang bagus di sini yang lebih imajinatif (daripada menyusun bahasa scripting).
daniero

Jawaban:

40

C

Hanya ada begitu banyak angka Romawi, karena 4000 dan lebih tinggi tidak memiliki notasi standar, dan preprosesor adalah alat dekompresi yang luar biasa, terutama jika Anda tidak memiliki masalah dengan fakta bahwa kode memiliki perilaku yang tidak terdefinisi.

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

Ini mendefinisikan semua angka Romawi dari Ihingga MMMCMXCIXsebagai konstanta enumerasi, ditambah _(yang dapat diganti dengan apa pun yang Anda suka) sebagai nol.

hvd
sumber
12
Benar-benar brilian, usulkan menambahkannya ke rilis C berikutnya (C2X?) Sebagai <roman.h>! Dengan format% r dan% R printf!
3
Saya pikir saya tahu cara menggunakan preprocessor, sampai sekarang: | Bisakah Anda memperbarui jawaban Anda dengan contoh penggunaan minimal enum ini?
klingt.net
@YiminRong Dan scanfjuga :) @ klingt.net Saya tidak yakin contoh seperti apa yang Anda cari. Yang cukup sederhana adalahint main() { return MMMCMXCIX - M - M - M - CM - XC - IX; }
hvd
Saya suka ide itu, tetapi mengapa Anda memilih untuk pergi dengan perilaku yang tidak terdefinisi ketika perilaku yang didefinisikan cukup sederhana? Tidak menghakimi, hanya ingin tahu?
CasaDeRobison
1
@CasaDeRobison Untuk bersenang-senang, terutama. Saya suka karena ini adalah salah satu dari sedikit kasus perilaku tidak terdefinisi pada waktu preprocessing yang tidak ditandai sebagai kesalahan oleh kompiler saat ini, dan kemungkinan tidak akan di masa depan. Saya tidak pernah menulis hal seperti itu dalam kode yang dimaksudkan untuk berguna, tetapi kode yang diposting di sini tidak dimaksudkan untuk menjadi berguna, jadi kesempatan apa yang lebih baik untuk mencobanya?
hvd
15

Rubi

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

Setiap angka Romawi (huruf besar) sekarang akan diuraikan seperti ekivalen desimalnya. Satu-satunya masalah adalah bahwa mereka masih ditugaskan: Anda dapat melakukannya X = 9, tetapi tidak 10 = 9. Saya tidak berpikir ada cara untuk memperbaikinya.

histokrat
sumber
1
Ini sempurna. Masalah penugasan bukan masalah; Anda dapat menggunakan tugas untuk melakukan segala macam hal bodoh.
daniero
12

JavaScript (ES6)

Gunakan Proxyuntuk menangkap angka romawi.

Dapat diuji di Firefox (terbaru) di JSFiddle .
Tidak dapat diuji di Chrome (dengan Traceur) karena Proxyimplementasi rusak.

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

Pemakaian:

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

C & C ++ (Jawaban Diperbarui)

Seperti yang diamati dalam komentar, solusi asli saya memiliki dua masalah:

  1. Parameter opsional hanya tersedia dalam C99 dan standar selanjutnya dari rumpun bahasa.
  2. Trailing koma dalam definisi enum juga khusus untuk C99 dan yang lebih baru.

Karena saya ingin kode saya menjadi generik mungkin untuk bekerja pada platform lama, saya memutuskan untuk mengambil bacokan lain. Ini lebih lama dari sebelumnya, tetapi bekerja pada kompiler dan preprosesor yang diatur ke mode kompatibilitas C89 / C90. Semua makro diberikan sejumlah argumen yang sesuai dalam kode sumber, meskipun terkadang makro itu "tidak berkembang".

Visual C ++ 2013 (alias versi 12) memancarkan peringatan tentang parameter yang hilang, tetapi tidak mcpp (preprocessor open source yang mengklaim kepatuhan tinggi dengan standar) atau gcc 4.8.1 (dengan -std = iso9899: 1990 -pedantic-errors switches) memancarkan peringatan atau kesalahan untuk doa makro dengan daftar argumen kosong yang efektif.

Setelah meninjau standar yang relevan (ANSI / ISO 9899-1990, 6.8.3, Penggantian Makro), saya pikir ada ambiguitas yang cukup bahwa ini tidak boleh dianggap non-standar. "Jumlah argumen dalam doa makro seperti fungsi harus setuju dengan jumlah parameter dalam definisi makro ...". Tampaknya tidak menghalangi daftar argumen kosong selama tanda kurung yang diperlukan (dan koma dalam kasus beberapa parameter) tersedia untuk menjalankan makro

Adapun masalah trailing koma, yang diselesaikan dengan menambahkan pengenal ekstra untuk enumerasi (dalam kasus saya, MMMM yang tampaknya masuk akal seperti apa pun untuk pengidentifikasi untuk mengikuti 3999 bahkan jika itu tidak mematuhi aturan yang diterima dari pengurutan angka Romawi. persis).

Solusi yang sedikit lebih bersih akan melibatkan memindahkan enum dan mendukung makro ke file header terpisah seperti yang tersirat dalam komentar di tempat lain, dan menggunakan undef dari nama makro segera setelah mereka digunakan untuk menghindari polusi namespace. Nama-nama makro yang lebih baik tidak diragukan lagi harus dipilih juga, tetapi ini cukup untuk tugas yang ada.

Solusi saya yang diperbarui, diikuti oleh solusi asli saya:

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

Jawaban asli (yang menerima enam upvotes pertama, jadi jika tidak ada yang pernah memperbaiki ini lagi, Anda seharusnya tidak berpikir solusi saya yang diperbarui mendapat upvotes):

Dengan semangat yang sama dengan jawaban sebelumnya, tetapi dilakukan dengan cara yang seharusnya portabel hanya menggunakan perilaku yang didefinisikan (meskipun lingkungan yang berbeda tidak selalu setuju pada beberapa aspek preprosesor). Memperlakukan beberapa parameter sebagai opsional, mengabaikan yang lain, itu harus bekerja pada preprosesor yang tidak mendukung __VA_ARGS__makro, termasuk C ++, menggunakan makro tidak langsung untuk memastikan parameter diperluas sebelum menempelkan token, dan akhirnya lebih pendek dan saya pikir lebih mudah dibaca ( meskipun masih sulit dan mungkin tidak mudah dibaca, hanya saja lebih mudah):

#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 };
CasaDeRobison
sumber
1
+1, tetapi perhatikan bahwa penggunaan argumen makro kosong, dan koma di akhir daftar enumerator, keduanya adalah fitur baru C99 dan C ++ 11, dan C99 dan C ++ 11 mendukung __VA_ARGS__.
hvd
Sial kalau kamu tidak benar! Saya kira saya telah melihat ini digunakan sebagai ekstensi selama ini. Baiklah :)
CasaDeRobison
8

Gangguan umum

Berikut ini adalah penjelasan yang agak panjang tentang bagaimana saya membuat makro yang dapat Anda gunakan seperti ini:

(roman-progn
  (+ XIV XXVIII))

Ketika makro disebut dalam Common Lisp, pada dasarnya ia bertindak seperti fungsi, hanya argumen yang diterima sebelum dievaluasi. Sebenarnya, karena dalam kode Common Lisp hanya data, apa yang kami terima adalah daftar (bersarang) yang mewakili pohon sintaksis yang tidak diuraikan yang dapat kami lakukan dengan apa pun yang kami inginkan, dan itu dilakukan dalam waktu kompilasi.

Fungsi pembantu

Langkah pertama dari rencana ini adalah mengambil pohon ini dan memindai untuk apa pun yang tampak seperti Angka Romawi. Ini adalah Lisp dan semuanya, mari kita coba untuk melakukannya secara fungsional: Kita memerlukan fungsi yang akan melakukan traversal yang dalam dari sebuah pohon dan mengembalikan setiap objek yang fungsi yang disediakannya searchpmengembalikan true. Yang ini bahkan (semi) rekursif.

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

Kemudian beberapa kode untuk menguraikan angka romawi, milik 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)))

Makro yang sebenarnya

Kami mengambil pohon sintaks ( body), mencari dengan prosedur deep-find-all kami dan entah bagaimana membuat angka romawi yang kami temukan tersedia.

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

Jadi apa 1 + 2 + 3 + (4 * (5 + 6)) + 7?

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

Dan untuk melihat apa yang sebenarnya terjadi ketika makro dipanggil:

(macroexpand-1 +)
=> (LET ((VII 7) (VI 6) (V 5) (IV 4) (III 3) (II 2) (I 1))
   (+ I II III (* IV (+ V VI)) VII))
daniero
sumber
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
})

Cukup mundur __index untuk tabel global. Konversi yang sebenarnya menggunakan gsub ternyata jauh lebih cantik daripada yang saya bayangkan.

mniip
sumber
5

Nota bene

Saya mencoba mengikuti yang C tetapi saya tidak memahaminya. Jadi saya melakukannya dengan cara ini:

/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 tidak memiliki enumtetapi kita dapat membuat kamus dengan nilai integer berurutan dan melipatnya menjadi sebuah array. Ini mengurangi masalah untuk menghasilkan semua string secara berurutan, yang dilakukan dengan menggabungkan dalam 4 loop bersarang. Jadi itu menghasilkan semua string, lalu interleave setiap string dengan nilai penghitung yang meningkat, menghasilkan serangkaian panjang <string> <int> pasangan pada tumpukan yang dibungkus <<... >>untuk menghasilkan objek kamus.

Program ini membangun dan menginstal kamus yang memetakan semua nama untuk angka romawi ke nilai yang sesuai. Jadi menyebutkan nama-nama dalam teks sumber memanggil pencarian nama otomatis dan menghasilkan nilai integer pada stack.

II IV MC pstack

cetakan

2
4
600
luser droog
sumber
4

Smalltalk (Smalltalk / X) (87/101 karakter)

tentu saja kita dapat dengan mudah memodifikasi tokenizer parser (karena itu adalah bagian dari perpustakaan kelas, dan karena itu terbuka untuk modifikasi, dan selalu ada), tetapi tantangannya adalah untuk mempengaruhi hanya evaluasi dalam konteks yang diberikan, sehingga sisa sistem bekerja seperti biasa.

Versi 1:

mendefinisikan sejumlah variabel dalam namespace evaluasi. Jadi ini akan memengaruhi doIts interaktif (alias evals):

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

maka saya bisa melakukan (dalam doIt, tetapi tidak dalam kode yang dikompilasi):

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

-> 2019

Perhatikan: 101 karakter mencakup spasi; sebenarnya itu bisa dilakukan dengan 87 karakter.
Juga perhatikan, ketika mendefinisikan dalam namespace Smalltalk global, saya akan melihat konstanta-konstanta itu juga dalam kode yang dikompilasi.

Versi 2:

Gunakan hook methodWrapper, yang memungkinkan kode apa pun yang ada untuk dibungkus tanpa kompilasi ulang. Berikut ini membungkus tokenizer Parser untuk mencari pengidentifikasi roman yang akan dipindai dan menjadikannya bilangan bulat. Bagian yang sulit adalah mendeteksi secara dinamis apakah konteks panggilan berasal dari kekaisaran Romawi atau tidak. Ini dilakukan dengan menggunakan sinyal kueri (yang secara teknis merupakan pengecualian yang dapat diproses):

tentukan kueri:

InRomanScope := QuerySignal new defaultAnswer:false.

Jadi kita dapat meminta kapan saja ("permintaan InRomanScope") untuk mendapatkan false secara default.

Kemudian bungkus metode checkIdentifier pemindai:

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

Sekarang pemindai berfungsi seperti biasa, kecuali kita berada di kekaisaran Romawi:

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

-> 2525

kita bahkan dapat mengkompilasi kode:

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

usaha yang bagus; tetapi ini gagal dengan kesalahan sintaks (yang persis seperti yang kita inginkan). Namun, di kekaisaran Romawi, kita BISA mengkompilasi:

InRomanScope answer:true do:[

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

dan sekarang, kita dapat meminta bilangan bulat apa pun (mengirim pesan itu) dari dalam dan luar Roma:

(1000 factorial) inTheYear2525

-> 2525

blabla999
sumber
Senang melihat Smalltalk!
4

Haskell, menggunakan meta-programming di Template Haskell dan roman-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

Pengidentifikasi cadangan Haskell dimulai dengan huruf besar untuk konstruktor, jadi saya menggunakan huruf kecil.

Petr Pudlák
sumber
4

J - 78 char

Ini hanya naik ke MMMCMXCIX = 3999, seperti solusi lainnya.

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

Memecahnya (ingat J biasanya dibaca dari kanan ke kiri, kecuali digantikan oleh tanda kurung):

  • M`CDM`XLC`IVX- Empat kotak surat. Kita akan menggunakan array numerik ke dalam indeks ke dalam surat-surat ini dan membangun subword dari angka Romawi.
  • 841,3#79bc5yuukh - Ini adalah data numerik, yang dikodekan dengan ketat. *
  • (_1,~3#.inv]) - Ini akan memecahkan kode data di atas, dengan memperluas di ternary dan menambahkan -1.
  • ('';&;:(...){' ',[)&.>- Memasangkan nomor di sebelah kiri dengan kotak di sebelah kanan ( &.>), mendekode array angka dan menggunakannya untuk mengindeks ke dalam huruf-huruf. Kami memperlakukan 0 sebagai spasi dengan menambahkan karakter spasi ke daftar surat. Prosedur ini membangun daftar kata-kata seperti I II III IV V VI VII VIII IXdan M MM MMM.
  • {- Ambil produk Cartesian dari empat kotak ini penuh dengan kata-kata. Sekarang kita memiliki array 4D dari semua angka Romawi.
  • }.,;L:1- Jalankan semua itu ke daftar angka Romawi 1D tunggal, dan hapus string kosong di depan karena itu akan membuat kesalahan. ( L:Adalah pemandangan langka di J golf! Biasanya tidak banyak level tinju yang terlibat.)
  • }.i.4e3- Bilangan bulat dari 0 hingga 4000, tidak termasuk titik akhir.
  • Akhirnya, kami menggabungkan semuanya dengan tugas global =:. J memungkinkan Anda memiliki daftar nama kotak pada LHS, sebagai bentuk penugasan berganda yang dikomputasi, jadi ini berfungsi dengan baik.

Sekarang namespace J penuh dengan variabel yang mewakili angka Romawi.

   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

* Saya memerlukan nomor 2933774030998 untuk kemudian dibaca di base 3. Kebetulan saya bisa mengekspresikannya di base 79 menggunakan angka tidak lebih dari 30, yang bagus karena J hanya bisa memahami angka hingga 35 (0-9 dan kemudian az). Ini menghemat 3 karakter lebih dari desimal.

algoritme hiu
sumber
3

Python

Ide itu sederhana seperti jawaban lainnya. Tapi Hanya agar rapi dan tidak mencemari namespace global, manajer konteks digunakan. Ini juga memberlakukan batasan, bahwa Anda harus menyatakan sebelumnya, sejauh mana angka Romawi yang Anda rencanakan untuk digunakan.

Catatan Hanya untuk membuatnya tetap sederhana, dan bukan untuk menemukan kembali roda, saya telah menggunakan paket python Romawi

Pelaksanaan

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)

Demo

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
Abhijit
sumber
3

Python

Ini mungkin solusi paling sederhana menggunakan 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
Dhara
sumber
3
Lebih baik digunakan globals()[var] = valuedaripada exec().
Ramchandra Apte
3

D

menggunakan evaluasi fungsi waktu kompilasi 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());
ratchet freak
sumber
3

APL (Dyalog APL) , 77 byte

Meminta panjang maksimum angka Romawi dan mendefinisikan semua variabel.

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

t←t mendapat

'IVXLCDM', Karakter Romawi diikuti oleh

 sebuah tertutup

 daftar kosong

t[... ] indeks t dengan ...

 yang dialihkan (untuk mendapatkan urutan yang benar)

8⊥⍣¯1 lebar yang sesuai basis-delapan representasi

indeks n  pertama , di mana n adalah

¯1+ satu kurang dari

8*⎕ delapan pangkat input numerik

,/ ratakan baris (setiap representasi)

{...  terapkan fungsi anonim berikut pada setiap representasi ...

(... )[t⍳⍵] sesuai dengan posisi item argumen di t , pilih dari ...

   yang terdaftar

  1 5∘ר masing-masing satu dan lima kali

  10* sepuluh pangkat dari

  ⍳4 nol hingga tiga

0,⍨ tambahkan nol

2(…)/ pada setiap jendela geser panjang-panjang, terapkan fungsi kereta anonim berikut ...

  ⊣× kali argumen kiri

  ¯1* negatif terhadap kekuatan

  < apakah argumen kiri kurang dari argumen kanan

+/ jumlah

⍵'←', tambahkan argumen (angka Romawi) dan panah tugas

 format (untuk meratakan dan mengonversi angka menjadi teks)

 jalankan itu (membuat tugas di luar fungsi anonim)

Cobalah online! (menggunakan max-length 5)

Adm
sumber
2

PHP

Ada beberapa aturan untuk angka romawi yang valid

  1. Tulis nilai terbesar sebelum nilai yang lebih rendah

  2. Kurangi hanya [I,X,C]sebelum 2 nilai berikutnya yang lebih besar

  3. Kurangi dua kali lipat [I,X,C]sebelum 2 nilai selanjutnya yang lebih besar

  4. Kurangi dua kali [I,X,C]sebelum nilai yang lebih besar

  5. Gabungkan 4 + 5

Versi Online

Langkah 1 Buat aturan

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

adalah output JSON untuk semua angka romawi yang valid

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

Langkah 2 Buat daftar untuk semua aturan hingga 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;
}

Langkah 3 Buat konstanta

Gabungkan semua daftar dan tetapkan konstanta

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

Keluaran

Dalam contoh, gonta-ganti dua versi nomor 8 yang valid

echo IIX *  VIII;
Jörg Hülsermann
sumber
Bagus, tapi bagaimana saya menggunakan ini? Saya tidak lancar berbahasa PHP; Bisakah Anda memberi contoh bagaimana ini memungkinkan saya untuk menulis angka Romawi dalam kode?
daniero
@Daniero Sekarang kodenya harusnya berfungsi
Jörg Hülsermann
Ah, itu lebih baik :)
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]
]


Contoh

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

Keluaran:

15

1015

Ya itu 2014

1 + 10 = 11

2 + 10 = 12

3 + 10 = 13

4 + 10 = 14

5 + 10 = 15


Penafian: Saya yakin ada cara lain (dan mungkin lebih baik!) Untuk melakukan ini di Rebol juga.

PS. roman-to-integerFungsi saya adalah transliterasi dari algoritma Ruby yang bagus milik histokrat untuk mengubah string Angka Romawi menjadi angka. Kembali dengan terima kasih! +1

draegtun
sumber
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

Ini efek metatable dari tabel global, memberikannya fungsi indeks baru. Ketika variabel global yang hanya berisi angka romawi diminta, misalnya XVII, ia menguraikannya.

Mudah diuji;

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

Cobalah online!

ATaco
sumber
1

VBA, 204 byte

Sebuah menyatakan subroutine yang tidak membutuhkan input, dan ketika dijalankan, menciptakan publicly diakses Enum, Ryang berisi semua nilai-nilai Romawi Angka. Nilai-nilai ini dapat digunakan secara langsung, tanpa merujuk pada Enum.

Enum memegang nilai dari 1 hingga 3999.

Catatan: Terminal "pada baris 3 dan 7 disertakan hanya untuk penyorotan sintaks, dan tidak berkontribusi pada 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

Tidak Diikat dan Dijelaskan

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
Taylor Scott
sumber