Kod Romanlaştırması


33

Buradaki zorluk, herhangi bir Romen rakamını seçtiğiniz dilde geçerli bir kod yapmaktır .

Onlar gerektiğini değil tıpkı diğer jeton gibi dizeleri içini veya benzer bir şey, ama işi görünür değişmezleri mesela ( Arapça ) sayılar, karakterler veya dizeleri; veya değişken / yöntem / işlev tanımlayıcıları vb.

Örneğin, Java’da, aşağıdakiler ibaşlatılmış gibi derlenip çalıştırılmalıdır 42:

int i = XLII;

Rakamların gerçek ayrıştırması ikincildir, yani isterseniz bir kütüphane kullanabilirsiniz, ancak bu bir popülerlik yarışmasıdır, bu nedenle yaratıcılık teşvik edilir.

Böyle bir şey varsa, aslında Roma rakamlarını kullanan hiçbir dili kullanamazsınız.

İyi şanslar.


1
Öyleyse, dile bir uzantı yazmalıyız, böylece yeni bir tane mi yaratmalıyız?
Kendall Frey

4
İstersem şikayet edeceğim, çünkü kullandığım diller bu kadar genişletilebilir değil, bu yüzden katılamam bile.
Kendall Frey

3
@KendallFrey Kaynak derlemek ve çalıştırmak zorunda kalacak. Java için, kaynağı düzenleyen bir "derleyici" yazabilir ve ardından programlı olarak derleyebilirsiniz . Böyle derleme bir yolu çalışan geçerProcess
Justin

1
Çoğu dilde sıkıcı görünüyor. Örneğin python'da sadece astkaynağı ayrıştırmak için kullanılan bir betik yazardım. AST'nin en üstüne 1 ila 3999 arası romen rakamlarının tanımını ekleyin. Her şeyi derleyin ve çalıştırın. Süreci ele almak için kodu yazmak sıkıcı.
Bakuriu

2
@Bakuriu ve yorumunuz çok sıkıcı. Bu bir popülerlik yarışması, bu yüzden eğlenceli bir şey bulmaya çalışmalısın. Bence burada daha yaratıcı olan (bir betik dili derlemekten çok) güzel cevaplar var.
daniero

Yanıtlar:


40

C

4000 ve daha üstü standart notasyonlara sahip olmadığından sadece Roma rakamları vardır ve önişlemci harika bir dekompresyon aracıdır, özellikle de kodun tanımsız davranışa sahip olması ile ilgili herhangi bir probleminiz yoksa.

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

Bu tüm Roma rakamlarını tanımlar Iiçin MMMCMXCIXnumaralandırma sabitler olarak, artı _sıfır olarak (istediğiniz herhangi bir şey ile değiştirilebilir olan).


12
Kesinlikle mükemmel, bir sonraki C sürümüne (C2X?) <Roman.h> olarak eklemeyi önerin! % R ve% R printf formatları ile!

3
Şimdiye kadar önişlemciyi nasıl kullanacağımı bildiğimi sanıyordum: | Cevabınızı bu enumun en az kullanım örneği ile güncelleyebilir misiniz?
klingt.net

@YiminRong Ve scanfçok :) @ klingt.net Ne tür bir örnek aradığınızdan emin değilim. Oldukça basit bir olurduint main() { return MMMCMXCIX - M - M - M - CM - XC - IX; }
hvd

Bu fikri sevdim, ancak tanımlanmış davranış oldukça basitken neden tanımsız davranışa girmeyi tercih ettiniz? Yargılamıyor, sadece merak ediyor musun?
CasaDeRobison

1
@CasaDeRobison Özellikle eğlence için. Bu hoşuma gitti çünkü ön işleme zamanında mevcut derleyiciler tarafından bir hata olarak işaretlenmeyen ve muhtemelen gelecekte olmayacak olan tanımlanmamış davranış durumlarından biri. Daha önce hiç böyle bir şeyi faydalı kodlara yazmıyorum, ancak buraya gönderilen kodun yararlı olması gerekmiyor, bu yüzden ne denemek için daha iyi bir fırsat var?
hvd

15

Yakut

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

Herhangi bir (büyük harf) Romen rakamları şimdi ondalık eşdeğerleri gibi ayrıştırılır. Tek sorun, hala atanabilir olmalarıdır: yapabilir X = 9, ancak yapamazsınız 10 = 9. Bunu düzeltmenin bir yolu olduğunu sanmıyorum.


1
Bu harika. Atama konusu bir sorun değildir; Her türlü aptalca şeyi yapmak için ödevleri kullanabilirsiniz.
daniero

12

JavaScript (ES6)

ProxyRomen rakamlarını yakalamak için kullanın .

JSFiddle'da Firefox'ta test edilebilir (en son sürüm) . Uygulama
bozulduğundan Chrome'da (Traceur ile) test edilemez 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
});

Kullanımı:

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

10

C & C ++ (Güncellenmiş Cevap)

Bir yorumda görüldüğü gibi, orijinal çözümümün iki sorunu vardı:

  1. İsteğe bağlı parametreler yalnızca C99 ve daha sonra dil ailesinin standartlarında bulunur.
  2. Enum tanımındaki izleyen virgül C99 ve sonrasına da özgüdür.

Kodumun eski platformlarda çalışabilmesi için olabildiğince jenerik olmasını istediğim için, başka bir bıçak almaya karar verdim. Daha öncekinden daha uzun, ancak C89 / C90 uyumluluk moduna ayarlanmış derleyiciler ve önişleyiciler üzerinde çalışıyor. Tüm makrolar, kaynak kodda uygun sayıda argüman iletilir, ancak bu makrolar hiçbir zaman "genişler".

Visual C ++ 2013 (aka sürüm 12) eksik parametrelerle ilgili uyarılar yayınlar, ancak ne mcpp (standarda uygun olduğunu iddia eden açık kaynak kodlu bir işlemci) ne de gcc 4.8.1 (-std = iso9899: 1990 -pedantik-hata anahtarları) etkili bir boş argüman listesi olan bu makro çağrıları için uyarılar veya hatalar.

İlgili standardın gözden geçirilmesinden sonra (ANSI / ISO 9899-1990, 6.8.3, Makro Değiştirme), bunun standart dışı kabul edilmemesi için yeterli belirsizlik olduğunu düşünüyorum. "İşlev benzeri bir makro çağırmadaki argümanların sayısı, makro tanımındaki parametre sayısı ile aynı olacaktır ...". Makroyu çağırmak için gerekli parantezler (ve çoklu parametreler durumunda virgüller) bulunduğu sürece boş bir argüman listesi bulunmuyor gibi görünmektedir.

Sondaki virgül sorununa gelince, bu numaralandırmaya fazladan bir tanımlayıcı ekleyerek çözülür (benim durumumda, MMMM, Roma rakamlı sıralamanın kabul edilen kurallarına uymasa bile, tanımlayıcı için 3999 izlemesi gereken herhangi bir şey kadar makul görünüyor. kesinlikle).

Biraz daha temiz bir çözüm, enum ve destek makrolarının başka bir yerde yorumlandığı gibi ayrı bir başlık dosyasına taşınmasını ve ad alanını kirletmemek için kullanıldıktan hemen sonra makro adlarının undef kullanılmasını içerecektir. Daha iyi makro isimleri de şüphesiz seçilmelidir, ancak bu eldeki görev için yeterlidir.

Güncellenen çözümüm, ardından özgün çözümüm:

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

Orijinal cevap (ilk altı yükseltme oyunu aldı, bu yüzden kimse bunu bir daha yenemezse, güncellenmiş çözümümün en iyi değeri aldığını düşünmemelisin):

Daha önceki bir cevapla aynı ruhta, ancak yalnızca tanımlanmış davranış kullanılarak taşınabilir olması gereken bir şekilde yapılmasına rağmen (farklı ortamlar her zaman önişlemcinin bazı yönleri üzerinde hemfikir değildir). Bazı parametreleri isteğe bağlı olarak ele alır, diğerlerini yok sayar, __VA_ARGS__makroyu desteklemeyen ön işlemciler üzerinde çalışmalıdır, C ++ dahil, dolaylı makroları kullanır ve parametrelerin token yapıştırılmasından önce genişlemesini sağlamak için dolaylı makrolar kullanır ve sonunda daha kısa olduğunu ve daha kolay okunacağını düşünüyorum ( yine de zor ve okunması kolay olmasa da, daha kolay):

#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, ancak boş makro argümanlarının ve bir sayıcı listesinin sonundaki virgül kullanımının hem C99 hem de C ++ 11 özelliklerinin yeni olduğunu ve hem C99 hem de C ++ 11'in destek verdiğini unutmayın __VA_ARGS__.
hvd

Eğer haklı değilsen Dang! Sanırım bu süre boyunca uzantı olarak kullanıldığını görüyorum. Ah iyi. :)
CasaDeRobison

8

Ortak Lisp

Aşağıda, bu şekilde kullanabileceğiniz bir makroyu nasıl yaptığımın oldukça uzun bir açıklaması yer almaktadır:

(roman-progn
  (+ XIV XXVIII))

Common Lisp'te bir makro çağrıldığında, temelde bir işlev gibi davranır, yalnızca argümanların değerlendirilmeden önce alındığını gösterir . Aslında, Common Lisp kodunda sadece veri olduğundan, aldığımız şey, istediğimiz her şeyi yapabileceğimiz, ayrıştırılmamış bir sözdizimi ağacını temsil eden (iç içe geçmiş) bir listedir ve derleme zamanında yapılır.

Yardımcı fonksiyonlar

Planın ilk adımı bu ağacı alıp, Romen Rakamları gibi görünen her şeyi taramak. Bu, Lisp ve diğerleri olarak, biraz işlevsel olarak yapmaya çalışalım: Bir ağacın derinlemesine bir geçişini yapacak ve sağlanan bir fonksiyonun searchpdoğru döndüğü her nesneyi döndürecek bir işleve ihtiyacımız var . Bu bile (yarı) kuyruk özyinelemeli.

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

Sonra Roma rakamlarını ayrıştırmak için bazı kodlar, Rosetta Kodu'nun izniyle :

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

Gerçek makro

Sözdizimi ağacını ( body) alıyoruz, derinlemesine-bulma prosedürümüzle araştırıyoruz ve bir şekilde bulduğumuz romen rakamlarını kullanıyoruz.

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

Peki nedir 1 + 2 + 3 + (4 * (5 + 6)) + 7?

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

Makro çağrıldığında gerçekte ne olduğunu görmek için:

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

Genel tablo için sadece bir yedek __index. Gsub kullanarak yapılan gerçek dönüşüm, tahmin ettiğimden çok daha güzel çıktı.


5

dipnot

C'yi izlemeye çalıştım ama anlamadım. Böylece bu şekilde yaptım:

/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 yok enumama sıralı tamsayı değerlerine sahip bir sözlük oluşturabilir ve bunları bir diziye katlayabiliriz. Bu, 4 iç içe döngüde birleştirilerek yapılan tüm dizileri sırayla oluşturma problemini azaltır. Böylece tüm dizeleri oluşturur, ardından her dizgiyi artan bir sayaç değeriyle birleştirir, böylece bir sözlük nesnesi üretmek için ... içine sarılmış yığın üzerinde uzun bir dizi <string> <int> çifti oluşur .<<>>

Program, Roma rakamları için tüm isimleri karşılık gelen değerlerine eşleyen bir sözlük oluşturur ve kurar. Bu nedenle kaynak metindeki adlardan söz etmek, otomatik ad aramasını başlatır ve yığında tamsayı değerini verir.

II IV MC pstack

baskılar

2
4
600

4

Küçük Konuşma (Küçük konuşma / X) (87/101 karakter)

Elbette, ayrıştırıcının belirleyicisini kolayca değiştirebildik (sınıf kütüphanesinin bir parçası olduğu ve değişime açık ve her zaman mevcut olduğu gibi), ancak zorluk sadece belirli bir bağlamdaki değerlendirmeleri etkilemektir; sistem her zamanki gibi çalışır.

Versiyon 1:

Değerlendirme ad alanında bir dizi değişken tanımlayın. Bu, etkileşimli alanları etkileyecektir (aka evals):

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

o zaman yapabilirim (doIt, fakat derlenmiş kodda değil):

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

-> 2019

Dikkat: 101 karakter boşluk içeriyor; Aslında 87 karakterle yapılabilir.
Ayrıca, genel Smalltalk ad alanında tanımlandığında, bu sabitleri de derlenmiş kodda göreceğimi dikkat edin.

Versiyon 2:

Var olan herhangi bir kodun derlenmeden sarılmasını sağlayan bir methodWrapper kancası kullanın. Aşağıdaki, Parser'in belirtecini taranacak bir roman tanımlayıcısının aranmasını sağlar ve bir tamsayı yapar. İşin zor yanı, çağıran bağlamın Roma imparatorluğundan olup olmadığını dinamik olarak saptamaktır. Bu, bir sorgu sinyali kullanılarak yapılır (teknik olarak kabul edilebilir bir istisnadır):

sorguyu tanımla:

InRomanScope := QuerySignal new defaultAnswer:false.

Bu nedenle, herhangi bir zamanda ("InRomanScope query") öntanımlı olarak yanlışlanmasını isteyebiliriz.

Ardından tarayıcının checkIdentifier yöntemini kaydırın:

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

Artık Roma imparatorluğunda olmadıkça tarayıcı her zamanki gibi çalışıyor:

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

-> 2525

Hatta kodu derleyebiliriz:

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

iyi deneme; ancak bu bir sözdizimi hatasıyla başarısız olur (tam olarak istediğimiz şey). Bununla birlikte, Roma İmparatorluğu’nda derleriz:

InRomanScope answer:true do:[

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

ve şimdi, Roma'nın içinden ve dışından herhangi bir tamsayı (bu mesajı göndererek) sorabiliriz:

(1000 factorial) inTheYear2525

-> 2525


Smalltalk'ı görmek güzel!

4

Şablon Haskell ve romen rakamlarında meta-programlama kullanarak 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, inşaatçılar için büyük harflerle başlayan tanımlayıcıları saklıyor, ben de küçük harf kullandım.


4

J - 78 karakter

Bu, diğer çözümlerde olduğu gibi, yalnızca MMMCMXCIX = 3999'a kadar gider.

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

Parçalama (parantez içinde olmadıkça J hatırlama genellikle sağdan sola okunur):

  • M`CDM`XLC`IVX- Dört kutu mektup. Sayısal dizileri bu harflere endekslemek için kullanacağız ve Roma rakamlarının alt kelimelerini oluşturacağız.
  • 841,3#79bc5yuukh - Bu, sıkıca kodlanan sayısal veridir. *
  • (_1,~3#.inv]) - Bu, üçlü olarak genişleyerek ve -1 ekleyerek yukarıdaki verilerin kodunu çözecektir.
  • ('';&;:(...){' ',[)&.>- Soldaki sayıları sağdaki kutularla ( &.>) eşleştirin, sayı dizilerini çözün ve harfleri dizine almak için kullanın. Mektup listelerine bir boşluk karakteri hazırlayarak 0'ı boşluk olarak kabul ediyoruz. Bu prosedür I II III IV V VI VII VIII IXve gibi kelimelerin bir listesini oluşturur M MM MMM.
  • {- Bu dört kutunun Kartezyen ürününü kelimelerle dolu olarak alın. Şimdi tüm Romen rakamlarından oluşan bir 4D dizisine sahibiz.
  • }.,;L:1- Bunları tek bir 1B Romen rakamı listesine sokun ve öndeki boş dizgiyi kaldırın çünkü bir hata yaratacaktır. ( L:J golfünde nadir görülen bir manzara! Genellikle bu kadar fazla boks seviyesi yoktur.)
  • }.i.4e3- Bitiş noktaları hariç , 0 ile 4000 arasında tamsayılar .
  • Sonunda, küresel bir görevle her şeyi bir araya getirdik =:. J, hesaplanan çoklu atama şekli olarak LHS'de kutulu bir isim listesine sahip olmanıza izin verir, bu da iyi sonuç verir.

Şimdi J ad alanı, Romen rakamlarını temsil eden değişkenlerle doludur.

   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 numarasının daha sonra 3. basamakta okunmasına ihtiyacım var. Üstelik, üssünde 79'dan 30'dan büyük olmayan rakamları kullanarak ifade edebiliyorum, bu iyi, çünkü J sadece 35'e kadar olan rakamları anlayabilir (0-9 ve sonra az). Bu işlem ondalık basamağa göre 3 karakter kaydeder.


3

piton

Fikir diğer cevaplar gibi basittir. Ancak, sadece temiz olmak ve küresel isim alanını kirletmemek için bir içerik yöneticisi kullanılır. Bu aynı zamanda, daha önce kullanmayı planladığınız Romalı nümerik seviyesini bildirmeniz gereken kısıtlamayı da getirir.

Not Sadece basit tutmak ve tekerleği yeniden icat etmemek için Roma piton paketini kullandım.

uygulama

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)

gösteri

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

piton

Bu muhtemelen Python kullanan en basit çözümdür:

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
Kullanmak globals()[var] = valuedaha iyidir exec().
Ramchandra Apte

3

D

D'nin derleme zamanı fonksiyon değerlendirmesini kullanarak

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 bayt

Romen rakamı maksimum uzunluğunu ister ve tüm değişkenleri tanımlar.

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

t←t alır

'IVXLCDM', Roma karakterleri

 kapalı

 boş liste

t[] İndeksini t ile…

 devredilen (doğru siparişi almak için)

8⊥⍣¯1 uygun genişlik taban-sekiz gösterimi

 İlk n indisleri, n, bir

¯1+ bir daha az

8*⎕ sayısal girişin gücüne sekiz

,/ düzleştirilmiş satırlar (her gösterim)

{ Her temsil için aşağıdaki isimsiz işlevi uygulayın…

()[t⍳⍵] Argüman öğelerinin t içindeki konumlarına karşılık gelir, … arasından seçim yapın

   kayıtlı

  1 5∘ר her birinin bir ve beş katı

  10* gücü on

  ⍳4 sıfırdan üçe

0,⍨ sıfır eklemek

2(…)/ her iki uzunlukta sürgülü pencerede, aşağıdaki anonim işlev treni uygulayın…

  ⊣× sol argüman zamanları

  ¯1* gücüne olumsuz olanı

  < sol argümanın sağ argümandan az olup olmadığı

+/ toplam

⍵'←', Argümanı (Romen rakamı) ve ödev oku

 format (sayıyı düzleştirmek ve metne dönüştürmek için)

 Bunu yürüt (atamayı anonim işlev dışında yapar)

Çevrimiçi deneyin! (maksimum uzunluk 5 kullanarak)


2

PHP

Geçerli roman numaraları için birkaç kural var.

  1. En düşük değeri en büyük değere yazın

  2. Sadece bir [I,X,C]sonraki 2 büyük değerden önce çıkarın

  3. [I,X,C]Sonraki 2 büyük değerden önce iki katı çıkarın

  4. Daha [I,X,C]büyük değerlerden önce iki katı çıkarın

  5. 4 + 5 birleştir

Çevrimiçi sürüm

1. Adım Kuralları oluşturun

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

geçerli tüm roman numaraları için JSON çıktısıdır

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

Adım 2 3999'a kadar tüm kuralların listesini yapın.

$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. Adım Sabitler oluşturma

Tüm listeleri birleştirin ve sabitleri tanımlayın

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

Çıktı

Örnekte mutiply 8 sayısının iki geçerli sürümü

echo IIX *  VIII;

Güzel, ama bunu nasıl kullanırım? PHP'de akıcı değilim; Lütfen kodda Romen rakamları yazmamı nasıl sağladığına bir örnek verebilir misiniz?
daniero

@Daniero Şimdi kod çalışması gerekir
Jörg Hülsermann

Ah, bu daha iyi :)
daniero

1

Rebol'un

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


Örnek

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

Çıktı:

15

1015

Evet 2014

1 + 10 = 11

2 + 10 = 12

3 + 10 = 13

4 + 10 = 14

5 + 10 = 15


Feragatname: Rebol'da da bunu yapmanın başka (ve muhtemelen daha iyi!) Yolları olduğuna eminim.

PS. Benim işlevimroman-to-integer , Romen Rakamı dizesini bir sayıya dönüştürmek için histokratın güzel Ruby algoritmasının çevirisidir . Teşekkürler ile döndü! +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

Bu, global tablonun metaforunu etkileyerek yeni bir indeks fonksiyonu verir. Örneğin XVII, sadece romen rakamları içeren global bir değişken istendiğinde , onu ayrıştırır.

Test etmek kolay;

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

Çevrimiçi deneyin!


1

VBA, 204 bayt

Girdi almayan ve çalıştırıldığında, tüm Romen Rakamı değerlerini içeren publicly erişilebilir Enum, bildirilmiş bir alt yordam oluşturur R. Bu değerler Enum referans alınmadan doğrudan kullanılabilir.

Enum tutma değerleri 1 ila 3999 arasındadır.

Not:" 3 ve 7 satırlarındaki Terminal ler yalnızca sözdizimi vurgulaması için eklenmiştir ve bayt sayımına katkıda bulunmaz.

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 ve Açıklaması

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
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.