Match Roman Numerals












15












$begingroup$


Challenge



Given some input string, return a truthy value if it represents a correct roman numeral between 1 (=I) and 3999 (=MMMCMXCIX), and a falsey value otherwise.



Details




  • The input is a non-empty string that only comprises the characters IVXLCDM.

  • The roman numerals (that we use here in this challenge) are defined as follows:


We use only following symbols:



Symbol  I   V   X   L   C   D    M
Value 1 5 10 50 100 500 1000


To define which strings are actually valid roman numerals, it is probably easiest to provide the rule of conversation: To write a decimal number a3 a2 a1 a0 (where each ai represents one digit. So for example to represent 792 we have a3=0, a2=7, a1=9, a0=2.) as a roman numeral, we decompose it into the power of tens. The different powers of ten can be written as follows:



      1-9: I, II, III, IV, V, VI, VII, VIII, IX
10-90: X, XX, XXX, XL, L, LX, LXX, LXXX, XC
100-900: C, CC, CCC, CD, D, DC, DCC, DCCC, CM
1000-3000: M, MM, MMM


Beginning at the left side with the most significant digit of the, we can convert the number that each digit represents separately and concatenate them. So for the example from above this would look like so:



Digit        a3    a2   a1   a0
Decimal 0 7 9 2
Roman DCC XC II


Therefore the roman numeral for 792 is DCCXCII. Here is a full list of all roman numerals that are relevant for this challenge: OEIS a006968.txt



Examples



Truthy



MCCXXXIV (1234)
CMLXXXVIII (988)
DXIV (514)
CI (101)


Falsey



MMIXVIII
IVX
IXV
MMMM
XXXVX
IVI
VIV











share|improve this question











$endgroup$












  • $begingroup$
    Subset of this conversion challenge.
    $endgroup$
    – Shaggy
    yesterday










  • $begingroup$
    I still don't think this qualifies as a "subset" as the set of invalid inputs is larger. This challenge here only refers to the "well"-defined numbers that are used in OEIS A006968
    $endgroup$
    – flawr
    yesterday












  • $begingroup$
    Why is MMMM invalid? Is there a letter for 5000 that should be used instead for M<letter>?
    $endgroup$
    – Skyler
    13 hours ago










  • $begingroup$
    Check out the specs, there is no such letter. The only symbols used are I,V,X,L,C,D,M.
    $endgroup$
    – flawr
    12 hours ago


















15












$begingroup$


Challenge



Given some input string, return a truthy value if it represents a correct roman numeral between 1 (=I) and 3999 (=MMMCMXCIX), and a falsey value otherwise.



Details




  • The input is a non-empty string that only comprises the characters IVXLCDM.

  • The roman numerals (that we use here in this challenge) are defined as follows:


We use only following symbols:



Symbol  I   V   X   L   C   D    M
Value 1 5 10 50 100 500 1000


To define which strings are actually valid roman numerals, it is probably easiest to provide the rule of conversation: To write a decimal number a3 a2 a1 a0 (where each ai represents one digit. So for example to represent 792 we have a3=0, a2=7, a1=9, a0=2.) as a roman numeral, we decompose it into the power of tens. The different powers of ten can be written as follows:



      1-9: I, II, III, IV, V, VI, VII, VIII, IX
10-90: X, XX, XXX, XL, L, LX, LXX, LXXX, XC
100-900: C, CC, CCC, CD, D, DC, DCC, DCCC, CM
1000-3000: M, MM, MMM


Beginning at the left side with the most significant digit of the, we can convert the number that each digit represents separately and concatenate them. So for the example from above this would look like so:



Digit        a3    a2   a1   a0
Decimal 0 7 9 2
Roman DCC XC II


Therefore the roman numeral for 792 is DCCXCII. Here is a full list of all roman numerals that are relevant for this challenge: OEIS a006968.txt



Examples



Truthy



MCCXXXIV (1234)
CMLXXXVIII (988)
DXIV (514)
CI (101)


Falsey



MMIXVIII
IVX
IXV
MMMM
XXXVX
IVI
VIV











share|improve this question











$endgroup$












  • $begingroup$
    Subset of this conversion challenge.
    $endgroup$
    – Shaggy
    yesterday










  • $begingroup$
    I still don't think this qualifies as a "subset" as the set of invalid inputs is larger. This challenge here only refers to the "well"-defined numbers that are used in OEIS A006968
    $endgroup$
    – flawr
    yesterday












  • $begingroup$
    Why is MMMM invalid? Is there a letter for 5000 that should be used instead for M<letter>?
    $endgroup$
    – Skyler
    13 hours ago










  • $begingroup$
    Check out the specs, there is no such letter. The only symbols used are I,V,X,L,C,D,M.
    $endgroup$
    – flawr
    12 hours ago
















15












15








15


2



$begingroup$


Challenge



Given some input string, return a truthy value if it represents a correct roman numeral between 1 (=I) and 3999 (=MMMCMXCIX), and a falsey value otherwise.



Details




  • The input is a non-empty string that only comprises the characters IVXLCDM.

  • The roman numerals (that we use here in this challenge) are defined as follows:


We use only following symbols:



Symbol  I   V   X   L   C   D    M
Value 1 5 10 50 100 500 1000


To define which strings are actually valid roman numerals, it is probably easiest to provide the rule of conversation: To write a decimal number a3 a2 a1 a0 (where each ai represents one digit. So for example to represent 792 we have a3=0, a2=7, a1=9, a0=2.) as a roman numeral, we decompose it into the power of tens. The different powers of ten can be written as follows:



      1-9: I, II, III, IV, V, VI, VII, VIII, IX
10-90: X, XX, XXX, XL, L, LX, LXX, LXXX, XC
100-900: C, CC, CCC, CD, D, DC, DCC, DCCC, CM
1000-3000: M, MM, MMM


Beginning at the left side with the most significant digit of the, we can convert the number that each digit represents separately and concatenate them. So for the example from above this would look like so:



Digit        a3    a2   a1   a0
Decimal 0 7 9 2
Roman DCC XC II


Therefore the roman numeral for 792 is DCCXCII. Here is a full list of all roman numerals that are relevant for this challenge: OEIS a006968.txt



Examples



Truthy



MCCXXXIV (1234)
CMLXXXVIII (988)
DXIV (514)
CI (101)


Falsey



MMIXVIII
IVX
IXV
MMMM
XXXVX
IVI
VIV











share|improve this question











$endgroup$




Challenge



Given some input string, return a truthy value if it represents a correct roman numeral between 1 (=I) and 3999 (=MMMCMXCIX), and a falsey value otherwise.



Details




  • The input is a non-empty string that only comprises the characters IVXLCDM.

  • The roman numerals (that we use here in this challenge) are defined as follows:


We use only following symbols:



Symbol  I   V   X   L   C   D    M
Value 1 5 10 50 100 500 1000


To define which strings are actually valid roman numerals, it is probably easiest to provide the rule of conversation: To write a decimal number a3 a2 a1 a0 (where each ai represents one digit. So for example to represent 792 we have a3=0, a2=7, a1=9, a0=2.) as a roman numeral, we decompose it into the power of tens. The different powers of ten can be written as follows:



      1-9: I, II, III, IV, V, VI, VII, VIII, IX
10-90: X, XX, XXX, XL, L, LX, LXX, LXXX, XC
100-900: C, CC, CCC, CD, D, DC, DCC, DCCC, CM
1000-3000: M, MM, MMM


Beginning at the left side with the most significant digit of the, we can convert the number that each digit represents separately and concatenate them. So for the example from above this would look like so:



Digit        a3    a2   a1   a0
Decimal 0 7 9 2
Roman DCC XC II


Therefore the roman numeral for 792 is DCCXCII. Here is a full list of all roman numerals that are relevant for this challenge: OEIS a006968.txt



Examples



Truthy



MCCXXXIV (1234)
CMLXXXVIII (988)
DXIV (514)
CI (101)


Falsey



MMIXVIII
IVX
IXV
MMMM
XXXVX
IVI
VIV








code-golf string number decision-problem roman-numerals






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited 20 hours ago







flawr

















asked yesterday









flawrflawr

27.1k667190




27.1k667190












  • $begingroup$
    Subset of this conversion challenge.
    $endgroup$
    – Shaggy
    yesterday










  • $begingroup$
    I still don't think this qualifies as a "subset" as the set of invalid inputs is larger. This challenge here only refers to the "well"-defined numbers that are used in OEIS A006968
    $endgroup$
    – flawr
    yesterday












  • $begingroup$
    Why is MMMM invalid? Is there a letter for 5000 that should be used instead for M<letter>?
    $endgroup$
    – Skyler
    13 hours ago










  • $begingroup$
    Check out the specs, there is no such letter. The only symbols used are I,V,X,L,C,D,M.
    $endgroup$
    – flawr
    12 hours ago




















  • $begingroup$
    Subset of this conversion challenge.
    $endgroup$
    – Shaggy
    yesterday










  • $begingroup$
    I still don't think this qualifies as a "subset" as the set of invalid inputs is larger. This challenge here only refers to the "well"-defined numbers that are used in OEIS A006968
    $endgroup$
    – flawr
    yesterday












  • $begingroup$
    Why is MMMM invalid? Is there a letter for 5000 that should be used instead for M<letter>?
    $endgroup$
    – Skyler
    13 hours ago










  • $begingroup$
    Check out the specs, there is no such letter. The only symbols used are I,V,X,L,C,D,M.
    $endgroup$
    – flawr
    12 hours ago


















$begingroup$
Subset of this conversion challenge.
$endgroup$
– Shaggy
yesterday




$begingroup$
Subset of this conversion challenge.
$endgroup$
– Shaggy
yesterday












$begingroup$
I still don't think this qualifies as a "subset" as the set of invalid inputs is larger. This challenge here only refers to the "well"-defined numbers that are used in OEIS A006968
$endgroup$
– flawr
yesterday






$begingroup$
I still don't think this qualifies as a "subset" as the set of invalid inputs is larger. This challenge here only refers to the "well"-defined numbers that are used in OEIS A006968
$endgroup$
– flawr
yesterday














$begingroup$
Why is MMMM invalid? Is there a letter for 5000 that should be used instead for M<letter>?
$endgroup$
– Skyler
13 hours ago




$begingroup$
Why is MMMM invalid? Is there a letter for 5000 that should be used instead for M<letter>?
$endgroup$
– Skyler
13 hours ago












$begingroup$
Check out the specs, there is no such letter. The only symbols used are I,V,X,L,C,D,M.
$endgroup$
– flawr
12 hours ago






$begingroup$
Check out the specs, there is no such letter. The only symbols used are I,V,X,L,C,D,M.
$endgroup$
– flawr
12 hours ago












15 Answers
15






active

oldest

votes


















9












$begingroup$


Verbose, 1362 bytes



GET A ROMAN NUMERAL TYPED IN BY THE CURRENT PERSON USING THIS PROGRAM AND PUT IT ONTO THE TOP OF THE PROGRAM STACK
PUT THE NUMBER MMMM ONTO THE TOP OF THE PROGRAM STACK
MOVE THE FIRST ELEMENT OF THE PROGRAM STACK TO THE SECOND ELEMENT'S PLACE AND THE SECOND ELEMENT OF THE STACK TO THE FIRST ELEMENT'S PLACE
DIVIDE THE FIRST ELEMENT OF THE PROGRAM STACK BY THE SECOND ELEMENT OF THE PROGRAM STACK AND PUT THE RESULT ONTO THE TOP OF THE PROGRAM STACK
PUT THE NUMBER V ONTO THE TOP OF THE PROGRAM STACK
GET THE FIRST ELEMENT OF THE PROGRAM STACK AND THE SECOND ELEMENT OF THE PROGRAM STACK AND IF THE SECOND ELEMENT OF THE PROGRAM STACK IS NOT ZERO JUMP TO THE INSTRUCTION THAT IS THE CURRENT INSTRUCTION NUMBER AND THE FIRST ELEMENT ADDED TOGETHER'S RESULT
PUT THE NUMBER I ONTO THE TOP OF THE PROGRAM STACK
GET THE TOP ELEMENT OF THE STACK AND OUTPUT IT FOR THE CURRENT PERSON USING THIS PROGRAM TO SEE
PUT THE NUMBER III ONTO THE TOP OF THE PROGRAM STACK
GET THE FIRST ELEMENT OF THE PROGRAM STACK AND THE SECOND ELEMENT OF THE PROGRAM STACK AND IF THE SECOND ELEMENT OF THE PROGRAM STACK IS NOT ZERO JUMP TO THE INSTRUCTION THAT IS THE CURRENT INSTRUCTION NUMBER AND THE FIRST ELEMENT ADDED TOGETHER'S RESULT
PUT THE NUMBER NULLA ONTO THE TOP OF THE PROGRAM STACK
GET THE TOP ELEMENT OF THE STACK AND OUTPUT IT FOR THE CURRENT PERSON USING THIS PROGRAM TO SEE


Outputs I for valid roman numerals in the range I-MMMCMXCIX and NULLA (0) or informs user input is not a valid roman numeral otherwise.






share|improve this answer









$endgroup$









  • 10




    $begingroup$
    I can't decide if this is the right tool for the job or not.
    $endgroup$
    – Vaelus
    yesterday






  • 4




    $begingroup$
    Is this the right tool for any job?
    $endgroup$
    – omzrs
    yesterday



















8












$begingroup$


C# (Visual C# Interactive Compiler), 79 109 bytes



This seems like a Regex challenge, I'm sure a shorter solution can be found...



s=>System.Text.RegularExpressions.Regex.IsMatch(s,"^M{0,3}(C[MD]|D?C{0,3})(X[CL]|L?X{0,3})(I[XV]|V?I{0,3})$")


Try it online!






share|improve this answer











$endgroup$













  • $begingroup$
    Couldn't you shorten {0,3} to {,3}?
    $endgroup$
    – flawr
    yesterday










  • $begingroup$
    @flawr doesn't seem to capture anything then
    $endgroup$
    – Innat3
    yesterday






  • 1




    $begingroup$
    Ah sorry, only things like {5,} work, but not {,5}.
    $endgroup$
    – flawr
    yesterday






  • 2




    $begingroup$
    You can add it as compiler flag instead, so it's 72 bytes and the language should be changed to C# (Visual C# Interactive Compiler) with flag /u:System.Text.RegularExpressions.Regex, like this answer :)
    $endgroup$
    – Kevin Cruijssen
    yesterday








  • 3




    $begingroup$
    Alternate regex: ^M?M?M?(C[MD]|D?C?C?C?)(X[CL]|L?X?X?X?)(I[XV]|V?I?I?I?)$. Same length, but looks weirder (which is the goal, right?)
    $endgroup$
    – Embodiment of Ignorance
    yesterday





















7












$begingroup$

Java 8, 70 bytes





s->s.matches("M{0,3}(C[MD]|D?C{0,3})(X[CL]|L?X{0,3})(I[XV]|V?I{0,3})")


Port of @Innat3's C# answer, so make sure to upvote him!



Try it online.



Explanation:



s->                // Method with String parameter and boolean return-type
s.matches("...") // Check if the string matches the regex fully
// (which implicitly adds a leading "^" and trailing "$")

M{0,3} // No, 1, 2, or 3 adjacent "M"
( | ) // Followed by either:
C[MD] // A "C" with an "M" or "D" after it
| // or:
D? // An optional "D"
C{0,3} // Followed by no, 1, 2, or 3 adjacent "C"
( | ) // Followed by either:
X[CL] // An "X" with a "C" or "L" after it
| // or:
L? // An optional "L"
X{0,3} // Followed by no, 1, 2, or 3 adjacent "X"
( | ) // Followed by either:
I[XV] // An "I" with an "X" or "V" after it
| // or:
V? // An optional "V"
I{0,3} // Followed by no, 1, 2, or 3 adjacent "I"





share|improve this answer









$endgroup$





















    7












    $begingroup$


    Wolfram Language (Mathematica), 35 bytes



    Check[FromRomanNumeral@#<3999,1<0]&


    Try it online!



    5 bytes saved, thanks to @attinat



    the limitation [1,3999] unfortunateley costs 7 bytes...

    here is the code for any roman number




    Wolfram Language (Mathematica), 28 bytes



    Check[FromRomanNumeral@#,F]&


    Try it online!



    the above code works for any number, not just [1,3999]






    share|improve this answer











    $endgroup$









    • 2




      $begingroup$
      @ExpiredData "The input is a non-empty string that only comprises the characters IVXLCDM."
      $endgroup$
      – mathmandan
      yesterday












    • $begingroup$
      35 bytes. Boole is also shorter (by one byte) than using If in that way.
      $endgroup$
      – attinat
      yesterday



















    7












    $begingroup$


    CP-1610 assembly (Intellivision),  52 ... 48  47 DECLEs1 = 59 bytes



    Let's try this on a system that predates Perl by a good 7 years. :-)



    Takes a pointer to a null-terminated string in R4. Sets the Zero flag if the input is a valid Roman numeral, or clears it otherwise.



                    ROMW    10              ; use 10-bit ROM width
    ORG $4800 ; map this program at $4800

    ;; ------------------------------------------------------------- ;;
    ;; test code ;;
    ;; ------------------------------------------------------------- ;;
    4800 EIS ; enable interrupts

    4801 SDBD ; R5 = pointer into test case index
    4802 MVII #ndx, R5
    4805 MVII #$214, R3 ; R3 = backtab pointer
    4807 MVII #11, R0 ; R0 = number of test cases

    4809 loop SDBD ; R4 = pointer to next test case
    480A MVI@ R5, R4
    480B PSHR R0 ; save R0, R3, R5 onto the stack
    480C PSHR R3
    480D PSHR R5
    480E CALL isRoman ; invoke our routine
    4811 PULR R5 ; restore R5 and R3
    4812 PULR R3

    4813 MVII #$1A7, R0 ; use a white 'T' by default
    4815 BEQ disp

    4817 MVII #$137, R0 ; or a white 'F' is the Z flag was cleared

    4819 disp MVO@ R0, R3 ; draw it
    481A INCR R3 ; increment the backtab pointer

    481B PULR R0 ; restore R0
    481C DECR R0 ; and advance to the next test case, if any
    481D BNEQ loop

    481F DECR R7 ; loop forever

    ;; ------------------------------------------------------------- ;;
    ;; test cases ;;
    ;; ------------------------------------------------------------- ;;
    4820 ndx BIDECLE test0, test1, test2, test3
    4828 BIDECLE test4, test5, test6, test7, test8, test9, test10

    ; truthy
    4836 test0 STRING "MCCXXXIV", 0
    483F test1 STRING "CMLXXXVIII", 0
    484A test2 STRING "DXIV", 0
    484F test3 STRING "CI", 0

    ; falsy
    4852 test4 STRING "MMIXVIII", 0
    485B test5 STRING "IVX", 0
    485F test6 STRING "IXV", 0
    4863 test7 STRING "MMMM", 0
    4868 test8 STRING "XXXVX", 0
    486E test9 STRING "IVI", 0
    4872 test10 STRING "VIV", 0

    ;; ------------------------------------------------------------- ;;
    ;; routine ;;
    ;; ------------------------------------------------------------- ;;
    isRoman PROC

    4876 PSHR R5 ; push the return address

    4877 MOVR R7, R2 ; R2 = dummy 1st suffix
    4878 MOVR R2, R5 ; R5 = pointer into table
    4879 ADDI #@tbl-$+1,R5

    487B @loop MVI@ R5, R1 ; R1 = main digit (M, C, X, I)
    487C MVI@ R5, R3 ; R3 = prefix or 2nd suffix (-, D, L, V)

    487D MVI@ R4, R0 ; R0 = next digit

    487E CMPR R0, R3 ; if this is the prefix ...
    487F BNEQ @main

    4881 COMR R2 ; ... disable the suffixes
    4882 COMR R3 ; by setting them to invalid values
    4883 MVI@ R4, R0 ; and read R0 again

    4884 @main CMPR R0, R1 ; if R0 is not equal to the main digit,
    4885 BNEQ @back ; assume that this part is over

    4887 MVI@ R4, R0 ; R0 = next digit
    4888 CMPR R0, R1 ; if this is a 2nd occurrence
    4889 BNEQ @suffix ; of the main digit ...

    488B CMP@ R4, R1 ; ... it may be followed by a 3rd occurrence
    488C BNEQ @back

    488E MOVR R2, R0 ; if so, force the test below to succeed

    488F @suffix CMPR R0, R2 ; otherwise, it may be either the 1st suffix
    4890 BEQ @next
    4892 CMPR R0, R3 ; or the 2nd suffix (these tests always fail
    4893 BEQ @next ; if the suffixes were disabled above)

    4895 @back DECR R4 ; the last digit either belongs to the next
    ; iteration or is invalid

    4896 @next MOVR R1, R2 ; use the current main digit
    ; as the next 1st suffix

    4897 SUBI #'I', R1 ; was it the last iteration? ...
    4899 BNEQ @loop

    489B CMP@ R4, R1 ; ... yes: make sure that we've also reached
    ; the end of the input

    489C PULR R7 ; return

    489D @tbl DECLE 'M', '-' ; table format: main digit, 2nd suffix
    489F DECLE 'C', 'D'
    48A1 DECLE 'X', 'L'
    48A3 DECLE 'I', 'V'

    ENDP


    How?



    The regular expression can be rewritten as 4 groups with the same structure, provided that # is any invalid character that is guaranteed not be present in the input string.



                     +-------+---> main digit
    | |
    (M[##]|#?M{0,3})(C[MD]|D?C{0,3})(X[CL]|L?X{0,3})(I[XV]|V?I{0,3})
    || |
    |+--+-----> prefix or second suffix
    |
    +---------> first suffix


    The first suffix of the group $N$ is the main digit of the group $N-1$. Therefore, we can store the patterns with the pair $(text{main_digit}, text{second_suffix})$ alone.



    Our routine attempts to parse the input string character by character according to these patterns and eventually checks whether the end of the string is reached.



    Output



    output



    screenshot of jzIntv





    1. A CP-1610 opcode is encoded with a 10-bit value, known as a 'DECLE'. This routine is 47 DECLEs long, starting at $4876 and ending at $48A4 (included).






    share|improve this answer











    $endgroup$













    • $begingroup$
      wouldn't this be one of the few places where fractional bytes are valid
      $endgroup$
      – ASCII-only
      20 hours ago










    • $begingroup$
      @ASCII-only I used to think so, but I don't know for sure. See the comments of this answer for some insight about this.
      $endgroup$
      – Arnauld
      20 hours ago










    • $begingroup$
      @ASCII-only Also, I've just found this post in meta that tends to confirm it's probably best to round to whole bytes.
      $endgroup$
      – Arnauld
      20 hours ago










    • $begingroup$
      ah, so it's only 10 bits when it's in RAM?
      $endgroup$
      – ASCII-only
      20 hours ago












    • $begingroup$
      The program is never stored in RAM, only in ROM. So it depends on the memory chips used in the cartridge. The CPU is designed to access either 10-bit or 16-bit ROM. The "ROMW 10" directive forces the compiler to generate code in 10-bit format.
      $endgroup$
      – Arnauld
      19 hours ago



















    5












    $begingroup$


    R, 74 71 56 bytes



    Thanks to @RobinRyder, @Giuseppe, & @MickyT for their suggestions how to use grep effectively with R's built in as.roman.





    sub("^M(.+)","\1",scan(,""))%in%paste(as.roman(1:2999))


    Try it online!






    share|improve this answer











    $endgroup$













    • $begingroup$
      as.roman won't work anyway, since it only works up to 3899 for some reason.
      $endgroup$
      – Giuseppe
      yesterday












    • $begingroup$
      I really should read the documentation better, Probably because 4000 doesn't have a definite representation in Roman, so how'd one do 3900. This is similar to 390 and now I just found an issue with my grep where I'd have to anchor the pattern.
      $endgroup$
      – CT Hall
      yesterday












    • $begingroup$
      @Giuseppe, addressed, using the same regex as the other answers.
      $endgroup$
      – CT Hall
      yesterday










    • $begingroup$
      I wonder if there's a way to use .romans here...probably not.
      $endgroup$
      – Giuseppe
      yesterday






    • 2




      $begingroup$
      66 bytes using as.roman: first strip the initial M if there is one, then check whether the result is in as.roman(1:2999). This requires special handling of the case where the input is M.
      $endgroup$
      – Robin Ryder
      yesterday



















    4












    $begingroup$


    Wolfram Language (Mathematica), 32 bytes



    RomanNumeral@Range@3999~Count~#&


    Try it online!






    share|improve this answer









    $endgroup$





















      2












      $begingroup$


      Jelly,  48 47  46 bytes



      -1 thanks to Nick Kennedy



      5Żo7;“ÆæC‘b3ð“IVXLCDM”ị@3Ƥm2”MẋⱮ3¤ṭŻ€ṚŒpF€ḟ€0ċ


      A monadic Link accepting a non-empty list of characters consisting only of IVXLCDM which yields either 1 (when it's a valid Roman numeral between $1$ and $3999$) or 0 (if not).



      Try it online! Or see the test-suite.



      How?



      Needs updating...



      7R;“¿ç‘ḃ2ŒQ€6¦ị - Link 1, acceptable "digits": list of characters        e.g. "IVX"
      7R - range of seven [1,2,3,4,5,6,7]
      “¿ç‘ - list of code-page indices [11,23]
      ; - concatenate [1,2,3,4,5,6,7,11,23]
      ḃ2 - to bijective base two [[1],[2],[1,1],[1,2],[2,1],[2,2],[1,1,1],[2,1,1],[2,1,1,1]]
      ¦ - sparse application...
      6 - ...to indices: six
      ŒQ€ - ...do: for €ach: distinct sieve ...,[1,0],...
      ị - index into input ["I","V","II", "IV", "VI", "IX", "III", "VII", "VIII" ]

      “IVXLCDM”Ç3Ƥm2”MẋⱮ3¤ṭŻ€ṚŒpF€ḟ€0ċ - Main Link: list of characters
      “IVXLCDM” - list of characters
      3Ƥ - for infixes of length three: ("IVX","VXL","XLC","LCD","CDM")
      Ç - call the last Link (1) as a monad
      m2 - modulo two slice (results for "IVX", "XLC", and "CDM" only)
      ¤ - nilad followed by link(s) as a nilad:
      ”M - character 'M'
      Ɱ3 - map across [1,2,3] with:
      ẋ - repeat -> ["M", "MM", "MMM"]
      ṭ - tack
      Ż€ - prepend a zero to each
      Ṛ - reverse
      - -- now we have the table:
      - 0 M MM MMM
      - 0 C D CC CD DC CM CCC DCC DCCC
      - 0 X L XX XL LX XC XXX LXX LXXX
      - 0 I V II IV VI IX III VII VIII
      Œp - Cartesian product -> [[0,0,0,0],[0,0,0,"I"],...,["M","CM",0,"IV"],...]
      F€ - flatten €ach -> [[0,0,0,0],[0,0,0,'I'],...,['M','C','M',0,'I','V'],...]
      ḟ€0 - filter out the zeros from €ach -> ["","I",...,"MCMIV",...]
      ċ - count occurrences of the input





      share|improve this answer











      $endgroup$













      • $begingroup$
        There seems to be a redundant space on the first line. Another byte. Another byte can be saved by using a simpler first line. Try it online!
        $endgroup$
        – Nick Kennedy
        21 hours ago












      • $begingroup$
        Thanks, I've saved one more from it.
        $endgroup$
        – Jonathan Allan
        20 hours ago



















      1












      $begingroup$

      Perl 5 (-p), 57 bytes



      $_=/^M*(C[MD]|D?C*)(X[CL]|L?X*)(I[XV]|V?I*)$/&!/(.)1{3}/


      TIO




      • uses almost the same regular expression except {0,3} quantifier was changed by *


      • &!/(.)1{3}/ to ensure the same character can't occur 4 times in a row.

      • can't be golfed with -/(.)1{3}/ because would give-1 for IIIIVI for example






      share|improve this answer











      $endgroup$





















        1












        $begingroup$


        Python 2, 81 bytes





        import re
        re.compile('M{,3}(D?C{,3}|C[DM])(L?X{,3}|X[LC])(V?I{,3}|I[VX])$').match


        Try it online!



        Let's look at the last part of the regex, which matching the Roman numerals up to 9 (including the empty string)



        V?I{,3}|I[VX]


        This has two alternatives separated by |:





        • V?I{,3}: An optional V followed by up to 3 I's. This matches the empty string I,II,III, V, VI,VII,VIII.


        • I[VX]: An I followed by a V or X. This matches IV and IX.


        The same things with X,L,C matching the tens, with C,D,M matches the hundreds, and finally ^M{,3} allows up to 3 M's (thousands) at the start.



        I tried generating the template for each trio of characters rather than writing it 3 times, but this was a lot longer.






        share|improve this answer











        $endgroup$













        • $begingroup$
          No need for the ^ anchor at the beginning; match already implies it matches at the beginning of the string.
          $endgroup$
          – ShadowRanger
          yesterday










        • $begingroup$
          @ShadowRanger Thanks, I removed the ^.
          $endgroup$
          – xnor
          yesterday










        • $begingroup$
          Although I think you messed up the count in the edit; should be 83, not 81.
          $endgroup$
          – ShadowRanger
          yesterday










        • $begingroup$
          @ShadowRanger The count is 81 because the f= isn't included in the code since anonynomous functions are allowed. It's just for TIO.
          $endgroup$
          – xnor
          yesterday






        • 1




          $begingroup$
          Ah, makes sense. Annoying there's no way to organize it to hide that in the header or footer, but yeah, unassigned lambdas are legal, so unassigned bound methods of compiled regex should be good too.
          $endgroup$
          – ShadowRanger
          yesterday



















        1












        $begingroup$


        Retina, 56 51 bytes



        (.)1{3}
        0
        ^M*(C[MD]|D?C*)(X[CL]|L?X*)(I[XV]|V?I*)$


        Port of @NahuelFouilleul's Perl 5 answer, so make sure to upvote him!



        Try it online or verify all test cases.



        Explanation:





        (.)1{3}        # If four adjacent characters can be found which are the same
        0 # Replace it with a 0

        ^...$ # Then check if the string matches the following fully:
        M* # No or any amount of adjacent "M"
        ( | ) # Followed by either:
        C[MD] # A "C" with an "M" or "D" after it
        | # or:
        D? # An optional "D"
        C* # Followed by no or any amount of adjacent "C"
        ( | ) # Followed by either:
        X[CL] # An "X" with a "C" or "L" after it
        | # or:
        L? # An optional "L"
        X* # Followed by no or any amount of adjacent "X"
        ( | ) # Followed by either:
        I[XV] # An "I" with an "X" or "V" after it
        | # or:
        V? # An optional "V"
        I* # Followed by no or any amount of adjacent "I"





        share|improve this answer











        $endgroup$





















          0












          $begingroup$


          05AB1E, 61 bytes



          •1∞Γ'иÛnuÞ₂…•Ž8вв€SÐ)v.•6#&‘нδ•u3ôNèyè}'M3L×)Rεõš}`3Fâ}€˜JIå


          Try it online or verify all test cases.



          Explanation:





          •1∞Γ'иÛnuÞ₂…•             '# Push compressed integer 397940501547566186191992778
          Ž8в # Push compressed integer 2112
          в # Convert the integer to Base-2112 as list:
          # [1,11,111,12,2,21,211,2111,10]
          €S # Convert each number to a list of digits
          Ð # Triplicate this list
          ) # And wrap it into a list of lists (of lists)
          v # Loop `y` over each these three lists:
          .•6#&‘нδ• # Push compressed string "xivcxlmcd"
          u # Uppercased
          3ô # And split into parts of size 3: ["XIV","CXL","MCD"]
          Nè # Use the loop index to get the current part
          yè # And index the list of lists of digits into this string
          }'M '# After the loop: push "M"
          3L # Push list [1,2,3]
          × # Repeat the "M" that many times: ["M","MM","MMM"]
          ) # Wrap all lists on the stack into a list:
          # [[["I"],["I","I"],["I","I","I"],["I","V"],["V"],["V","I"],["V","I","I"],["V","I","I","I"],["I","X"]],[["X"],["X","X"],["X","X","X"],["X","L"],["L"],["L","X"],["L","X","X"],["L","X","X","X"],["X","C"]],[["C"],["C","C"],["C","C","C"],["C","D"],["D"],["D","C"],["D","C","C"],["D","C","C","C"],["C","M"]],["M","MM","MMM"]]
          R # Reverse this list
          εõš} # Prepend an empty string "" before each inner list
          ` # Push the four lists onto the stack
          3F # Loop 3 times:
          â # Take the cartesian product of the two top lists
          }€˜ # After the loop: flatten each inner list
          J # Join each inner list together to a single string
          Iå # And check if the input is in this list
          # (after which the result is output implicitly)


          See this 05AB1E tip of mine (sections How to compress strings not part of the dictionary?, How to compress large integers?, and How to compress integer lists?) to understand why:





          • •1∞Γ'иÛnuÞ₂…• is 397940501547566186191992778


          • Ž8в is 2112


          • •1∞Γ'иÛnuÞ₂…•Ž8вв is [1,11,111,12,2,21,211,2111,10]


          • .•6#&‘нδ• is "xivcxlmcd"






          share|improve this answer











          $endgroup$





















            0












            $begingroup$

            perl -MRegexp::Common -pe, 34 bytes



            $_=/^$RE{num}{roman}$/&!/(.)1{3}/


            The &!/(.)1{3}/ part is necessary, because Regexp::Common allows four (but not five) of the same characters in a row. That way, it matches roman numbers used on clock faces, where IIII is often used for 4.






            share|improve this answer









            $endgroup$





















              0












              $begingroup$


              Python 3, 116 113 109 107 105 106 bytes





              import re
              lambda n:re.match(r'(M{,3}(C(M|CC?|D)?|DC{,3}))(X(C|XX?|L)?|(LX{,3}))?(I(X|II?|V)?|VI{,3})?$',n)


              Try it online!



              -1 byte thanks to ShadowRanger






              share|improve this answer











              $endgroup$









              • 2




                $begingroup$
                As I mentioned on the Py2 answer, the leading ^ is unnecessary since match only matches at the beginning of a string already.
                $endgroup$
                – ShadowRanger
                yesterday










              • $begingroup$
                @ShadowRanger added anchors while debugging and then didn't try again without them. I'll remember that now - thanks! :)
                $endgroup$
                – Noodle9
                14 hours ago










              • $begingroup$
                Well, just to be clear, the trailing $ is necessary (only fullmatch implies anchors on both ends, and obviously that would cost more than a $).
                $endgroup$
                – ShadowRanger
                14 hours ago










              • $begingroup$
                @ShadowRanger Ah! That explains why I needed anchors! Didn't realize I only needed to anchor the end. Thanks again.
                $endgroup$
                – Noodle9
                13 hours ago





















              0












              $begingroup$


              Ruby, (-n) 56 bytes





              p~/^M{,3}(D?C{,3}|CM|CD)(L?X{,3}|XC|XL)(V?I{,3}|IV|IX)$/


              Try it online!



              Outputs 0 (truthy) or nil (falsy).






              share|improve this answer









              $endgroup$














                Your Answer






                StackExchange.ifUsing("editor", function () {
                StackExchange.using("externalEditor", function () {
                StackExchange.using("snippets", function () {
                StackExchange.snippets.init();
                });
                });
                }, "code-snippets");

                StackExchange.ready(function() {
                var channelOptions = {
                tags: "".split(" "),
                id: "200"
                };
                initTagRenderer("".split(" "), "".split(" "), channelOptions);

                StackExchange.using("externalEditor", function() {
                // Have to fire editor after snippets, if snippets enabled
                if (StackExchange.settings.snippets.snippetsEnabled) {
                StackExchange.using("snippets", function() {
                createEditor();
                });
                }
                else {
                createEditor();
                }
                });

                function createEditor() {
                StackExchange.prepareEditor({
                heartbeatType: 'answer',
                autoActivateHeartbeat: false,
                convertImagesToLinks: false,
                noModals: true,
                showLowRepImageUploadWarning: true,
                reputationToPostImages: null,
                bindNavPrevention: true,
                postfix: "",
                imageUploader: {
                brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
                contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
                allowUrls: true
                },
                onDemand: true,
                discardSelector: ".discard-answer"
                ,immediatelyShowMarkdownHelp:true
                });


                }
                });














                draft saved

                draft discarded


















                StackExchange.ready(
                function () {
                StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodegolf.stackexchange.com%2fquestions%2f183014%2fmatch-roman-numerals%23new-answer', 'question_page');
                }
                );

                Post as a guest















                Required, but never shown

























                15 Answers
                15






                active

                oldest

                votes








                15 Answers
                15






                active

                oldest

                votes









                active

                oldest

                votes






                active

                oldest

                votes









                9












                $begingroup$


                Verbose, 1362 bytes



                GET A ROMAN NUMERAL TYPED IN BY THE CURRENT PERSON USING THIS PROGRAM AND PUT IT ONTO THE TOP OF THE PROGRAM STACK
                PUT THE NUMBER MMMM ONTO THE TOP OF THE PROGRAM STACK
                MOVE THE FIRST ELEMENT OF THE PROGRAM STACK TO THE SECOND ELEMENT'S PLACE AND THE SECOND ELEMENT OF THE STACK TO THE FIRST ELEMENT'S PLACE
                DIVIDE THE FIRST ELEMENT OF THE PROGRAM STACK BY THE SECOND ELEMENT OF THE PROGRAM STACK AND PUT THE RESULT ONTO THE TOP OF THE PROGRAM STACK
                PUT THE NUMBER V ONTO THE TOP OF THE PROGRAM STACK
                GET THE FIRST ELEMENT OF THE PROGRAM STACK AND THE SECOND ELEMENT OF THE PROGRAM STACK AND IF THE SECOND ELEMENT OF THE PROGRAM STACK IS NOT ZERO JUMP TO THE INSTRUCTION THAT IS THE CURRENT INSTRUCTION NUMBER AND THE FIRST ELEMENT ADDED TOGETHER'S RESULT
                PUT THE NUMBER I ONTO THE TOP OF THE PROGRAM STACK
                GET THE TOP ELEMENT OF THE STACK AND OUTPUT IT FOR THE CURRENT PERSON USING THIS PROGRAM TO SEE
                PUT THE NUMBER III ONTO THE TOP OF THE PROGRAM STACK
                GET THE FIRST ELEMENT OF THE PROGRAM STACK AND THE SECOND ELEMENT OF THE PROGRAM STACK AND IF THE SECOND ELEMENT OF THE PROGRAM STACK IS NOT ZERO JUMP TO THE INSTRUCTION THAT IS THE CURRENT INSTRUCTION NUMBER AND THE FIRST ELEMENT ADDED TOGETHER'S RESULT
                PUT THE NUMBER NULLA ONTO THE TOP OF THE PROGRAM STACK
                GET THE TOP ELEMENT OF THE STACK AND OUTPUT IT FOR THE CURRENT PERSON USING THIS PROGRAM TO SEE


                Outputs I for valid roman numerals in the range I-MMMCMXCIX and NULLA (0) or informs user input is not a valid roman numeral otherwise.






                share|improve this answer









                $endgroup$









                • 10




                  $begingroup$
                  I can't decide if this is the right tool for the job or not.
                  $endgroup$
                  – Vaelus
                  yesterday






                • 4




                  $begingroup$
                  Is this the right tool for any job?
                  $endgroup$
                  – omzrs
                  yesterday
















                9












                $begingroup$


                Verbose, 1362 bytes



                GET A ROMAN NUMERAL TYPED IN BY THE CURRENT PERSON USING THIS PROGRAM AND PUT IT ONTO THE TOP OF THE PROGRAM STACK
                PUT THE NUMBER MMMM ONTO THE TOP OF THE PROGRAM STACK
                MOVE THE FIRST ELEMENT OF THE PROGRAM STACK TO THE SECOND ELEMENT'S PLACE AND THE SECOND ELEMENT OF THE STACK TO THE FIRST ELEMENT'S PLACE
                DIVIDE THE FIRST ELEMENT OF THE PROGRAM STACK BY THE SECOND ELEMENT OF THE PROGRAM STACK AND PUT THE RESULT ONTO THE TOP OF THE PROGRAM STACK
                PUT THE NUMBER V ONTO THE TOP OF THE PROGRAM STACK
                GET THE FIRST ELEMENT OF THE PROGRAM STACK AND THE SECOND ELEMENT OF THE PROGRAM STACK AND IF THE SECOND ELEMENT OF THE PROGRAM STACK IS NOT ZERO JUMP TO THE INSTRUCTION THAT IS THE CURRENT INSTRUCTION NUMBER AND THE FIRST ELEMENT ADDED TOGETHER'S RESULT
                PUT THE NUMBER I ONTO THE TOP OF THE PROGRAM STACK
                GET THE TOP ELEMENT OF THE STACK AND OUTPUT IT FOR THE CURRENT PERSON USING THIS PROGRAM TO SEE
                PUT THE NUMBER III ONTO THE TOP OF THE PROGRAM STACK
                GET THE FIRST ELEMENT OF THE PROGRAM STACK AND THE SECOND ELEMENT OF THE PROGRAM STACK AND IF THE SECOND ELEMENT OF THE PROGRAM STACK IS NOT ZERO JUMP TO THE INSTRUCTION THAT IS THE CURRENT INSTRUCTION NUMBER AND THE FIRST ELEMENT ADDED TOGETHER'S RESULT
                PUT THE NUMBER NULLA ONTO THE TOP OF THE PROGRAM STACK
                GET THE TOP ELEMENT OF THE STACK AND OUTPUT IT FOR THE CURRENT PERSON USING THIS PROGRAM TO SEE


                Outputs I for valid roman numerals in the range I-MMMCMXCIX and NULLA (0) or informs user input is not a valid roman numeral otherwise.






                share|improve this answer









                $endgroup$









                • 10




                  $begingroup$
                  I can't decide if this is the right tool for the job or not.
                  $endgroup$
                  – Vaelus
                  yesterday






                • 4




                  $begingroup$
                  Is this the right tool for any job?
                  $endgroup$
                  – omzrs
                  yesterday














                9












                9








                9





                $begingroup$


                Verbose, 1362 bytes



                GET A ROMAN NUMERAL TYPED IN BY THE CURRENT PERSON USING THIS PROGRAM AND PUT IT ONTO THE TOP OF THE PROGRAM STACK
                PUT THE NUMBER MMMM ONTO THE TOP OF THE PROGRAM STACK
                MOVE THE FIRST ELEMENT OF THE PROGRAM STACK TO THE SECOND ELEMENT'S PLACE AND THE SECOND ELEMENT OF THE STACK TO THE FIRST ELEMENT'S PLACE
                DIVIDE THE FIRST ELEMENT OF THE PROGRAM STACK BY THE SECOND ELEMENT OF THE PROGRAM STACK AND PUT THE RESULT ONTO THE TOP OF THE PROGRAM STACK
                PUT THE NUMBER V ONTO THE TOP OF THE PROGRAM STACK
                GET THE FIRST ELEMENT OF THE PROGRAM STACK AND THE SECOND ELEMENT OF THE PROGRAM STACK AND IF THE SECOND ELEMENT OF THE PROGRAM STACK IS NOT ZERO JUMP TO THE INSTRUCTION THAT IS THE CURRENT INSTRUCTION NUMBER AND THE FIRST ELEMENT ADDED TOGETHER'S RESULT
                PUT THE NUMBER I ONTO THE TOP OF THE PROGRAM STACK
                GET THE TOP ELEMENT OF THE STACK AND OUTPUT IT FOR THE CURRENT PERSON USING THIS PROGRAM TO SEE
                PUT THE NUMBER III ONTO THE TOP OF THE PROGRAM STACK
                GET THE FIRST ELEMENT OF THE PROGRAM STACK AND THE SECOND ELEMENT OF THE PROGRAM STACK AND IF THE SECOND ELEMENT OF THE PROGRAM STACK IS NOT ZERO JUMP TO THE INSTRUCTION THAT IS THE CURRENT INSTRUCTION NUMBER AND THE FIRST ELEMENT ADDED TOGETHER'S RESULT
                PUT THE NUMBER NULLA ONTO THE TOP OF THE PROGRAM STACK
                GET THE TOP ELEMENT OF THE STACK AND OUTPUT IT FOR THE CURRENT PERSON USING THIS PROGRAM TO SEE


                Outputs I for valid roman numerals in the range I-MMMCMXCIX and NULLA (0) or informs user input is not a valid roman numeral otherwise.






                share|improve this answer









                $endgroup$




                Verbose, 1362 bytes



                GET A ROMAN NUMERAL TYPED IN BY THE CURRENT PERSON USING THIS PROGRAM AND PUT IT ONTO THE TOP OF THE PROGRAM STACK
                PUT THE NUMBER MMMM ONTO THE TOP OF THE PROGRAM STACK
                MOVE THE FIRST ELEMENT OF THE PROGRAM STACK TO THE SECOND ELEMENT'S PLACE AND THE SECOND ELEMENT OF THE STACK TO THE FIRST ELEMENT'S PLACE
                DIVIDE THE FIRST ELEMENT OF THE PROGRAM STACK BY THE SECOND ELEMENT OF THE PROGRAM STACK AND PUT THE RESULT ONTO THE TOP OF THE PROGRAM STACK
                PUT THE NUMBER V ONTO THE TOP OF THE PROGRAM STACK
                GET THE FIRST ELEMENT OF THE PROGRAM STACK AND THE SECOND ELEMENT OF THE PROGRAM STACK AND IF THE SECOND ELEMENT OF THE PROGRAM STACK IS NOT ZERO JUMP TO THE INSTRUCTION THAT IS THE CURRENT INSTRUCTION NUMBER AND THE FIRST ELEMENT ADDED TOGETHER'S RESULT
                PUT THE NUMBER I ONTO THE TOP OF THE PROGRAM STACK
                GET THE TOP ELEMENT OF THE STACK AND OUTPUT IT FOR THE CURRENT PERSON USING THIS PROGRAM TO SEE
                PUT THE NUMBER III ONTO THE TOP OF THE PROGRAM STACK
                GET THE FIRST ELEMENT OF THE PROGRAM STACK AND THE SECOND ELEMENT OF THE PROGRAM STACK AND IF THE SECOND ELEMENT OF THE PROGRAM STACK IS NOT ZERO JUMP TO THE INSTRUCTION THAT IS THE CURRENT INSTRUCTION NUMBER AND THE FIRST ELEMENT ADDED TOGETHER'S RESULT
                PUT THE NUMBER NULLA ONTO THE TOP OF THE PROGRAM STACK
                GET THE TOP ELEMENT OF THE STACK AND OUTPUT IT FOR THE CURRENT PERSON USING THIS PROGRAM TO SEE


                Outputs I for valid roman numerals in the range I-MMMCMXCIX and NULLA (0) or informs user input is not a valid roman numeral otherwise.







                share|improve this answer












                share|improve this answer



                share|improve this answer










                answered yesterday









                Expired DataExpired Data

                898216




                898216








                • 10




                  $begingroup$
                  I can't decide if this is the right tool for the job or not.
                  $endgroup$
                  – Vaelus
                  yesterday






                • 4




                  $begingroup$
                  Is this the right tool for any job?
                  $endgroup$
                  – omzrs
                  yesterday














                • 10




                  $begingroup$
                  I can't decide if this is the right tool for the job or not.
                  $endgroup$
                  – Vaelus
                  yesterday






                • 4




                  $begingroup$
                  Is this the right tool for any job?
                  $endgroup$
                  – omzrs
                  yesterday








                10




                10




                $begingroup$
                I can't decide if this is the right tool for the job or not.
                $endgroup$
                – Vaelus
                yesterday




                $begingroup$
                I can't decide if this is the right tool for the job or not.
                $endgroup$
                – Vaelus
                yesterday




                4




                4




                $begingroup$
                Is this the right tool for any job?
                $endgroup$
                – omzrs
                yesterday




                $begingroup$
                Is this the right tool for any job?
                $endgroup$
                – omzrs
                yesterday











                8












                $begingroup$


                C# (Visual C# Interactive Compiler), 79 109 bytes



                This seems like a Regex challenge, I'm sure a shorter solution can be found...



                s=>System.Text.RegularExpressions.Regex.IsMatch(s,"^M{0,3}(C[MD]|D?C{0,3})(X[CL]|L?X{0,3})(I[XV]|V?I{0,3})$")


                Try it online!






                share|improve this answer











                $endgroup$













                • $begingroup$
                  Couldn't you shorten {0,3} to {,3}?
                  $endgroup$
                  – flawr
                  yesterday










                • $begingroup$
                  @flawr doesn't seem to capture anything then
                  $endgroup$
                  – Innat3
                  yesterday






                • 1




                  $begingroup$
                  Ah sorry, only things like {5,} work, but not {,5}.
                  $endgroup$
                  – flawr
                  yesterday






                • 2




                  $begingroup$
                  You can add it as compiler flag instead, so it's 72 bytes and the language should be changed to C# (Visual C# Interactive Compiler) with flag /u:System.Text.RegularExpressions.Regex, like this answer :)
                  $endgroup$
                  – Kevin Cruijssen
                  yesterday








                • 3




                  $begingroup$
                  Alternate regex: ^M?M?M?(C[MD]|D?C?C?C?)(X[CL]|L?X?X?X?)(I[XV]|V?I?I?I?)$. Same length, but looks weirder (which is the goal, right?)
                  $endgroup$
                  – Embodiment of Ignorance
                  yesterday


















                8












                $begingroup$


                C# (Visual C# Interactive Compiler), 79 109 bytes



                This seems like a Regex challenge, I'm sure a shorter solution can be found...



                s=>System.Text.RegularExpressions.Regex.IsMatch(s,"^M{0,3}(C[MD]|D?C{0,3})(X[CL]|L?X{0,3})(I[XV]|V?I{0,3})$")


                Try it online!






                share|improve this answer











                $endgroup$













                • $begingroup$
                  Couldn't you shorten {0,3} to {,3}?
                  $endgroup$
                  – flawr
                  yesterday










                • $begingroup$
                  @flawr doesn't seem to capture anything then
                  $endgroup$
                  – Innat3
                  yesterday






                • 1




                  $begingroup$
                  Ah sorry, only things like {5,} work, but not {,5}.
                  $endgroup$
                  – flawr
                  yesterday






                • 2




                  $begingroup$
                  You can add it as compiler flag instead, so it's 72 bytes and the language should be changed to C# (Visual C# Interactive Compiler) with flag /u:System.Text.RegularExpressions.Regex, like this answer :)
                  $endgroup$
                  – Kevin Cruijssen
                  yesterday








                • 3




                  $begingroup$
                  Alternate regex: ^M?M?M?(C[MD]|D?C?C?C?)(X[CL]|L?X?X?X?)(I[XV]|V?I?I?I?)$. Same length, but looks weirder (which is the goal, right?)
                  $endgroup$
                  – Embodiment of Ignorance
                  yesterday
















                8












                8








                8





                $begingroup$


                C# (Visual C# Interactive Compiler), 79 109 bytes



                This seems like a Regex challenge, I'm sure a shorter solution can be found...



                s=>System.Text.RegularExpressions.Regex.IsMatch(s,"^M{0,3}(C[MD]|D?C{0,3})(X[CL]|L?X{0,3})(I[XV]|V?I{0,3})$")


                Try it online!






                share|improve this answer











                $endgroup$




                C# (Visual C# Interactive Compiler), 79 109 bytes



                This seems like a Regex challenge, I'm sure a shorter solution can be found...



                s=>System.Text.RegularExpressions.Regex.IsMatch(s,"^M{0,3}(C[MD]|D?C{0,3})(X[CL]|L?X{0,3})(I[XV]|V?I{0,3})$")


                Try it online!







                share|improve this answer














                share|improve this answer



                share|improve this answer








                edited yesterday

























                answered yesterday









                Innat3Innat3

                2314




                2314












                • $begingroup$
                  Couldn't you shorten {0,3} to {,3}?
                  $endgroup$
                  – flawr
                  yesterday










                • $begingroup$
                  @flawr doesn't seem to capture anything then
                  $endgroup$
                  – Innat3
                  yesterday






                • 1




                  $begingroup$
                  Ah sorry, only things like {5,} work, but not {,5}.
                  $endgroup$
                  – flawr
                  yesterday






                • 2




                  $begingroup$
                  You can add it as compiler flag instead, so it's 72 bytes and the language should be changed to C# (Visual C# Interactive Compiler) with flag /u:System.Text.RegularExpressions.Regex, like this answer :)
                  $endgroup$
                  – Kevin Cruijssen
                  yesterday








                • 3




                  $begingroup$
                  Alternate regex: ^M?M?M?(C[MD]|D?C?C?C?)(X[CL]|L?X?X?X?)(I[XV]|V?I?I?I?)$. Same length, but looks weirder (which is the goal, right?)
                  $endgroup$
                  – Embodiment of Ignorance
                  yesterday




















                • $begingroup$
                  Couldn't you shorten {0,3} to {,3}?
                  $endgroup$
                  – flawr
                  yesterday










                • $begingroup$
                  @flawr doesn't seem to capture anything then
                  $endgroup$
                  – Innat3
                  yesterday






                • 1




                  $begingroup$
                  Ah sorry, only things like {5,} work, but not {,5}.
                  $endgroup$
                  – flawr
                  yesterday






                • 2




                  $begingroup$
                  You can add it as compiler flag instead, so it's 72 bytes and the language should be changed to C# (Visual C# Interactive Compiler) with flag /u:System.Text.RegularExpressions.Regex, like this answer :)
                  $endgroup$
                  – Kevin Cruijssen
                  yesterday








                • 3




                  $begingroup$
                  Alternate regex: ^M?M?M?(C[MD]|D?C?C?C?)(X[CL]|L?X?X?X?)(I[XV]|V?I?I?I?)$. Same length, but looks weirder (which is the goal, right?)
                  $endgroup$
                  – Embodiment of Ignorance
                  yesterday


















                $begingroup$
                Couldn't you shorten {0,3} to {,3}?
                $endgroup$
                – flawr
                yesterday




                $begingroup$
                Couldn't you shorten {0,3} to {,3}?
                $endgroup$
                – flawr
                yesterday












                $begingroup$
                @flawr doesn't seem to capture anything then
                $endgroup$
                – Innat3
                yesterday




                $begingroup$
                @flawr doesn't seem to capture anything then
                $endgroup$
                – Innat3
                yesterday




                1




                1




                $begingroup$
                Ah sorry, only things like {5,} work, but not {,5}.
                $endgroup$
                – flawr
                yesterday




                $begingroup$
                Ah sorry, only things like {5,} work, but not {,5}.
                $endgroup$
                – flawr
                yesterday




                2




                2




                $begingroup$
                You can add it as compiler flag instead, so it's 72 bytes and the language should be changed to C# (Visual C# Interactive Compiler) with flag /u:System.Text.RegularExpressions.Regex, like this answer :)
                $endgroup$
                – Kevin Cruijssen
                yesterday






                $begingroup$
                You can add it as compiler flag instead, so it's 72 bytes and the language should be changed to C# (Visual C# Interactive Compiler) with flag /u:System.Text.RegularExpressions.Regex, like this answer :)
                $endgroup$
                – Kevin Cruijssen
                yesterday






                3




                3




                $begingroup$
                Alternate regex: ^M?M?M?(C[MD]|D?C?C?C?)(X[CL]|L?X?X?X?)(I[XV]|V?I?I?I?)$. Same length, but looks weirder (which is the goal, right?)
                $endgroup$
                – Embodiment of Ignorance
                yesterday






                $begingroup$
                Alternate regex: ^M?M?M?(C[MD]|D?C?C?C?)(X[CL]|L?X?X?X?)(I[XV]|V?I?I?I?)$. Same length, but looks weirder (which is the goal, right?)
                $endgroup$
                – Embodiment of Ignorance
                yesterday













                7












                $begingroup$

                Java 8, 70 bytes





                s->s.matches("M{0,3}(C[MD]|D?C{0,3})(X[CL]|L?X{0,3})(I[XV]|V?I{0,3})")


                Port of @Innat3's C# answer, so make sure to upvote him!



                Try it online.



                Explanation:



                s->                // Method with String parameter and boolean return-type
                s.matches("...") // Check if the string matches the regex fully
                // (which implicitly adds a leading "^" and trailing "$")

                M{0,3} // No, 1, 2, or 3 adjacent "M"
                ( | ) // Followed by either:
                C[MD] // A "C" with an "M" or "D" after it
                | // or:
                D? // An optional "D"
                C{0,3} // Followed by no, 1, 2, or 3 adjacent "C"
                ( | ) // Followed by either:
                X[CL] // An "X" with a "C" or "L" after it
                | // or:
                L? // An optional "L"
                X{0,3} // Followed by no, 1, 2, or 3 adjacent "X"
                ( | ) // Followed by either:
                I[XV] // An "I" with an "X" or "V" after it
                | // or:
                V? // An optional "V"
                I{0,3} // Followed by no, 1, 2, or 3 adjacent "I"





                share|improve this answer









                $endgroup$


















                  7












                  $begingroup$

                  Java 8, 70 bytes





                  s->s.matches("M{0,3}(C[MD]|D?C{0,3})(X[CL]|L?X{0,3})(I[XV]|V?I{0,3})")


                  Port of @Innat3's C# answer, so make sure to upvote him!



                  Try it online.



                  Explanation:



                  s->                // Method with String parameter and boolean return-type
                  s.matches("...") // Check if the string matches the regex fully
                  // (which implicitly adds a leading "^" and trailing "$")

                  M{0,3} // No, 1, 2, or 3 adjacent "M"
                  ( | ) // Followed by either:
                  C[MD] // A "C" with an "M" or "D" after it
                  | // or:
                  D? // An optional "D"
                  C{0,3} // Followed by no, 1, 2, or 3 adjacent "C"
                  ( | ) // Followed by either:
                  X[CL] // An "X" with a "C" or "L" after it
                  | // or:
                  L? // An optional "L"
                  X{0,3} // Followed by no, 1, 2, or 3 adjacent "X"
                  ( | ) // Followed by either:
                  I[XV] // An "I" with an "X" or "V" after it
                  | // or:
                  V? // An optional "V"
                  I{0,3} // Followed by no, 1, 2, or 3 adjacent "I"





                  share|improve this answer









                  $endgroup$
















                    7












                    7








                    7





                    $begingroup$

                    Java 8, 70 bytes





                    s->s.matches("M{0,3}(C[MD]|D?C{0,3})(X[CL]|L?X{0,3})(I[XV]|V?I{0,3})")


                    Port of @Innat3's C# answer, so make sure to upvote him!



                    Try it online.



                    Explanation:



                    s->                // Method with String parameter and boolean return-type
                    s.matches("...") // Check if the string matches the regex fully
                    // (which implicitly adds a leading "^" and trailing "$")

                    M{0,3} // No, 1, 2, or 3 adjacent "M"
                    ( | ) // Followed by either:
                    C[MD] // A "C" with an "M" or "D" after it
                    | // or:
                    D? // An optional "D"
                    C{0,3} // Followed by no, 1, 2, or 3 adjacent "C"
                    ( | ) // Followed by either:
                    X[CL] // An "X" with a "C" or "L" after it
                    | // or:
                    L? // An optional "L"
                    X{0,3} // Followed by no, 1, 2, or 3 adjacent "X"
                    ( | ) // Followed by either:
                    I[XV] // An "I" with an "X" or "V" after it
                    | // or:
                    V? // An optional "V"
                    I{0,3} // Followed by no, 1, 2, or 3 adjacent "I"





                    share|improve this answer









                    $endgroup$



                    Java 8, 70 bytes





                    s->s.matches("M{0,3}(C[MD]|D?C{0,3})(X[CL]|L?X{0,3})(I[XV]|V?I{0,3})")


                    Port of @Innat3's C# answer, so make sure to upvote him!



                    Try it online.



                    Explanation:



                    s->                // Method with String parameter and boolean return-type
                    s.matches("...") // Check if the string matches the regex fully
                    // (which implicitly adds a leading "^" and trailing "$")

                    M{0,3} // No, 1, 2, or 3 adjacent "M"
                    ( | ) // Followed by either:
                    C[MD] // A "C" with an "M" or "D" after it
                    | // or:
                    D? // An optional "D"
                    C{0,3} // Followed by no, 1, 2, or 3 adjacent "C"
                    ( | ) // Followed by either:
                    X[CL] // An "X" with a "C" or "L" after it
                    | // or:
                    L? // An optional "L"
                    X{0,3} // Followed by no, 1, 2, or 3 adjacent "X"
                    ( | ) // Followed by either:
                    I[XV] // An "I" with an "X" or "V" after it
                    | // or:
                    V? // An optional "V"
                    I{0,3} // Followed by no, 1, 2, or 3 adjacent "I"






                    share|improve this answer












                    share|improve this answer



                    share|improve this answer










                    answered yesterday









                    Kevin CruijssenKevin Cruijssen

                    42.7k571217




                    42.7k571217























                        7












                        $begingroup$


                        Wolfram Language (Mathematica), 35 bytes



                        Check[FromRomanNumeral@#<3999,1<0]&


                        Try it online!



                        5 bytes saved, thanks to @attinat



                        the limitation [1,3999] unfortunateley costs 7 bytes...

                        here is the code for any roman number




                        Wolfram Language (Mathematica), 28 bytes



                        Check[FromRomanNumeral@#,F]&


                        Try it online!



                        the above code works for any number, not just [1,3999]






                        share|improve this answer











                        $endgroup$









                        • 2




                          $begingroup$
                          @ExpiredData "The input is a non-empty string that only comprises the characters IVXLCDM."
                          $endgroup$
                          – mathmandan
                          yesterday












                        • $begingroup$
                          35 bytes. Boole is also shorter (by one byte) than using If in that way.
                          $endgroup$
                          – attinat
                          yesterday
















                        7












                        $begingroup$


                        Wolfram Language (Mathematica), 35 bytes



                        Check[FromRomanNumeral@#<3999,1<0]&


                        Try it online!



                        5 bytes saved, thanks to @attinat



                        the limitation [1,3999] unfortunateley costs 7 bytes...

                        here is the code for any roman number




                        Wolfram Language (Mathematica), 28 bytes



                        Check[FromRomanNumeral@#,F]&


                        Try it online!



                        the above code works for any number, not just [1,3999]






                        share|improve this answer











                        $endgroup$









                        • 2




                          $begingroup$
                          @ExpiredData "The input is a non-empty string that only comprises the characters IVXLCDM."
                          $endgroup$
                          – mathmandan
                          yesterday












                        • $begingroup$
                          35 bytes. Boole is also shorter (by one byte) than using If in that way.
                          $endgroup$
                          – attinat
                          yesterday














                        7












                        7








                        7





                        $begingroup$


                        Wolfram Language (Mathematica), 35 bytes



                        Check[FromRomanNumeral@#<3999,1<0]&


                        Try it online!



                        5 bytes saved, thanks to @attinat



                        the limitation [1,3999] unfortunateley costs 7 bytes...

                        here is the code for any roman number




                        Wolfram Language (Mathematica), 28 bytes



                        Check[FromRomanNumeral@#,F]&


                        Try it online!



                        the above code works for any number, not just [1,3999]






                        share|improve this answer











                        $endgroup$




                        Wolfram Language (Mathematica), 35 bytes



                        Check[FromRomanNumeral@#<3999,1<0]&


                        Try it online!



                        5 bytes saved, thanks to @attinat



                        the limitation [1,3999] unfortunateley costs 7 bytes...

                        here is the code for any roman number




                        Wolfram Language (Mathematica), 28 bytes



                        Check[FromRomanNumeral@#,F]&


                        Try it online!



                        the above code works for any number, not just [1,3999]







                        share|improve this answer














                        share|improve this answer



                        share|improve this answer








                        edited 20 hours ago

























                        answered yesterday









                        J42161217J42161217

                        14k21353




                        14k21353








                        • 2




                          $begingroup$
                          @ExpiredData "The input is a non-empty string that only comprises the characters IVXLCDM."
                          $endgroup$
                          – mathmandan
                          yesterday












                        • $begingroup$
                          35 bytes. Boole is also shorter (by one byte) than using If in that way.
                          $endgroup$
                          – attinat
                          yesterday














                        • 2




                          $begingroup$
                          @ExpiredData "The input is a non-empty string that only comprises the characters IVXLCDM."
                          $endgroup$
                          – mathmandan
                          yesterday












                        • $begingroup$
                          35 bytes. Boole is also shorter (by one byte) than using If in that way.
                          $endgroup$
                          – attinat
                          yesterday








                        2




                        2




                        $begingroup$
                        @ExpiredData "The input is a non-empty string that only comprises the characters IVXLCDM."
                        $endgroup$
                        – mathmandan
                        yesterday






                        $begingroup$
                        @ExpiredData "The input is a non-empty string that only comprises the characters IVXLCDM."
                        $endgroup$
                        – mathmandan
                        yesterday














                        $begingroup$
                        35 bytes. Boole is also shorter (by one byte) than using If in that way.
                        $endgroup$
                        – attinat
                        yesterday




                        $begingroup$
                        35 bytes. Boole is also shorter (by one byte) than using If in that way.
                        $endgroup$
                        – attinat
                        yesterday











                        7












                        $begingroup$


                        CP-1610 assembly (Intellivision),  52 ... 48  47 DECLEs1 = 59 bytes



                        Let's try this on a system that predates Perl by a good 7 years. :-)



                        Takes a pointer to a null-terminated string in R4. Sets the Zero flag if the input is a valid Roman numeral, or clears it otherwise.



                                        ROMW    10              ; use 10-bit ROM width
                        ORG $4800 ; map this program at $4800

                        ;; ------------------------------------------------------------- ;;
                        ;; test code ;;
                        ;; ------------------------------------------------------------- ;;
                        4800 EIS ; enable interrupts

                        4801 SDBD ; R5 = pointer into test case index
                        4802 MVII #ndx, R5
                        4805 MVII #$214, R3 ; R3 = backtab pointer
                        4807 MVII #11, R0 ; R0 = number of test cases

                        4809 loop SDBD ; R4 = pointer to next test case
                        480A MVI@ R5, R4
                        480B PSHR R0 ; save R0, R3, R5 onto the stack
                        480C PSHR R3
                        480D PSHR R5
                        480E CALL isRoman ; invoke our routine
                        4811 PULR R5 ; restore R5 and R3
                        4812 PULR R3

                        4813 MVII #$1A7, R0 ; use a white 'T' by default
                        4815 BEQ disp

                        4817 MVII #$137, R0 ; or a white 'F' is the Z flag was cleared

                        4819 disp MVO@ R0, R3 ; draw it
                        481A INCR R3 ; increment the backtab pointer

                        481B PULR R0 ; restore R0
                        481C DECR R0 ; and advance to the next test case, if any
                        481D BNEQ loop

                        481F DECR R7 ; loop forever

                        ;; ------------------------------------------------------------- ;;
                        ;; test cases ;;
                        ;; ------------------------------------------------------------- ;;
                        4820 ndx BIDECLE test0, test1, test2, test3
                        4828 BIDECLE test4, test5, test6, test7, test8, test9, test10

                        ; truthy
                        4836 test0 STRING "MCCXXXIV", 0
                        483F test1 STRING "CMLXXXVIII", 0
                        484A test2 STRING "DXIV", 0
                        484F test3 STRING "CI", 0

                        ; falsy
                        4852 test4 STRING "MMIXVIII", 0
                        485B test5 STRING "IVX", 0
                        485F test6 STRING "IXV", 0
                        4863 test7 STRING "MMMM", 0
                        4868 test8 STRING "XXXVX", 0
                        486E test9 STRING "IVI", 0
                        4872 test10 STRING "VIV", 0

                        ;; ------------------------------------------------------------- ;;
                        ;; routine ;;
                        ;; ------------------------------------------------------------- ;;
                        isRoman PROC

                        4876 PSHR R5 ; push the return address

                        4877 MOVR R7, R2 ; R2 = dummy 1st suffix
                        4878 MOVR R2, R5 ; R5 = pointer into table
                        4879 ADDI #@tbl-$+1,R5

                        487B @loop MVI@ R5, R1 ; R1 = main digit (M, C, X, I)
                        487C MVI@ R5, R3 ; R3 = prefix or 2nd suffix (-, D, L, V)

                        487D MVI@ R4, R0 ; R0 = next digit

                        487E CMPR R0, R3 ; if this is the prefix ...
                        487F BNEQ @main

                        4881 COMR R2 ; ... disable the suffixes
                        4882 COMR R3 ; by setting them to invalid values
                        4883 MVI@ R4, R0 ; and read R0 again

                        4884 @main CMPR R0, R1 ; if R0 is not equal to the main digit,
                        4885 BNEQ @back ; assume that this part is over

                        4887 MVI@ R4, R0 ; R0 = next digit
                        4888 CMPR R0, R1 ; if this is a 2nd occurrence
                        4889 BNEQ @suffix ; of the main digit ...

                        488B CMP@ R4, R1 ; ... it may be followed by a 3rd occurrence
                        488C BNEQ @back

                        488E MOVR R2, R0 ; if so, force the test below to succeed

                        488F @suffix CMPR R0, R2 ; otherwise, it may be either the 1st suffix
                        4890 BEQ @next
                        4892 CMPR R0, R3 ; or the 2nd suffix (these tests always fail
                        4893 BEQ @next ; if the suffixes were disabled above)

                        4895 @back DECR R4 ; the last digit either belongs to the next
                        ; iteration or is invalid

                        4896 @next MOVR R1, R2 ; use the current main digit
                        ; as the next 1st suffix

                        4897 SUBI #'I', R1 ; was it the last iteration? ...
                        4899 BNEQ @loop

                        489B CMP@ R4, R1 ; ... yes: make sure that we've also reached
                        ; the end of the input

                        489C PULR R7 ; return

                        489D @tbl DECLE 'M', '-' ; table format: main digit, 2nd suffix
                        489F DECLE 'C', 'D'
                        48A1 DECLE 'X', 'L'
                        48A3 DECLE 'I', 'V'

                        ENDP


                        How?



                        The regular expression can be rewritten as 4 groups with the same structure, provided that # is any invalid character that is guaranteed not be present in the input string.



                                         +-------+---> main digit
                        | |
                        (M[##]|#?M{0,3})(C[MD]|D?C{0,3})(X[CL]|L?X{0,3})(I[XV]|V?I{0,3})
                        || |
                        |+--+-----> prefix or second suffix
                        |
                        +---------> first suffix


                        The first suffix of the group $N$ is the main digit of the group $N-1$. Therefore, we can store the patterns with the pair $(text{main_digit}, text{second_suffix})$ alone.



                        Our routine attempts to parse the input string character by character according to these patterns and eventually checks whether the end of the string is reached.



                        Output



                        output



                        screenshot of jzIntv





                        1. A CP-1610 opcode is encoded with a 10-bit value, known as a 'DECLE'. This routine is 47 DECLEs long, starting at $4876 and ending at $48A4 (included).






                        share|improve this answer











                        $endgroup$













                        • $begingroup$
                          wouldn't this be one of the few places where fractional bytes are valid
                          $endgroup$
                          – ASCII-only
                          20 hours ago










                        • $begingroup$
                          @ASCII-only I used to think so, but I don't know for sure. See the comments of this answer for some insight about this.
                          $endgroup$
                          – Arnauld
                          20 hours ago










                        • $begingroup$
                          @ASCII-only Also, I've just found this post in meta that tends to confirm it's probably best to round to whole bytes.
                          $endgroup$
                          – Arnauld
                          20 hours ago










                        • $begingroup$
                          ah, so it's only 10 bits when it's in RAM?
                          $endgroup$
                          – ASCII-only
                          20 hours ago












                        • $begingroup$
                          The program is never stored in RAM, only in ROM. So it depends on the memory chips used in the cartridge. The CPU is designed to access either 10-bit or 16-bit ROM. The "ROMW 10" directive forces the compiler to generate code in 10-bit format.
                          $endgroup$
                          – Arnauld
                          19 hours ago
















                        7












                        $begingroup$


                        CP-1610 assembly (Intellivision),  52 ... 48  47 DECLEs1 = 59 bytes



                        Let's try this on a system that predates Perl by a good 7 years. :-)



                        Takes a pointer to a null-terminated string in R4. Sets the Zero flag if the input is a valid Roman numeral, or clears it otherwise.



                                        ROMW    10              ; use 10-bit ROM width
                        ORG $4800 ; map this program at $4800

                        ;; ------------------------------------------------------------- ;;
                        ;; test code ;;
                        ;; ------------------------------------------------------------- ;;
                        4800 EIS ; enable interrupts

                        4801 SDBD ; R5 = pointer into test case index
                        4802 MVII #ndx, R5
                        4805 MVII #$214, R3 ; R3 = backtab pointer
                        4807 MVII #11, R0 ; R0 = number of test cases

                        4809 loop SDBD ; R4 = pointer to next test case
                        480A MVI@ R5, R4
                        480B PSHR R0 ; save R0, R3, R5 onto the stack
                        480C PSHR R3
                        480D PSHR R5
                        480E CALL isRoman ; invoke our routine
                        4811 PULR R5 ; restore R5 and R3
                        4812 PULR R3

                        4813 MVII #$1A7, R0 ; use a white 'T' by default
                        4815 BEQ disp

                        4817 MVII #$137, R0 ; or a white 'F' is the Z flag was cleared

                        4819 disp MVO@ R0, R3 ; draw it
                        481A INCR R3 ; increment the backtab pointer

                        481B PULR R0 ; restore R0
                        481C DECR R0 ; and advance to the next test case, if any
                        481D BNEQ loop

                        481F DECR R7 ; loop forever

                        ;; ------------------------------------------------------------- ;;
                        ;; test cases ;;
                        ;; ------------------------------------------------------------- ;;
                        4820 ndx BIDECLE test0, test1, test2, test3
                        4828 BIDECLE test4, test5, test6, test7, test8, test9, test10

                        ; truthy
                        4836 test0 STRING "MCCXXXIV", 0
                        483F test1 STRING "CMLXXXVIII", 0
                        484A test2 STRING "DXIV", 0
                        484F test3 STRING "CI", 0

                        ; falsy
                        4852 test4 STRING "MMIXVIII", 0
                        485B test5 STRING "IVX", 0
                        485F test6 STRING "IXV", 0
                        4863 test7 STRING "MMMM", 0
                        4868 test8 STRING "XXXVX", 0
                        486E test9 STRING "IVI", 0
                        4872 test10 STRING "VIV", 0

                        ;; ------------------------------------------------------------- ;;
                        ;; routine ;;
                        ;; ------------------------------------------------------------- ;;
                        isRoman PROC

                        4876 PSHR R5 ; push the return address

                        4877 MOVR R7, R2 ; R2 = dummy 1st suffix
                        4878 MOVR R2, R5 ; R5 = pointer into table
                        4879 ADDI #@tbl-$+1,R5

                        487B @loop MVI@ R5, R1 ; R1 = main digit (M, C, X, I)
                        487C MVI@ R5, R3 ; R3 = prefix or 2nd suffix (-, D, L, V)

                        487D MVI@ R4, R0 ; R0 = next digit

                        487E CMPR R0, R3 ; if this is the prefix ...
                        487F BNEQ @main

                        4881 COMR R2 ; ... disable the suffixes
                        4882 COMR R3 ; by setting them to invalid values
                        4883 MVI@ R4, R0 ; and read R0 again

                        4884 @main CMPR R0, R1 ; if R0 is not equal to the main digit,
                        4885 BNEQ @back ; assume that this part is over

                        4887 MVI@ R4, R0 ; R0 = next digit
                        4888 CMPR R0, R1 ; if this is a 2nd occurrence
                        4889 BNEQ @suffix ; of the main digit ...

                        488B CMP@ R4, R1 ; ... it may be followed by a 3rd occurrence
                        488C BNEQ @back

                        488E MOVR R2, R0 ; if so, force the test below to succeed

                        488F @suffix CMPR R0, R2 ; otherwise, it may be either the 1st suffix
                        4890 BEQ @next
                        4892 CMPR R0, R3 ; or the 2nd suffix (these tests always fail
                        4893 BEQ @next ; if the suffixes were disabled above)

                        4895 @back DECR R4 ; the last digit either belongs to the next
                        ; iteration or is invalid

                        4896 @next MOVR R1, R2 ; use the current main digit
                        ; as the next 1st suffix

                        4897 SUBI #'I', R1 ; was it the last iteration? ...
                        4899 BNEQ @loop

                        489B CMP@ R4, R1 ; ... yes: make sure that we've also reached
                        ; the end of the input

                        489C PULR R7 ; return

                        489D @tbl DECLE 'M', '-' ; table format: main digit, 2nd suffix
                        489F DECLE 'C', 'D'
                        48A1 DECLE 'X', 'L'
                        48A3 DECLE 'I', 'V'

                        ENDP


                        How?



                        The regular expression can be rewritten as 4 groups with the same structure, provided that # is any invalid character that is guaranteed not be present in the input string.



                                         +-------+---> main digit
                        | |
                        (M[##]|#?M{0,3})(C[MD]|D?C{0,3})(X[CL]|L?X{0,3})(I[XV]|V?I{0,3})
                        || |
                        |+--+-----> prefix or second suffix
                        |
                        +---------> first suffix


                        The first suffix of the group $N$ is the main digit of the group $N-1$. Therefore, we can store the patterns with the pair $(text{main_digit}, text{second_suffix})$ alone.



                        Our routine attempts to parse the input string character by character according to these patterns and eventually checks whether the end of the string is reached.



                        Output



                        output



                        screenshot of jzIntv





                        1. A CP-1610 opcode is encoded with a 10-bit value, known as a 'DECLE'. This routine is 47 DECLEs long, starting at $4876 and ending at $48A4 (included).






                        share|improve this answer











                        $endgroup$













                        • $begingroup$
                          wouldn't this be one of the few places where fractional bytes are valid
                          $endgroup$
                          – ASCII-only
                          20 hours ago










                        • $begingroup$
                          @ASCII-only I used to think so, but I don't know for sure. See the comments of this answer for some insight about this.
                          $endgroup$
                          – Arnauld
                          20 hours ago










                        • $begingroup$
                          @ASCII-only Also, I've just found this post in meta that tends to confirm it's probably best to round to whole bytes.
                          $endgroup$
                          – Arnauld
                          20 hours ago










                        • $begingroup$
                          ah, so it's only 10 bits when it's in RAM?
                          $endgroup$
                          – ASCII-only
                          20 hours ago












                        • $begingroup$
                          The program is never stored in RAM, only in ROM. So it depends on the memory chips used in the cartridge. The CPU is designed to access either 10-bit or 16-bit ROM. The "ROMW 10" directive forces the compiler to generate code in 10-bit format.
                          $endgroup$
                          – Arnauld
                          19 hours ago














                        7












                        7








                        7





                        $begingroup$


                        CP-1610 assembly (Intellivision),  52 ... 48  47 DECLEs1 = 59 bytes



                        Let's try this on a system that predates Perl by a good 7 years. :-)



                        Takes a pointer to a null-terminated string in R4. Sets the Zero flag if the input is a valid Roman numeral, or clears it otherwise.



                                        ROMW    10              ; use 10-bit ROM width
                        ORG $4800 ; map this program at $4800

                        ;; ------------------------------------------------------------- ;;
                        ;; test code ;;
                        ;; ------------------------------------------------------------- ;;
                        4800 EIS ; enable interrupts

                        4801 SDBD ; R5 = pointer into test case index
                        4802 MVII #ndx, R5
                        4805 MVII #$214, R3 ; R3 = backtab pointer
                        4807 MVII #11, R0 ; R0 = number of test cases

                        4809 loop SDBD ; R4 = pointer to next test case
                        480A MVI@ R5, R4
                        480B PSHR R0 ; save R0, R3, R5 onto the stack
                        480C PSHR R3
                        480D PSHR R5
                        480E CALL isRoman ; invoke our routine
                        4811 PULR R5 ; restore R5 and R3
                        4812 PULR R3

                        4813 MVII #$1A7, R0 ; use a white 'T' by default
                        4815 BEQ disp

                        4817 MVII #$137, R0 ; or a white 'F' is the Z flag was cleared

                        4819 disp MVO@ R0, R3 ; draw it
                        481A INCR R3 ; increment the backtab pointer

                        481B PULR R0 ; restore R0
                        481C DECR R0 ; and advance to the next test case, if any
                        481D BNEQ loop

                        481F DECR R7 ; loop forever

                        ;; ------------------------------------------------------------- ;;
                        ;; test cases ;;
                        ;; ------------------------------------------------------------- ;;
                        4820 ndx BIDECLE test0, test1, test2, test3
                        4828 BIDECLE test4, test5, test6, test7, test8, test9, test10

                        ; truthy
                        4836 test0 STRING "MCCXXXIV", 0
                        483F test1 STRING "CMLXXXVIII", 0
                        484A test2 STRING "DXIV", 0
                        484F test3 STRING "CI", 0

                        ; falsy
                        4852 test4 STRING "MMIXVIII", 0
                        485B test5 STRING "IVX", 0
                        485F test6 STRING "IXV", 0
                        4863 test7 STRING "MMMM", 0
                        4868 test8 STRING "XXXVX", 0
                        486E test9 STRING "IVI", 0
                        4872 test10 STRING "VIV", 0

                        ;; ------------------------------------------------------------- ;;
                        ;; routine ;;
                        ;; ------------------------------------------------------------- ;;
                        isRoman PROC

                        4876 PSHR R5 ; push the return address

                        4877 MOVR R7, R2 ; R2 = dummy 1st suffix
                        4878 MOVR R2, R5 ; R5 = pointer into table
                        4879 ADDI #@tbl-$+1,R5

                        487B @loop MVI@ R5, R1 ; R1 = main digit (M, C, X, I)
                        487C MVI@ R5, R3 ; R3 = prefix or 2nd suffix (-, D, L, V)

                        487D MVI@ R4, R0 ; R0 = next digit

                        487E CMPR R0, R3 ; if this is the prefix ...
                        487F BNEQ @main

                        4881 COMR R2 ; ... disable the suffixes
                        4882 COMR R3 ; by setting them to invalid values
                        4883 MVI@ R4, R0 ; and read R0 again

                        4884 @main CMPR R0, R1 ; if R0 is not equal to the main digit,
                        4885 BNEQ @back ; assume that this part is over

                        4887 MVI@ R4, R0 ; R0 = next digit
                        4888 CMPR R0, R1 ; if this is a 2nd occurrence
                        4889 BNEQ @suffix ; of the main digit ...

                        488B CMP@ R4, R1 ; ... it may be followed by a 3rd occurrence
                        488C BNEQ @back

                        488E MOVR R2, R0 ; if so, force the test below to succeed

                        488F @suffix CMPR R0, R2 ; otherwise, it may be either the 1st suffix
                        4890 BEQ @next
                        4892 CMPR R0, R3 ; or the 2nd suffix (these tests always fail
                        4893 BEQ @next ; if the suffixes were disabled above)

                        4895 @back DECR R4 ; the last digit either belongs to the next
                        ; iteration or is invalid

                        4896 @next MOVR R1, R2 ; use the current main digit
                        ; as the next 1st suffix

                        4897 SUBI #'I', R1 ; was it the last iteration? ...
                        4899 BNEQ @loop

                        489B CMP@ R4, R1 ; ... yes: make sure that we've also reached
                        ; the end of the input

                        489C PULR R7 ; return

                        489D @tbl DECLE 'M', '-' ; table format: main digit, 2nd suffix
                        489F DECLE 'C', 'D'
                        48A1 DECLE 'X', 'L'
                        48A3 DECLE 'I', 'V'

                        ENDP


                        How?



                        The regular expression can be rewritten as 4 groups with the same structure, provided that # is any invalid character that is guaranteed not be present in the input string.



                                         +-------+---> main digit
                        | |
                        (M[##]|#?M{0,3})(C[MD]|D?C{0,3})(X[CL]|L?X{0,3})(I[XV]|V?I{0,3})
                        || |
                        |+--+-----> prefix or second suffix
                        |
                        +---------> first suffix


                        The first suffix of the group $N$ is the main digit of the group $N-1$. Therefore, we can store the patterns with the pair $(text{main_digit}, text{second_suffix})$ alone.



                        Our routine attempts to parse the input string character by character according to these patterns and eventually checks whether the end of the string is reached.



                        Output



                        output



                        screenshot of jzIntv





                        1. A CP-1610 opcode is encoded with a 10-bit value, known as a 'DECLE'. This routine is 47 DECLEs long, starting at $4876 and ending at $48A4 (included).






                        share|improve this answer











                        $endgroup$




                        CP-1610 assembly (Intellivision),  52 ... 48  47 DECLEs1 = 59 bytes



                        Let's try this on a system that predates Perl by a good 7 years. :-)



                        Takes a pointer to a null-terminated string in R4. Sets the Zero flag if the input is a valid Roman numeral, or clears it otherwise.



                                        ROMW    10              ; use 10-bit ROM width
                        ORG $4800 ; map this program at $4800

                        ;; ------------------------------------------------------------- ;;
                        ;; test code ;;
                        ;; ------------------------------------------------------------- ;;
                        4800 EIS ; enable interrupts

                        4801 SDBD ; R5 = pointer into test case index
                        4802 MVII #ndx, R5
                        4805 MVII #$214, R3 ; R3 = backtab pointer
                        4807 MVII #11, R0 ; R0 = number of test cases

                        4809 loop SDBD ; R4 = pointer to next test case
                        480A MVI@ R5, R4
                        480B PSHR R0 ; save R0, R3, R5 onto the stack
                        480C PSHR R3
                        480D PSHR R5
                        480E CALL isRoman ; invoke our routine
                        4811 PULR R5 ; restore R5 and R3
                        4812 PULR R3

                        4813 MVII #$1A7, R0 ; use a white 'T' by default
                        4815 BEQ disp

                        4817 MVII #$137, R0 ; or a white 'F' is the Z flag was cleared

                        4819 disp MVO@ R0, R3 ; draw it
                        481A INCR R3 ; increment the backtab pointer

                        481B PULR R0 ; restore R0
                        481C DECR R0 ; and advance to the next test case, if any
                        481D BNEQ loop

                        481F DECR R7 ; loop forever

                        ;; ------------------------------------------------------------- ;;
                        ;; test cases ;;
                        ;; ------------------------------------------------------------- ;;
                        4820 ndx BIDECLE test0, test1, test2, test3
                        4828 BIDECLE test4, test5, test6, test7, test8, test9, test10

                        ; truthy
                        4836 test0 STRING "MCCXXXIV", 0
                        483F test1 STRING "CMLXXXVIII", 0
                        484A test2 STRING "DXIV", 0
                        484F test3 STRING "CI", 0

                        ; falsy
                        4852 test4 STRING "MMIXVIII", 0
                        485B test5 STRING "IVX", 0
                        485F test6 STRING "IXV", 0
                        4863 test7 STRING "MMMM", 0
                        4868 test8 STRING "XXXVX", 0
                        486E test9 STRING "IVI", 0
                        4872 test10 STRING "VIV", 0

                        ;; ------------------------------------------------------------- ;;
                        ;; routine ;;
                        ;; ------------------------------------------------------------- ;;
                        isRoman PROC

                        4876 PSHR R5 ; push the return address

                        4877 MOVR R7, R2 ; R2 = dummy 1st suffix
                        4878 MOVR R2, R5 ; R5 = pointer into table
                        4879 ADDI #@tbl-$+1,R5

                        487B @loop MVI@ R5, R1 ; R1 = main digit (M, C, X, I)
                        487C MVI@ R5, R3 ; R3 = prefix or 2nd suffix (-, D, L, V)

                        487D MVI@ R4, R0 ; R0 = next digit

                        487E CMPR R0, R3 ; if this is the prefix ...
                        487F BNEQ @main

                        4881 COMR R2 ; ... disable the suffixes
                        4882 COMR R3 ; by setting them to invalid values
                        4883 MVI@ R4, R0 ; and read R0 again

                        4884 @main CMPR R0, R1 ; if R0 is not equal to the main digit,
                        4885 BNEQ @back ; assume that this part is over

                        4887 MVI@ R4, R0 ; R0 = next digit
                        4888 CMPR R0, R1 ; if this is a 2nd occurrence
                        4889 BNEQ @suffix ; of the main digit ...

                        488B CMP@ R4, R1 ; ... it may be followed by a 3rd occurrence
                        488C BNEQ @back

                        488E MOVR R2, R0 ; if so, force the test below to succeed

                        488F @suffix CMPR R0, R2 ; otherwise, it may be either the 1st suffix
                        4890 BEQ @next
                        4892 CMPR R0, R3 ; or the 2nd suffix (these tests always fail
                        4893 BEQ @next ; if the suffixes were disabled above)

                        4895 @back DECR R4 ; the last digit either belongs to the next
                        ; iteration or is invalid

                        4896 @next MOVR R1, R2 ; use the current main digit
                        ; as the next 1st suffix

                        4897 SUBI #'I', R1 ; was it the last iteration? ...
                        4899 BNEQ @loop

                        489B CMP@ R4, R1 ; ... yes: make sure that we've also reached
                        ; the end of the input

                        489C PULR R7 ; return

                        489D @tbl DECLE 'M', '-' ; table format: main digit, 2nd suffix
                        489F DECLE 'C', 'D'
                        48A1 DECLE 'X', 'L'
                        48A3 DECLE 'I', 'V'

                        ENDP


                        How?



                        The regular expression can be rewritten as 4 groups with the same structure, provided that # is any invalid character that is guaranteed not be present in the input string.



                                         +-------+---> main digit
                        | |
                        (M[##]|#?M{0,3})(C[MD]|D?C{0,3})(X[CL]|L?X{0,3})(I[XV]|V?I{0,3})
                        || |
                        |+--+-----> prefix or second suffix
                        |
                        +---------> first suffix


                        The first suffix of the group $N$ is the main digit of the group $N-1$. Therefore, we can store the patterns with the pair $(text{main_digit}, text{second_suffix})$ alone.



                        Our routine attempts to parse the input string character by character according to these patterns and eventually checks whether the end of the string is reached.



                        Output



                        output



                        screenshot of jzIntv





                        1. A CP-1610 opcode is encoded with a 10-bit value, known as a 'DECLE'. This routine is 47 DECLEs long, starting at $4876 and ending at $48A4 (included).







                        share|improve this answer














                        share|improve this answer



                        share|improve this answer








                        edited 16 hours ago

























                        answered yesterday









                        ArnauldArnauld

                        80.9k797334




                        80.9k797334












                        • $begingroup$
                          wouldn't this be one of the few places where fractional bytes are valid
                          $endgroup$
                          – ASCII-only
                          20 hours ago










                        • $begingroup$
                          @ASCII-only I used to think so, but I don't know for sure. See the comments of this answer for some insight about this.
                          $endgroup$
                          – Arnauld
                          20 hours ago










                        • $begingroup$
                          @ASCII-only Also, I've just found this post in meta that tends to confirm it's probably best to round to whole bytes.
                          $endgroup$
                          – Arnauld
                          20 hours ago










                        • $begingroup$
                          ah, so it's only 10 bits when it's in RAM?
                          $endgroup$
                          – ASCII-only
                          20 hours ago












                        • $begingroup$
                          The program is never stored in RAM, only in ROM. So it depends on the memory chips used in the cartridge. The CPU is designed to access either 10-bit or 16-bit ROM. The "ROMW 10" directive forces the compiler to generate code in 10-bit format.
                          $endgroup$
                          – Arnauld
                          19 hours ago


















                        • $begingroup$
                          wouldn't this be one of the few places where fractional bytes are valid
                          $endgroup$
                          – ASCII-only
                          20 hours ago










                        • $begingroup$
                          @ASCII-only I used to think so, but I don't know for sure. See the comments of this answer for some insight about this.
                          $endgroup$
                          – Arnauld
                          20 hours ago










                        • $begingroup$
                          @ASCII-only Also, I've just found this post in meta that tends to confirm it's probably best to round to whole bytes.
                          $endgroup$
                          – Arnauld
                          20 hours ago










                        • $begingroup$
                          ah, so it's only 10 bits when it's in RAM?
                          $endgroup$
                          – ASCII-only
                          20 hours ago












                        • $begingroup$
                          The program is never stored in RAM, only in ROM. So it depends on the memory chips used in the cartridge. The CPU is designed to access either 10-bit or 16-bit ROM. The "ROMW 10" directive forces the compiler to generate code in 10-bit format.
                          $endgroup$
                          – Arnauld
                          19 hours ago
















                        $begingroup$
                        wouldn't this be one of the few places where fractional bytes are valid
                        $endgroup$
                        – ASCII-only
                        20 hours ago




                        $begingroup$
                        wouldn't this be one of the few places where fractional bytes are valid
                        $endgroup$
                        – ASCII-only
                        20 hours ago












                        $begingroup$
                        @ASCII-only I used to think so, but I don't know for sure. See the comments of this answer for some insight about this.
                        $endgroup$
                        – Arnauld
                        20 hours ago




                        $begingroup$
                        @ASCII-only I used to think so, but I don't know for sure. See the comments of this answer for some insight about this.
                        $endgroup$
                        – Arnauld
                        20 hours ago












                        $begingroup$
                        @ASCII-only Also, I've just found this post in meta that tends to confirm it's probably best to round to whole bytes.
                        $endgroup$
                        – Arnauld
                        20 hours ago




                        $begingroup$
                        @ASCII-only Also, I've just found this post in meta that tends to confirm it's probably best to round to whole bytes.
                        $endgroup$
                        – Arnauld
                        20 hours ago












                        $begingroup$
                        ah, so it's only 10 bits when it's in RAM?
                        $endgroup$
                        – ASCII-only
                        20 hours ago






                        $begingroup$
                        ah, so it's only 10 bits when it's in RAM?
                        $endgroup$
                        – ASCII-only
                        20 hours ago














                        $begingroup$
                        The program is never stored in RAM, only in ROM. So it depends on the memory chips used in the cartridge. The CPU is designed to access either 10-bit or 16-bit ROM. The "ROMW 10" directive forces the compiler to generate code in 10-bit format.
                        $endgroup$
                        – Arnauld
                        19 hours ago




                        $begingroup$
                        The program is never stored in RAM, only in ROM. So it depends on the memory chips used in the cartridge. The CPU is designed to access either 10-bit or 16-bit ROM. The "ROMW 10" directive forces the compiler to generate code in 10-bit format.
                        $endgroup$
                        – Arnauld
                        19 hours ago











                        5












                        $begingroup$


                        R, 74 71 56 bytes



                        Thanks to @RobinRyder, @Giuseppe, & @MickyT for their suggestions how to use grep effectively with R's built in as.roman.





                        sub("^M(.+)","\1",scan(,""))%in%paste(as.roman(1:2999))


                        Try it online!






                        share|improve this answer











                        $endgroup$













                        • $begingroup$
                          as.roman won't work anyway, since it only works up to 3899 for some reason.
                          $endgroup$
                          – Giuseppe
                          yesterday












                        • $begingroup$
                          I really should read the documentation better, Probably because 4000 doesn't have a definite representation in Roman, so how'd one do 3900. This is similar to 390 and now I just found an issue with my grep where I'd have to anchor the pattern.
                          $endgroup$
                          – CT Hall
                          yesterday












                        • $begingroup$
                          @Giuseppe, addressed, using the same regex as the other answers.
                          $endgroup$
                          – CT Hall
                          yesterday










                        • $begingroup$
                          I wonder if there's a way to use .romans here...probably not.
                          $endgroup$
                          – Giuseppe
                          yesterday






                        • 2




                          $begingroup$
                          66 bytes using as.roman: first strip the initial M if there is one, then check whether the result is in as.roman(1:2999). This requires special handling of the case where the input is M.
                          $endgroup$
                          – Robin Ryder
                          yesterday
















                        5












                        $begingroup$


                        R, 74 71 56 bytes



                        Thanks to @RobinRyder, @Giuseppe, & @MickyT for their suggestions how to use grep effectively with R's built in as.roman.





                        sub("^M(.+)","\1",scan(,""))%in%paste(as.roman(1:2999))


                        Try it online!






                        share|improve this answer











                        $endgroup$













                        • $begingroup$
                          as.roman won't work anyway, since it only works up to 3899 for some reason.
                          $endgroup$
                          – Giuseppe
                          yesterday












                        • $begingroup$
                          I really should read the documentation better, Probably because 4000 doesn't have a definite representation in Roman, so how'd one do 3900. This is similar to 390 and now I just found an issue with my grep where I'd have to anchor the pattern.
                          $endgroup$
                          – CT Hall
                          yesterday












                        • $begingroup$
                          @Giuseppe, addressed, using the same regex as the other answers.
                          $endgroup$
                          – CT Hall
                          yesterday










                        • $begingroup$
                          I wonder if there's a way to use .romans here...probably not.
                          $endgroup$
                          – Giuseppe
                          yesterday






                        • 2




                          $begingroup$
                          66 bytes using as.roman: first strip the initial M if there is one, then check whether the result is in as.roman(1:2999). This requires special handling of the case where the input is M.
                          $endgroup$
                          – Robin Ryder
                          yesterday














                        5












                        5








                        5





                        $begingroup$


                        R, 74 71 56 bytes



                        Thanks to @RobinRyder, @Giuseppe, & @MickyT for their suggestions how to use grep effectively with R's built in as.roman.





                        sub("^M(.+)","\1",scan(,""))%in%paste(as.roman(1:2999))


                        Try it online!






                        share|improve this answer











                        $endgroup$




                        R, 74 71 56 bytes



                        Thanks to @RobinRyder, @Giuseppe, & @MickyT for their suggestions how to use grep effectively with R's built in as.roman.





                        sub("^M(.+)","\1",scan(,""))%in%paste(as.roman(1:2999))


                        Try it online!







                        share|improve this answer














                        share|improve this answer



                        share|improve this answer








                        edited yesterday

























                        answered yesterday









                        CT HallCT Hall

                        51110




                        51110












                        • $begingroup$
                          as.roman won't work anyway, since it only works up to 3899 for some reason.
                          $endgroup$
                          – Giuseppe
                          yesterday












                        • $begingroup$
                          I really should read the documentation better, Probably because 4000 doesn't have a definite representation in Roman, so how'd one do 3900. This is similar to 390 and now I just found an issue with my grep where I'd have to anchor the pattern.
                          $endgroup$
                          – CT Hall
                          yesterday












                        • $begingroup$
                          @Giuseppe, addressed, using the same regex as the other answers.
                          $endgroup$
                          – CT Hall
                          yesterday










                        • $begingroup$
                          I wonder if there's a way to use .romans here...probably not.
                          $endgroup$
                          – Giuseppe
                          yesterday






                        • 2




                          $begingroup$
                          66 bytes using as.roman: first strip the initial M if there is one, then check whether the result is in as.roman(1:2999). This requires special handling of the case where the input is M.
                          $endgroup$
                          – Robin Ryder
                          yesterday


















                        • $begingroup$
                          as.roman won't work anyway, since it only works up to 3899 for some reason.
                          $endgroup$
                          – Giuseppe
                          yesterday












                        • $begingroup$
                          I really should read the documentation better, Probably because 4000 doesn't have a definite representation in Roman, so how'd one do 3900. This is similar to 390 and now I just found an issue with my grep where I'd have to anchor the pattern.
                          $endgroup$
                          – CT Hall
                          yesterday












                        • $begingroup$
                          @Giuseppe, addressed, using the same regex as the other answers.
                          $endgroup$
                          – CT Hall
                          yesterday










                        • $begingroup$
                          I wonder if there's a way to use .romans here...probably not.
                          $endgroup$
                          – Giuseppe
                          yesterday






                        • 2




                          $begingroup$
                          66 bytes using as.roman: first strip the initial M if there is one, then check whether the result is in as.roman(1:2999). This requires special handling of the case where the input is M.
                          $endgroup$
                          – Robin Ryder
                          yesterday
















                        $begingroup$
                        as.roman won't work anyway, since it only works up to 3899 for some reason.
                        $endgroup$
                        – Giuseppe
                        yesterday






                        $begingroup$
                        as.roman won't work anyway, since it only works up to 3899 for some reason.
                        $endgroup$
                        – Giuseppe
                        yesterday














                        $begingroup$
                        I really should read the documentation better, Probably because 4000 doesn't have a definite representation in Roman, so how'd one do 3900. This is similar to 390 and now I just found an issue with my grep where I'd have to anchor the pattern.
                        $endgroup$
                        – CT Hall
                        yesterday






                        $begingroup$
                        I really should read the documentation better, Probably because 4000 doesn't have a definite representation in Roman, so how'd one do 3900. This is similar to 390 and now I just found an issue with my grep where I'd have to anchor the pattern.
                        $endgroup$
                        – CT Hall
                        yesterday














                        $begingroup$
                        @Giuseppe, addressed, using the same regex as the other answers.
                        $endgroup$
                        – CT Hall
                        yesterday




                        $begingroup$
                        @Giuseppe, addressed, using the same regex as the other answers.
                        $endgroup$
                        – CT Hall
                        yesterday












                        $begingroup$
                        I wonder if there's a way to use .romans here...probably not.
                        $endgroup$
                        – Giuseppe
                        yesterday




                        $begingroup$
                        I wonder if there's a way to use .romans here...probably not.
                        $endgroup$
                        – Giuseppe
                        yesterday




                        2




                        2




                        $begingroup$
                        66 bytes using as.roman: first strip the initial M if there is one, then check whether the result is in as.roman(1:2999). This requires special handling of the case where the input is M.
                        $endgroup$
                        – Robin Ryder
                        yesterday




                        $begingroup$
                        66 bytes using as.roman: first strip the initial M if there is one, then check whether the result is in as.roman(1:2999). This requires special handling of the case where the input is M.
                        $endgroup$
                        – Robin Ryder
                        yesterday











                        4












                        $begingroup$


                        Wolfram Language (Mathematica), 32 bytes



                        RomanNumeral@Range@3999~Count~#&


                        Try it online!






                        share|improve this answer









                        $endgroup$


















                          4












                          $begingroup$


                          Wolfram Language (Mathematica), 32 bytes



                          RomanNumeral@Range@3999~Count~#&


                          Try it online!






                          share|improve this answer









                          $endgroup$
















                            4












                            4








                            4





                            $begingroup$


                            Wolfram Language (Mathematica), 32 bytes



                            RomanNumeral@Range@3999~Count~#&


                            Try it online!






                            share|improve this answer









                            $endgroup$




                            Wolfram Language (Mathematica), 32 bytes



                            RomanNumeral@Range@3999~Count~#&


                            Try it online!







                            share|improve this answer












                            share|improve this answer



                            share|improve this answer










                            answered yesterday









                            Expired DataExpired Data

                            898216




                            898216























                                2












                                $begingroup$


                                Jelly,  48 47  46 bytes



                                -1 thanks to Nick Kennedy



                                5Żo7;“ÆæC‘b3ð“IVXLCDM”ị@3Ƥm2”MẋⱮ3¤ṭŻ€ṚŒpF€ḟ€0ċ


                                A monadic Link accepting a non-empty list of characters consisting only of IVXLCDM which yields either 1 (when it's a valid Roman numeral between $1$ and $3999$) or 0 (if not).



                                Try it online! Or see the test-suite.



                                How?



                                Needs updating...



                                7R;“¿ç‘ḃ2ŒQ€6¦ị - Link 1, acceptable "digits": list of characters        e.g. "IVX"
                                7R - range of seven [1,2,3,4,5,6,7]
                                “¿ç‘ - list of code-page indices [11,23]
                                ; - concatenate [1,2,3,4,5,6,7,11,23]
                                ḃ2 - to bijective base two [[1],[2],[1,1],[1,2],[2,1],[2,2],[1,1,1],[2,1,1],[2,1,1,1]]
                                ¦ - sparse application...
                                6 - ...to indices: six
                                ŒQ€ - ...do: for €ach: distinct sieve ...,[1,0],...
                                ị - index into input ["I","V","II", "IV", "VI", "IX", "III", "VII", "VIII" ]

                                “IVXLCDM”Ç3Ƥm2”MẋⱮ3¤ṭŻ€ṚŒpF€ḟ€0ċ - Main Link: list of characters
                                “IVXLCDM” - list of characters
                                3Ƥ - for infixes of length three: ("IVX","VXL","XLC","LCD","CDM")
                                Ç - call the last Link (1) as a monad
                                m2 - modulo two slice (results for "IVX", "XLC", and "CDM" only)
                                ¤ - nilad followed by link(s) as a nilad:
                                ”M - character 'M'
                                Ɱ3 - map across [1,2,3] with:
                                ẋ - repeat -> ["M", "MM", "MMM"]
                                ṭ - tack
                                Ż€ - prepend a zero to each
                                Ṛ - reverse
                                - -- now we have the table:
                                - 0 M MM MMM
                                - 0 C D CC CD DC CM CCC DCC DCCC
                                - 0 X L XX XL LX XC XXX LXX LXXX
                                - 0 I V II IV VI IX III VII VIII
                                Œp - Cartesian product -> [[0,0,0,0],[0,0,0,"I"],...,["M","CM",0,"IV"],...]
                                F€ - flatten €ach -> [[0,0,0,0],[0,0,0,'I'],...,['M','C','M',0,'I','V'],...]
                                ḟ€0 - filter out the zeros from €ach -> ["","I",...,"MCMIV",...]
                                ċ - count occurrences of the input





                                share|improve this answer











                                $endgroup$













                                • $begingroup$
                                  There seems to be a redundant space on the first line. Another byte. Another byte can be saved by using a simpler first line. Try it online!
                                  $endgroup$
                                  – Nick Kennedy
                                  21 hours ago












                                • $begingroup$
                                  Thanks, I've saved one more from it.
                                  $endgroup$
                                  – Jonathan Allan
                                  20 hours ago
















                                2












                                $begingroup$


                                Jelly,  48 47  46 bytes



                                -1 thanks to Nick Kennedy



                                5Żo7;“ÆæC‘b3ð“IVXLCDM”ị@3Ƥm2”MẋⱮ3¤ṭŻ€ṚŒpF€ḟ€0ċ


                                A monadic Link accepting a non-empty list of characters consisting only of IVXLCDM which yields either 1 (when it's a valid Roman numeral between $1$ and $3999$) or 0 (if not).



                                Try it online! Or see the test-suite.



                                How?



                                Needs updating...



                                7R;“¿ç‘ḃ2ŒQ€6¦ị - Link 1, acceptable "digits": list of characters        e.g. "IVX"
                                7R - range of seven [1,2,3,4,5,6,7]
                                “¿ç‘ - list of code-page indices [11,23]
                                ; - concatenate [1,2,3,4,5,6,7,11,23]
                                ḃ2 - to bijective base two [[1],[2],[1,1],[1,2],[2,1],[2,2],[1,1,1],[2,1,1],[2,1,1,1]]
                                ¦ - sparse application...
                                6 - ...to indices: six
                                ŒQ€ - ...do: for €ach: distinct sieve ...,[1,0],...
                                ị - index into input ["I","V","II", "IV", "VI", "IX", "III", "VII", "VIII" ]

                                “IVXLCDM”Ç3Ƥm2”MẋⱮ3¤ṭŻ€ṚŒpF€ḟ€0ċ - Main Link: list of characters
                                “IVXLCDM” - list of characters
                                3Ƥ - for infixes of length three: ("IVX","VXL","XLC","LCD","CDM")
                                Ç - call the last Link (1) as a monad
                                m2 - modulo two slice (results for "IVX", "XLC", and "CDM" only)
                                ¤ - nilad followed by link(s) as a nilad:
                                ”M - character 'M'
                                Ɱ3 - map across [1,2,3] with:
                                ẋ - repeat -> ["M", "MM", "MMM"]
                                ṭ - tack
                                Ż€ - prepend a zero to each
                                Ṛ - reverse
                                - -- now we have the table:
                                - 0 M MM MMM
                                - 0 C D CC CD DC CM CCC DCC DCCC
                                - 0 X L XX XL LX XC XXX LXX LXXX
                                - 0 I V II IV VI IX III VII VIII
                                Œp - Cartesian product -> [[0,0,0,0],[0,0,0,"I"],...,["M","CM",0,"IV"],...]
                                F€ - flatten €ach -> [[0,0,0,0],[0,0,0,'I'],...,['M','C','M',0,'I','V'],...]
                                ḟ€0 - filter out the zeros from €ach -> ["","I",...,"MCMIV",...]
                                ċ - count occurrences of the input





                                share|improve this answer











                                $endgroup$













                                • $begingroup$
                                  There seems to be a redundant space on the first line. Another byte. Another byte can be saved by using a simpler first line. Try it online!
                                  $endgroup$
                                  – Nick Kennedy
                                  21 hours ago












                                • $begingroup$
                                  Thanks, I've saved one more from it.
                                  $endgroup$
                                  – Jonathan Allan
                                  20 hours ago














                                2












                                2








                                2





                                $begingroup$


                                Jelly,  48 47  46 bytes



                                -1 thanks to Nick Kennedy



                                5Żo7;“ÆæC‘b3ð“IVXLCDM”ị@3Ƥm2”MẋⱮ3¤ṭŻ€ṚŒpF€ḟ€0ċ


                                A monadic Link accepting a non-empty list of characters consisting only of IVXLCDM which yields either 1 (when it's a valid Roman numeral between $1$ and $3999$) or 0 (if not).



                                Try it online! Or see the test-suite.



                                How?



                                Needs updating...



                                7R;“¿ç‘ḃ2ŒQ€6¦ị - Link 1, acceptable "digits": list of characters        e.g. "IVX"
                                7R - range of seven [1,2,3,4,5,6,7]
                                “¿ç‘ - list of code-page indices [11,23]
                                ; - concatenate [1,2,3,4,5,6,7,11,23]
                                ḃ2 - to bijective base two [[1],[2],[1,1],[1,2],[2,1],[2,2],[1,1,1],[2,1,1],[2,1,1,1]]
                                ¦ - sparse application...
                                6 - ...to indices: six
                                ŒQ€ - ...do: for €ach: distinct sieve ...,[1,0],...
                                ị - index into input ["I","V","II", "IV", "VI", "IX", "III", "VII", "VIII" ]

                                “IVXLCDM”Ç3Ƥm2”MẋⱮ3¤ṭŻ€ṚŒpF€ḟ€0ċ - Main Link: list of characters
                                “IVXLCDM” - list of characters
                                3Ƥ - for infixes of length three: ("IVX","VXL","XLC","LCD","CDM")
                                Ç - call the last Link (1) as a monad
                                m2 - modulo two slice (results for "IVX", "XLC", and "CDM" only)
                                ¤ - nilad followed by link(s) as a nilad:
                                ”M - character 'M'
                                Ɱ3 - map across [1,2,3] with:
                                ẋ - repeat -> ["M", "MM", "MMM"]
                                ṭ - tack
                                Ż€ - prepend a zero to each
                                Ṛ - reverse
                                - -- now we have the table:
                                - 0 M MM MMM
                                - 0 C D CC CD DC CM CCC DCC DCCC
                                - 0 X L XX XL LX XC XXX LXX LXXX
                                - 0 I V II IV VI IX III VII VIII
                                Œp - Cartesian product -> [[0,0,0,0],[0,0,0,"I"],...,["M","CM",0,"IV"],...]
                                F€ - flatten €ach -> [[0,0,0,0],[0,0,0,'I'],...,['M','C','M',0,'I','V'],...]
                                ḟ€0 - filter out the zeros from €ach -> ["","I",...,"MCMIV",...]
                                ċ - count occurrences of the input





                                share|improve this answer











                                $endgroup$




                                Jelly,  48 47  46 bytes



                                -1 thanks to Nick Kennedy



                                5Żo7;“ÆæC‘b3ð“IVXLCDM”ị@3Ƥm2”MẋⱮ3¤ṭŻ€ṚŒpF€ḟ€0ċ


                                A monadic Link accepting a non-empty list of characters consisting only of IVXLCDM which yields either 1 (when it's a valid Roman numeral between $1$ and $3999$) or 0 (if not).



                                Try it online! Or see the test-suite.



                                How?



                                Needs updating...



                                7R;“¿ç‘ḃ2ŒQ€6¦ị - Link 1, acceptable "digits": list of characters        e.g. "IVX"
                                7R - range of seven [1,2,3,4,5,6,7]
                                “¿ç‘ - list of code-page indices [11,23]
                                ; - concatenate [1,2,3,4,5,6,7,11,23]
                                ḃ2 - to bijective base two [[1],[2],[1,1],[1,2],[2,1],[2,2],[1,1,1],[2,1,1],[2,1,1,1]]
                                ¦ - sparse application...
                                6 - ...to indices: six
                                ŒQ€ - ...do: for €ach: distinct sieve ...,[1,0],...
                                ị - index into input ["I","V","II", "IV", "VI", "IX", "III", "VII", "VIII" ]

                                “IVXLCDM”Ç3Ƥm2”MẋⱮ3¤ṭŻ€ṚŒpF€ḟ€0ċ - Main Link: list of characters
                                “IVXLCDM” - list of characters
                                3Ƥ - for infixes of length three: ("IVX","VXL","XLC","LCD","CDM")
                                Ç - call the last Link (1) as a monad
                                m2 - modulo two slice (results for "IVX", "XLC", and "CDM" only)
                                ¤ - nilad followed by link(s) as a nilad:
                                ”M - character 'M'
                                Ɱ3 - map across [1,2,3] with:
                                ẋ - repeat -> ["M", "MM", "MMM"]
                                ṭ - tack
                                Ż€ - prepend a zero to each
                                Ṛ - reverse
                                - -- now we have the table:
                                - 0 M MM MMM
                                - 0 C D CC CD DC CM CCC DCC DCCC
                                - 0 X L XX XL LX XC XXX LXX LXXX
                                - 0 I V II IV VI IX III VII VIII
                                Œp - Cartesian product -> [[0,0,0,0],[0,0,0,"I"],...,["M","CM",0,"IV"],...]
                                F€ - flatten €ach -> [[0,0,0,0],[0,0,0,'I'],...,['M','C','M',0,'I','V'],...]
                                ḟ€0 - filter out the zeros from €ach -> ["","I",...,"MCMIV",...]
                                ċ - count occurrences of the input






                                share|improve this answer














                                share|improve this answer



                                share|improve this answer








                                edited 20 hours ago

























                                answered yesterday









                                Jonathan AllanJonathan Allan

                                54.2k537174




                                54.2k537174












                                • $begingroup$
                                  There seems to be a redundant space on the first line. Another byte. Another byte can be saved by using a simpler first line. Try it online!
                                  $endgroup$
                                  – Nick Kennedy
                                  21 hours ago












                                • $begingroup$
                                  Thanks, I've saved one more from it.
                                  $endgroup$
                                  – Jonathan Allan
                                  20 hours ago


















                                • $begingroup$
                                  There seems to be a redundant space on the first line. Another byte. Another byte can be saved by using a simpler first line. Try it online!
                                  $endgroup$
                                  – Nick Kennedy
                                  21 hours ago












                                • $begingroup$
                                  Thanks, I've saved one more from it.
                                  $endgroup$
                                  – Jonathan Allan
                                  20 hours ago
















                                $begingroup$
                                There seems to be a redundant space on the first line. Another byte. Another byte can be saved by using a simpler first line. Try it online!
                                $endgroup$
                                – Nick Kennedy
                                21 hours ago






                                $begingroup$
                                There seems to be a redundant space on the first line. Another byte. Another byte can be saved by using a simpler first line. Try it online!
                                $endgroup$
                                – Nick Kennedy
                                21 hours ago














                                $begingroup$
                                Thanks, I've saved one more from it.
                                $endgroup$
                                – Jonathan Allan
                                20 hours ago




                                $begingroup$
                                Thanks, I've saved one more from it.
                                $endgroup$
                                – Jonathan Allan
                                20 hours ago











                                1












                                $begingroup$

                                Perl 5 (-p), 57 bytes



                                $_=/^M*(C[MD]|D?C*)(X[CL]|L?X*)(I[XV]|V?I*)$/&!/(.)1{3}/


                                TIO




                                • uses almost the same regular expression except {0,3} quantifier was changed by *


                                • &!/(.)1{3}/ to ensure the same character can't occur 4 times in a row.

                                • can't be golfed with -/(.)1{3}/ because would give-1 for IIIIVI for example






                                share|improve this answer











                                $endgroup$


















                                  1












                                  $begingroup$

                                  Perl 5 (-p), 57 bytes



                                  $_=/^M*(C[MD]|D?C*)(X[CL]|L?X*)(I[XV]|V?I*)$/&!/(.)1{3}/


                                  TIO




                                  • uses almost the same regular expression except {0,3} quantifier was changed by *


                                  • &!/(.)1{3}/ to ensure the same character can't occur 4 times in a row.

                                  • can't be golfed with -/(.)1{3}/ because would give-1 for IIIIVI for example






                                  share|improve this answer











                                  $endgroup$
















                                    1












                                    1








                                    1





                                    $begingroup$

                                    Perl 5 (-p), 57 bytes



                                    $_=/^M*(C[MD]|D?C*)(X[CL]|L?X*)(I[XV]|V?I*)$/&!/(.)1{3}/


                                    TIO




                                    • uses almost the same regular expression except {0,3} quantifier was changed by *


                                    • &!/(.)1{3}/ to ensure the same character can't occur 4 times in a row.

                                    • can't be golfed with -/(.)1{3}/ because would give-1 for IIIIVI for example






                                    share|improve this answer











                                    $endgroup$



                                    Perl 5 (-p), 57 bytes



                                    $_=/^M*(C[MD]|D?C*)(X[CL]|L?X*)(I[XV]|V?I*)$/&!/(.)1{3}/


                                    TIO




                                    • uses almost the same regular expression except {0,3} quantifier was changed by *


                                    • &!/(.)1{3}/ to ensure the same character can't occur 4 times in a row.

                                    • can't be golfed with -/(.)1{3}/ because would give-1 for IIIIVI for example







                                    share|improve this answer














                                    share|improve this answer



                                    share|improve this answer








                                    edited yesterday

























                                    answered yesterday









                                    Nahuel FouilleulNahuel Fouilleul

                                    3,035211




                                    3,035211























                                        1












                                        $begingroup$


                                        Python 2, 81 bytes





                                        import re
                                        re.compile('M{,3}(D?C{,3}|C[DM])(L?X{,3}|X[LC])(V?I{,3}|I[VX])$').match


                                        Try it online!



                                        Let's look at the last part of the regex, which matching the Roman numerals up to 9 (including the empty string)



                                        V?I{,3}|I[VX]


                                        This has two alternatives separated by |:





                                        • V?I{,3}: An optional V followed by up to 3 I's. This matches the empty string I,II,III, V, VI,VII,VIII.


                                        • I[VX]: An I followed by a V or X. This matches IV and IX.


                                        The same things with X,L,C matching the tens, with C,D,M matches the hundreds, and finally ^M{,3} allows up to 3 M's (thousands) at the start.



                                        I tried generating the template for each trio of characters rather than writing it 3 times, but this was a lot longer.






                                        share|improve this answer











                                        $endgroup$













                                        • $begingroup$
                                          No need for the ^ anchor at the beginning; match already implies it matches at the beginning of the string.
                                          $endgroup$
                                          – ShadowRanger
                                          yesterday










                                        • $begingroup$
                                          @ShadowRanger Thanks, I removed the ^.
                                          $endgroup$
                                          – xnor
                                          yesterday










                                        • $begingroup$
                                          Although I think you messed up the count in the edit; should be 83, not 81.
                                          $endgroup$
                                          – ShadowRanger
                                          yesterday










                                        • $begingroup$
                                          @ShadowRanger The count is 81 because the f= isn't included in the code since anonynomous functions are allowed. It's just for TIO.
                                          $endgroup$
                                          – xnor
                                          yesterday






                                        • 1




                                          $begingroup$
                                          Ah, makes sense. Annoying there's no way to organize it to hide that in the header or footer, but yeah, unassigned lambdas are legal, so unassigned bound methods of compiled regex should be good too.
                                          $endgroup$
                                          – ShadowRanger
                                          yesterday
















                                        1












                                        $begingroup$


                                        Python 2, 81 bytes





                                        import re
                                        re.compile('M{,3}(D?C{,3}|C[DM])(L?X{,3}|X[LC])(V?I{,3}|I[VX])$').match


                                        Try it online!



                                        Let's look at the last part of the regex, which matching the Roman numerals up to 9 (including the empty string)



                                        V?I{,3}|I[VX]


                                        This has two alternatives separated by |:





                                        • V?I{,3}: An optional V followed by up to 3 I's. This matches the empty string I,II,III, V, VI,VII,VIII.


                                        • I[VX]: An I followed by a V or X. This matches IV and IX.


                                        The same things with X,L,C matching the tens, with C,D,M matches the hundreds, and finally ^M{,3} allows up to 3 M's (thousands) at the start.



                                        I tried generating the template for each trio of characters rather than writing it 3 times, but this was a lot longer.






                                        share|improve this answer











                                        $endgroup$













                                        • $begingroup$
                                          No need for the ^ anchor at the beginning; match already implies it matches at the beginning of the string.
                                          $endgroup$
                                          – ShadowRanger
                                          yesterday










                                        • $begingroup$
                                          @ShadowRanger Thanks, I removed the ^.
                                          $endgroup$
                                          – xnor
                                          yesterday










                                        • $begingroup$
                                          Although I think you messed up the count in the edit; should be 83, not 81.
                                          $endgroup$
                                          – ShadowRanger
                                          yesterday










                                        • $begingroup$
                                          @ShadowRanger The count is 81 because the f= isn't included in the code since anonynomous functions are allowed. It's just for TIO.
                                          $endgroup$
                                          – xnor
                                          yesterday






                                        • 1




                                          $begingroup$
                                          Ah, makes sense. Annoying there's no way to organize it to hide that in the header or footer, but yeah, unassigned lambdas are legal, so unassigned bound methods of compiled regex should be good too.
                                          $endgroup$
                                          – ShadowRanger
                                          yesterday














                                        1












                                        1








                                        1





                                        $begingroup$


                                        Python 2, 81 bytes





                                        import re
                                        re.compile('M{,3}(D?C{,3}|C[DM])(L?X{,3}|X[LC])(V?I{,3}|I[VX])$').match


                                        Try it online!



                                        Let's look at the last part of the regex, which matching the Roman numerals up to 9 (including the empty string)



                                        V?I{,3}|I[VX]


                                        This has two alternatives separated by |:





                                        • V?I{,3}: An optional V followed by up to 3 I's. This matches the empty string I,II,III, V, VI,VII,VIII.


                                        • I[VX]: An I followed by a V or X. This matches IV and IX.


                                        The same things with X,L,C matching the tens, with C,D,M matches the hundreds, and finally ^M{,3} allows up to 3 M's (thousands) at the start.



                                        I tried generating the template for each trio of characters rather than writing it 3 times, but this was a lot longer.






                                        share|improve this answer











                                        $endgroup$




                                        Python 2, 81 bytes





                                        import re
                                        re.compile('M{,3}(D?C{,3}|C[DM])(L?X{,3}|X[LC])(V?I{,3}|I[VX])$').match


                                        Try it online!



                                        Let's look at the last part of the regex, which matching the Roman numerals up to 9 (including the empty string)



                                        V?I{,3}|I[VX]


                                        This has two alternatives separated by |:





                                        • V?I{,3}: An optional V followed by up to 3 I's. This matches the empty string I,II,III, V, VI,VII,VIII.


                                        • I[VX]: An I followed by a V or X. This matches IV and IX.


                                        The same things with X,L,C matching the tens, with C,D,M matches the hundreds, and finally ^M{,3} allows up to 3 M's (thousands) at the start.



                                        I tried generating the template for each trio of characters rather than writing it 3 times, but this was a lot longer.







                                        share|improve this answer














                                        share|improve this answer



                                        share|improve this answer








                                        edited yesterday

























                                        answered yesterday









                                        xnorxnor

                                        94k18192451




                                        94k18192451












                                        • $begingroup$
                                          No need for the ^ anchor at the beginning; match already implies it matches at the beginning of the string.
                                          $endgroup$
                                          – ShadowRanger
                                          yesterday










                                        • $begingroup$
                                          @ShadowRanger Thanks, I removed the ^.
                                          $endgroup$
                                          – xnor
                                          yesterday










                                        • $begingroup$
                                          Although I think you messed up the count in the edit; should be 83, not 81.
                                          $endgroup$
                                          – ShadowRanger
                                          yesterday










                                        • $begingroup$
                                          @ShadowRanger The count is 81 because the f= isn't included in the code since anonynomous functions are allowed. It's just for TIO.
                                          $endgroup$
                                          – xnor
                                          yesterday






                                        • 1




                                          $begingroup$
                                          Ah, makes sense. Annoying there's no way to organize it to hide that in the header or footer, but yeah, unassigned lambdas are legal, so unassigned bound methods of compiled regex should be good too.
                                          $endgroup$
                                          – ShadowRanger
                                          yesterday


















                                        • $begingroup$
                                          No need for the ^ anchor at the beginning; match already implies it matches at the beginning of the string.
                                          $endgroup$
                                          – ShadowRanger
                                          yesterday










                                        • $begingroup$
                                          @ShadowRanger Thanks, I removed the ^.
                                          $endgroup$
                                          – xnor
                                          yesterday










                                        • $begingroup$
                                          Although I think you messed up the count in the edit; should be 83, not 81.
                                          $endgroup$
                                          – ShadowRanger
                                          yesterday










                                        • $begingroup$
                                          @ShadowRanger The count is 81 because the f= isn't included in the code since anonynomous functions are allowed. It's just for TIO.
                                          $endgroup$
                                          – xnor
                                          yesterday






                                        • 1




                                          $begingroup$
                                          Ah, makes sense. Annoying there's no way to organize it to hide that in the header or footer, but yeah, unassigned lambdas are legal, so unassigned bound methods of compiled regex should be good too.
                                          $endgroup$
                                          – ShadowRanger
                                          yesterday
















                                        $begingroup$
                                        No need for the ^ anchor at the beginning; match already implies it matches at the beginning of the string.
                                        $endgroup$
                                        – ShadowRanger
                                        yesterday




                                        $begingroup$
                                        No need for the ^ anchor at the beginning; match already implies it matches at the beginning of the string.
                                        $endgroup$
                                        – ShadowRanger
                                        yesterday












                                        $begingroup$
                                        @ShadowRanger Thanks, I removed the ^.
                                        $endgroup$
                                        – xnor
                                        yesterday




                                        $begingroup$
                                        @ShadowRanger Thanks, I removed the ^.
                                        $endgroup$
                                        – xnor
                                        yesterday












                                        $begingroup$
                                        Although I think you messed up the count in the edit; should be 83, not 81.
                                        $endgroup$
                                        – ShadowRanger
                                        yesterday




                                        $begingroup$
                                        Although I think you messed up the count in the edit; should be 83, not 81.
                                        $endgroup$
                                        – ShadowRanger
                                        yesterday












                                        $begingroup$
                                        @ShadowRanger The count is 81 because the f= isn't included in the code since anonynomous functions are allowed. It's just for TIO.
                                        $endgroup$
                                        – xnor
                                        yesterday




                                        $begingroup$
                                        @ShadowRanger The count is 81 because the f= isn't included in the code since anonynomous functions are allowed. It's just for TIO.
                                        $endgroup$
                                        – xnor
                                        yesterday




                                        1




                                        1




                                        $begingroup$
                                        Ah, makes sense. Annoying there's no way to organize it to hide that in the header or footer, but yeah, unassigned lambdas are legal, so unassigned bound methods of compiled regex should be good too.
                                        $endgroup$
                                        – ShadowRanger
                                        yesterday




                                        $begingroup$
                                        Ah, makes sense. Annoying there's no way to organize it to hide that in the header or footer, but yeah, unassigned lambdas are legal, so unassigned bound methods of compiled regex should be good too.
                                        $endgroup$
                                        – ShadowRanger
                                        yesterday











                                        1












                                        $begingroup$


                                        Retina, 56 51 bytes



                                        (.)1{3}
                                        0
                                        ^M*(C[MD]|D?C*)(X[CL]|L?X*)(I[XV]|V?I*)$


                                        Port of @NahuelFouilleul's Perl 5 answer, so make sure to upvote him!



                                        Try it online or verify all test cases.



                                        Explanation:





                                        (.)1{3}        # If four adjacent characters can be found which are the same
                                        0 # Replace it with a 0

                                        ^...$ # Then check if the string matches the following fully:
                                        M* # No or any amount of adjacent "M"
                                        ( | ) # Followed by either:
                                        C[MD] # A "C" with an "M" or "D" after it
                                        | # or:
                                        D? # An optional "D"
                                        C* # Followed by no or any amount of adjacent "C"
                                        ( | ) # Followed by either:
                                        X[CL] # An "X" with a "C" or "L" after it
                                        | # or:
                                        L? # An optional "L"
                                        X* # Followed by no or any amount of adjacent "X"
                                        ( | ) # Followed by either:
                                        I[XV] # An "I" with an "X" or "V" after it
                                        | # or:
                                        V? # An optional "V"
                                        I* # Followed by no or any amount of adjacent "I"





                                        share|improve this answer











                                        $endgroup$


















                                          1












                                          $begingroup$


                                          Retina, 56 51 bytes



                                          (.)1{3}
                                          0
                                          ^M*(C[MD]|D?C*)(X[CL]|L?X*)(I[XV]|V?I*)$


                                          Port of @NahuelFouilleul's Perl 5 answer, so make sure to upvote him!



                                          Try it online or verify all test cases.



                                          Explanation:





                                          (.)1{3}        # If four adjacent characters can be found which are the same
                                          0 # Replace it with a 0

                                          ^...$ # Then check if the string matches the following fully:
                                          M* # No or any amount of adjacent "M"
                                          ( | ) # Followed by either:
                                          C[MD] # A "C" with an "M" or "D" after it
                                          | # or:
                                          D? # An optional "D"
                                          C* # Followed by no or any amount of adjacent "C"
                                          ( | ) # Followed by either:
                                          X[CL] # An "X" with a "C" or "L" after it
                                          | # or:
                                          L? # An optional "L"
                                          X* # Followed by no or any amount of adjacent "X"
                                          ( | ) # Followed by either:
                                          I[XV] # An "I" with an "X" or "V" after it
                                          | # or:
                                          V? # An optional "V"
                                          I* # Followed by no or any amount of adjacent "I"





                                          share|improve this answer











                                          $endgroup$
















                                            1












                                            1








                                            1





                                            $begingroup$


                                            Retina, 56 51 bytes



                                            (.)1{3}
                                            0
                                            ^M*(C[MD]|D?C*)(X[CL]|L?X*)(I[XV]|V?I*)$


                                            Port of @NahuelFouilleul's Perl 5 answer, so make sure to upvote him!



                                            Try it online or verify all test cases.



                                            Explanation:





                                            (.)1{3}        # If four adjacent characters can be found which are the same
                                            0 # Replace it with a 0

                                            ^...$ # Then check if the string matches the following fully:
                                            M* # No or any amount of adjacent "M"
                                            ( | ) # Followed by either:
                                            C[MD] # A "C" with an "M" or "D" after it
                                            | # or:
                                            D? # An optional "D"
                                            C* # Followed by no or any amount of adjacent "C"
                                            ( | ) # Followed by either:
                                            X[CL] # An "X" with a "C" or "L" after it
                                            | # or:
                                            L? # An optional "L"
                                            X* # Followed by no or any amount of adjacent "X"
                                            ( | ) # Followed by either:
                                            I[XV] # An "I" with an "X" or "V" after it
                                            | # or:
                                            V? # An optional "V"
                                            I* # Followed by no or any amount of adjacent "I"





                                            share|improve this answer











                                            $endgroup$




                                            Retina, 56 51 bytes



                                            (.)1{3}
                                            0
                                            ^M*(C[MD]|D?C*)(X[CL]|L?X*)(I[XV]|V?I*)$


                                            Port of @NahuelFouilleul's Perl 5 answer, so make sure to upvote him!



                                            Try it online or verify all test cases.



                                            Explanation:





                                            (.)1{3}        # If four adjacent characters can be found which are the same
                                            0 # Replace it with a 0

                                            ^...$ # Then check if the string matches the following fully:
                                            M* # No or any amount of adjacent "M"
                                            ( | ) # Followed by either:
                                            C[MD] # A "C" with an "M" or "D" after it
                                            | # or:
                                            D? # An optional "D"
                                            C* # Followed by no or any amount of adjacent "C"
                                            ( | ) # Followed by either:
                                            X[CL] # An "X" with a "C" or "L" after it
                                            | # or:
                                            L? # An optional "L"
                                            X* # Followed by no or any amount of adjacent "X"
                                            ( | ) # Followed by either:
                                            I[XV] # An "I" with an "X" or "V" after it
                                            | # or:
                                            V? # An optional "V"
                                            I* # Followed by no or any amount of adjacent "I"






                                            share|improve this answer














                                            share|improve this answer



                                            share|improve this answer








                                            edited 19 hours ago

























                                            answered 20 hours ago









                                            Kevin CruijssenKevin Cruijssen

                                            42.7k571217




                                            42.7k571217























                                                0












                                                $begingroup$


                                                05AB1E, 61 bytes



                                                •1∞Γ'иÛnuÞ₂…•Ž8вв€SÐ)v.•6#&‘нδ•u3ôNèyè}'M3L×)Rεõš}`3Fâ}€˜JIå


                                                Try it online or verify all test cases.



                                                Explanation:





                                                •1∞Γ'иÛnuÞ₂…•             '# Push compressed integer 397940501547566186191992778
                                                Ž8в # Push compressed integer 2112
                                                в # Convert the integer to Base-2112 as list:
                                                # [1,11,111,12,2,21,211,2111,10]
                                                €S # Convert each number to a list of digits
                                                Ð # Triplicate this list
                                                ) # And wrap it into a list of lists (of lists)
                                                v # Loop `y` over each these three lists:
                                                .•6#&‘нδ• # Push compressed string "xivcxlmcd"
                                                u # Uppercased
                                                3ô # And split into parts of size 3: ["XIV","CXL","MCD"]
                                                Nè # Use the loop index to get the current part
                                                yè # And index the list of lists of digits into this string
                                                }'M '# After the loop: push "M"
                                                3L # Push list [1,2,3]
                                                × # Repeat the "M" that many times: ["M","MM","MMM"]
                                                ) # Wrap all lists on the stack into a list:
                                                # [[["I"],["I","I"],["I","I","I"],["I","V"],["V"],["V","I"],["V","I","I"],["V","I","I","I"],["I","X"]],[["X"],["X","X"],["X","X","X"],["X","L"],["L"],["L","X"],["L","X","X"],["L","X","X","X"],["X","C"]],[["C"],["C","C"],["C","C","C"],["C","D"],["D"],["D","C"],["D","C","C"],["D","C","C","C"],["C","M"]],["M","MM","MMM"]]
                                                R # Reverse this list
                                                εõš} # Prepend an empty string "" before each inner list
                                                ` # Push the four lists onto the stack
                                                3F # Loop 3 times:
                                                â # Take the cartesian product of the two top lists
                                                }€˜ # After the loop: flatten each inner list
                                                J # Join each inner list together to a single string
                                                Iå # And check if the input is in this list
                                                # (after which the result is output implicitly)


                                                See this 05AB1E tip of mine (sections How to compress strings not part of the dictionary?, How to compress large integers?, and How to compress integer lists?) to understand why:





                                                • •1∞Γ'иÛnuÞ₂…• is 397940501547566186191992778


                                                • Ž8в is 2112


                                                • •1∞Γ'иÛnuÞ₂…•Ž8вв is [1,11,111,12,2,21,211,2111,10]


                                                • .•6#&‘нδ• is "xivcxlmcd"






                                                share|improve this answer











                                                $endgroup$


















                                                  0












                                                  $begingroup$


                                                  05AB1E, 61 bytes



                                                  •1∞Γ'иÛnuÞ₂…•Ž8вв€SÐ)v.•6#&‘нδ•u3ôNèyè}'M3L×)Rεõš}`3Fâ}€˜JIå


                                                  Try it online or verify all test cases.



                                                  Explanation:





                                                  •1∞Γ'иÛnuÞ₂…•             '# Push compressed integer 397940501547566186191992778
                                                  Ž8в # Push compressed integer 2112
                                                  в # Convert the integer to Base-2112 as list:
                                                  # [1,11,111,12,2,21,211,2111,10]
                                                  €S # Convert each number to a list of digits
                                                  Ð # Triplicate this list
                                                  ) # And wrap it into a list of lists (of lists)
                                                  v # Loop `y` over each these three lists:
                                                  .•6#&‘нδ• # Push compressed string "xivcxlmcd"
                                                  u # Uppercased
                                                  3ô # And split into parts of size 3: ["XIV","CXL","MCD"]
                                                  Nè # Use the loop index to get the current part
                                                  yè # And index the list of lists of digits into this string
                                                  }'M '# After the loop: push "M"
                                                  3L # Push list [1,2,3]
                                                  × # Repeat the "M" that many times: ["M","MM","MMM"]
                                                  ) # Wrap all lists on the stack into a list:
                                                  # [[["I"],["I","I"],["I","I","I"],["I","V"],["V"],["V","I"],["V","I","I"],["V","I","I","I"],["I","X"]],[["X"],["X","X"],["X","X","X"],["X","L"],["L"],["L","X"],["L","X","X"],["L","X","X","X"],["X","C"]],[["C"],["C","C"],["C","C","C"],["C","D"],["D"],["D","C"],["D","C","C"],["D","C","C","C"],["C","M"]],["M","MM","MMM"]]
                                                  R # Reverse this list
                                                  εõš} # Prepend an empty string "" before each inner list
                                                  ` # Push the four lists onto the stack
                                                  3F # Loop 3 times:
                                                  â # Take the cartesian product of the two top lists
                                                  }€˜ # After the loop: flatten each inner list
                                                  J # Join each inner list together to a single string
                                                  Iå # And check if the input is in this list
                                                  # (after which the result is output implicitly)


                                                  See this 05AB1E tip of mine (sections How to compress strings not part of the dictionary?, How to compress large integers?, and How to compress integer lists?) to understand why:





                                                  • •1∞Γ'иÛnuÞ₂…• is 397940501547566186191992778


                                                  • Ž8в is 2112


                                                  • •1∞Γ'иÛnuÞ₂…•Ž8вв is [1,11,111,12,2,21,211,2111,10]


                                                  • .•6#&‘нδ• is "xivcxlmcd"






                                                  share|improve this answer











                                                  $endgroup$
















                                                    0












                                                    0








                                                    0





                                                    $begingroup$


                                                    05AB1E, 61 bytes



                                                    •1∞Γ'иÛnuÞ₂…•Ž8вв€SÐ)v.•6#&‘нδ•u3ôNèyè}'M3L×)Rεõš}`3Fâ}€˜JIå


                                                    Try it online or verify all test cases.



                                                    Explanation:





                                                    •1∞Γ'иÛnuÞ₂…•             '# Push compressed integer 397940501547566186191992778
                                                    Ž8в # Push compressed integer 2112
                                                    в # Convert the integer to Base-2112 as list:
                                                    # [1,11,111,12,2,21,211,2111,10]
                                                    €S # Convert each number to a list of digits
                                                    Ð # Triplicate this list
                                                    ) # And wrap it into a list of lists (of lists)
                                                    v # Loop `y` over each these three lists:
                                                    .•6#&‘нδ• # Push compressed string "xivcxlmcd"
                                                    u # Uppercased
                                                    3ô # And split into parts of size 3: ["XIV","CXL","MCD"]
                                                    Nè # Use the loop index to get the current part
                                                    yè # And index the list of lists of digits into this string
                                                    }'M '# After the loop: push "M"
                                                    3L # Push list [1,2,3]
                                                    × # Repeat the "M" that many times: ["M","MM","MMM"]
                                                    ) # Wrap all lists on the stack into a list:
                                                    # [[["I"],["I","I"],["I","I","I"],["I","V"],["V"],["V","I"],["V","I","I"],["V","I","I","I"],["I","X"]],[["X"],["X","X"],["X","X","X"],["X","L"],["L"],["L","X"],["L","X","X"],["L","X","X","X"],["X","C"]],[["C"],["C","C"],["C","C","C"],["C","D"],["D"],["D","C"],["D","C","C"],["D","C","C","C"],["C","M"]],["M","MM","MMM"]]
                                                    R # Reverse this list
                                                    εõš} # Prepend an empty string "" before each inner list
                                                    ` # Push the four lists onto the stack
                                                    3F # Loop 3 times:
                                                    â # Take the cartesian product of the two top lists
                                                    }€˜ # After the loop: flatten each inner list
                                                    J # Join each inner list together to a single string
                                                    Iå # And check if the input is in this list
                                                    # (after which the result is output implicitly)


                                                    See this 05AB1E tip of mine (sections How to compress strings not part of the dictionary?, How to compress large integers?, and How to compress integer lists?) to understand why:





                                                    • •1∞Γ'иÛnuÞ₂…• is 397940501547566186191992778


                                                    • Ž8в is 2112


                                                    • •1∞Γ'иÛnuÞ₂…•Ž8вв is [1,11,111,12,2,21,211,2111,10]


                                                    • .•6#&‘нδ• is "xivcxlmcd"






                                                    share|improve this answer











                                                    $endgroup$




                                                    05AB1E, 61 bytes



                                                    •1∞Γ'иÛnuÞ₂…•Ž8вв€SÐ)v.•6#&‘нδ•u3ôNèyè}'M3L×)Rεõš}`3Fâ}€˜JIå


                                                    Try it online or verify all test cases.



                                                    Explanation:





                                                    •1∞Γ'иÛnuÞ₂…•             '# Push compressed integer 397940501547566186191992778
                                                    Ž8в # Push compressed integer 2112
                                                    в # Convert the integer to Base-2112 as list:
                                                    # [1,11,111,12,2,21,211,2111,10]
                                                    €S # Convert each number to a list of digits
                                                    Ð # Triplicate this list
                                                    ) # And wrap it into a list of lists (of lists)
                                                    v # Loop `y` over each these three lists:
                                                    .•6#&‘нδ• # Push compressed string "xivcxlmcd"
                                                    u # Uppercased
                                                    3ô # And split into parts of size 3: ["XIV","CXL","MCD"]
                                                    Nè # Use the loop index to get the current part
                                                    yè # And index the list of lists of digits into this string
                                                    }'M '# After the loop: push "M"
                                                    3L # Push list [1,2,3]
                                                    × # Repeat the "M" that many times: ["M","MM","MMM"]
                                                    ) # Wrap all lists on the stack into a list:
                                                    # [[["I"],["I","I"],["I","I","I"],["I","V"],["V"],["V","I"],["V","I","I"],["V","I","I","I"],["I","X"]],[["X"],["X","X"],["X","X","X"],["X","L"],["L"],["L","X"],["L","X","X"],["L","X","X","X"],["X","C"]],[["C"],["C","C"],["C","C","C"],["C","D"],["D"],["D","C"],["D","C","C"],["D","C","C","C"],["C","M"]],["M","MM","MMM"]]
                                                    R # Reverse this list
                                                    εõš} # Prepend an empty string "" before each inner list
                                                    ` # Push the four lists onto the stack
                                                    3F # Loop 3 times:
                                                    â # Take the cartesian product of the two top lists
                                                    }€˜ # After the loop: flatten each inner list
                                                    J # Join each inner list together to a single string
                                                    Iå # And check if the input is in this list
                                                    # (after which the result is output implicitly)


                                                    See this 05AB1E tip of mine (sections How to compress strings not part of the dictionary?, How to compress large integers?, and How to compress integer lists?) to understand why:





                                                    • •1∞Γ'иÛnuÞ₂…• is 397940501547566186191992778


                                                    • Ž8в is 2112


                                                    • •1∞Γ'иÛnuÞ₂…•Ž8вв is [1,11,111,12,2,21,211,2111,10]


                                                    • .•6#&‘нδ• is "xivcxlmcd"







                                                    share|improve this answer














                                                    share|improve this answer



                                                    share|improve this answer








                                                    edited 21 hours ago

























                                                    answered yesterday









                                                    Kevin CruijssenKevin Cruijssen

                                                    42.7k571217




                                                    42.7k571217























                                                        0












                                                        $begingroup$

                                                        perl -MRegexp::Common -pe, 34 bytes



                                                        $_=/^$RE{num}{roman}$/&!/(.)1{3}/


                                                        The &!/(.)1{3}/ part is necessary, because Regexp::Common allows four (but not five) of the same characters in a row. That way, it matches roman numbers used on clock faces, where IIII is often used for 4.






                                                        share|improve this answer









                                                        $endgroup$


















                                                          0












                                                          $begingroup$

                                                          perl -MRegexp::Common -pe, 34 bytes



                                                          $_=/^$RE{num}{roman}$/&!/(.)1{3}/


                                                          The &!/(.)1{3}/ part is necessary, because Regexp::Common allows four (but not five) of the same characters in a row. That way, it matches roman numbers used on clock faces, where IIII is often used for 4.






                                                          share|improve this answer









                                                          $endgroup$
















                                                            0












                                                            0








                                                            0





                                                            $begingroup$

                                                            perl -MRegexp::Common -pe, 34 bytes



                                                            $_=/^$RE{num}{roman}$/&!/(.)1{3}/


                                                            The &!/(.)1{3}/ part is necessary, because Regexp::Common allows four (but not five) of the same characters in a row. That way, it matches roman numbers used on clock faces, where IIII is often used for 4.






                                                            share|improve this answer









                                                            $endgroup$



                                                            perl -MRegexp::Common -pe, 34 bytes



                                                            $_=/^$RE{num}{roman}$/&!/(.)1{3}/


                                                            The &!/(.)1{3}/ part is necessary, because Regexp::Common allows four (but not five) of the same characters in a row. That way, it matches roman numbers used on clock faces, where IIII is often used for 4.







                                                            share|improve this answer












                                                            share|improve this answer



                                                            share|improve this answer










                                                            answered 14 hours ago









                                                            AbigailAbigail

                                                            45617




                                                            45617























                                                                0












                                                                $begingroup$


                                                                Python 3, 116 113 109 107 105 106 bytes





                                                                import re
                                                                lambda n:re.match(r'(M{,3}(C(M|CC?|D)?|DC{,3}))(X(C|XX?|L)?|(LX{,3}))?(I(X|II?|V)?|VI{,3})?$',n)


                                                                Try it online!



                                                                -1 byte thanks to ShadowRanger






                                                                share|improve this answer











                                                                $endgroup$









                                                                • 2




                                                                  $begingroup$
                                                                  As I mentioned on the Py2 answer, the leading ^ is unnecessary since match only matches at the beginning of a string already.
                                                                  $endgroup$
                                                                  – ShadowRanger
                                                                  yesterday










                                                                • $begingroup$
                                                                  @ShadowRanger added anchors while debugging and then didn't try again without them. I'll remember that now - thanks! :)
                                                                  $endgroup$
                                                                  – Noodle9
                                                                  14 hours ago










                                                                • $begingroup$
                                                                  Well, just to be clear, the trailing $ is necessary (only fullmatch implies anchors on both ends, and obviously that would cost more than a $).
                                                                  $endgroup$
                                                                  – ShadowRanger
                                                                  14 hours ago










                                                                • $begingroup$
                                                                  @ShadowRanger Ah! That explains why I needed anchors! Didn't realize I only needed to anchor the end. Thanks again.
                                                                  $endgroup$
                                                                  – Noodle9
                                                                  13 hours ago


















                                                                0












                                                                $begingroup$


                                                                Python 3, 116 113 109 107 105 106 bytes





                                                                import re
                                                                lambda n:re.match(r'(M{,3}(C(M|CC?|D)?|DC{,3}))(X(C|XX?|L)?|(LX{,3}))?(I(X|II?|V)?|VI{,3})?$',n)


                                                                Try it online!



                                                                -1 byte thanks to ShadowRanger






                                                                share|improve this answer











                                                                $endgroup$









                                                                • 2




                                                                  $begingroup$
                                                                  As I mentioned on the Py2 answer, the leading ^ is unnecessary since match only matches at the beginning of a string already.
                                                                  $endgroup$
                                                                  – ShadowRanger
                                                                  yesterday










                                                                • $begingroup$
                                                                  @ShadowRanger added anchors while debugging and then didn't try again without them. I'll remember that now - thanks! :)
                                                                  $endgroup$
                                                                  – Noodle9
                                                                  14 hours ago










                                                                • $begingroup$
                                                                  Well, just to be clear, the trailing $ is necessary (only fullmatch implies anchors on both ends, and obviously that would cost more than a $).
                                                                  $endgroup$
                                                                  – ShadowRanger
                                                                  14 hours ago










                                                                • $begingroup$
                                                                  @ShadowRanger Ah! That explains why I needed anchors! Didn't realize I only needed to anchor the end. Thanks again.
                                                                  $endgroup$
                                                                  – Noodle9
                                                                  13 hours ago
















                                                                0












                                                                0








                                                                0





                                                                $begingroup$


                                                                Python 3, 116 113 109 107 105 106 bytes





                                                                import re
                                                                lambda n:re.match(r'(M{,3}(C(M|CC?|D)?|DC{,3}))(X(C|XX?|L)?|(LX{,3}))?(I(X|II?|V)?|VI{,3})?$',n)


                                                                Try it online!



                                                                -1 byte thanks to ShadowRanger






                                                                share|improve this answer











                                                                $endgroup$




                                                                Python 3, 116 113 109 107 105 106 bytes





                                                                import re
                                                                lambda n:re.match(r'(M{,3}(C(M|CC?|D)?|DC{,3}))(X(C|XX?|L)?|(LX{,3}))?(I(X|II?|V)?|VI{,3})?$',n)


                                                                Try it online!



                                                                -1 byte thanks to ShadowRanger







                                                                share|improve this answer














                                                                share|improve this answer



                                                                share|improve this answer








                                                                edited 13 hours ago

























                                                                answered yesterday









                                                                Noodle9Noodle9

                                                                30137




                                                                30137








                                                                • 2




                                                                  $begingroup$
                                                                  As I mentioned on the Py2 answer, the leading ^ is unnecessary since match only matches at the beginning of a string already.
                                                                  $endgroup$
                                                                  – ShadowRanger
                                                                  yesterday










                                                                • $begingroup$
                                                                  @ShadowRanger added anchors while debugging and then didn't try again without them. I'll remember that now - thanks! :)
                                                                  $endgroup$
                                                                  – Noodle9
                                                                  14 hours ago










                                                                • $begingroup$
                                                                  Well, just to be clear, the trailing $ is necessary (only fullmatch implies anchors on both ends, and obviously that would cost more than a $).
                                                                  $endgroup$
                                                                  – ShadowRanger
                                                                  14 hours ago










                                                                • $begingroup$
                                                                  @ShadowRanger Ah! That explains why I needed anchors! Didn't realize I only needed to anchor the end. Thanks again.
                                                                  $endgroup$
                                                                  – Noodle9
                                                                  13 hours ago
















                                                                • 2




                                                                  $begingroup$
                                                                  As I mentioned on the Py2 answer, the leading ^ is unnecessary since match only matches at the beginning of a string already.
                                                                  $endgroup$
                                                                  – ShadowRanger
                                                                  yesterday










                                                                • $begingroup$
                                                                  @ShadowRanger added anchors while debugging and then didn't try again without them. I'll remember that now - thanks! :)
                                                                  $endgroup$
                                                                  – Noodle9
                                                                  14 hours ago










                                                                • $begingroup$
                                                                  Well, just to be clear, the trailing $ is necessary (only fullmatch implies anchors on both ends, and obviously that would cost more than a $).
                                                                  $endgroup$
                                                                  – ShadowRanger
                                                                  14 hours ago










                                                                • $begingroup$
                                                                  @ShadowRanger Ah! That explains why I needed anchors! Didn't realize I only needed to anchor the end. Thanks again.
                                                                  $endgroup$
                                                                  – Noodle9
                                                                  13 hours ago










                                                                2




                                                                2




                                                                $begingroup$
                                                                As I mentioned on the Py2 answer, the leading ^ is unnecessary since match only matches at the beginning of a string already.
                                                                $endgroup$
                                                                – ShadowRanger
                                                                yesterday




                                                                $begingroup$
                                                                As I mentioned on the Py2 answer, the leading ^ is unnecessary since match only matches at the beginning of a string already.
                                                                $endgroup$
                                                                – ShadowRanger
                                                                yesterday












                                                                $begingroup$
                                                                @ShadowRanger added anchors while debugging and then didn't try again without them. I'll remember that now - thanks! :)
                                                                $endgroup$
                                                                – Noodle9
                                                                14 hours ago




                                                                $begingroup$
                                                                @ShadowRanger added anchors while debugging and then didn't try again without them. I'll remember that now - thanks! :)
                                                                $endgroup$
                                                                – Noodle9
                                                                14 hours ago












                                                                $begingroup$
                                                                Well, just to be clear, the trailing $ is necessary (only fullmatch implies anchors on both ends, and obviously that would cost more than a $).
                                                                $endgroup$
                                                                – ShadowRanger
                                                                14 hours ago




                                                                $begingroup$
                                                                Well, just to be clear, the trailing $ is necessary (only fullmatch implies anchors on both ends, and obviously that would cost more than a $).
                                                                $endgroup$
                                                                – ShadowRanger
                                                                14 hours ago












                                                                $begingroup$
                                                                @ShadowRanger Ah! That explains why I needed anchors! Didn't realize I only needed to anchor the end. Thanks again.
                                                                $endgroup$
                                                                – Noodle9
                                                                13 hours ago






                                                                $begingroup$
                                                                @ShadowRanger Ah! That explains why I needed anchors! Didn't realize I only needed to anchor the end. Thanks again.
                                                                $endgroup$
                                                                – Noodle9
                                                                13 hours ago













                                                                0












                                                                $begingroup$


                                                                Ruby, (-n) 56 bytes





                                                                p~/^M{,3}(D?C{,3}|CM|CD)(L?X{,3}|XC|XL)(V?I{,3}|IV|IX)$/


                                                                Try it online!



                                                                Outputs 0 (truthy) or nil (falsy).






                                                                share|improve this answer









                                                                $endgroup$


















                                                                  0












                                                                  $begingroup$


                                                                  Ruby, (-n) 56 bytes





                                                                  p~/^M{,3}(D?C{,3}|CM|CD)(L?X{,3}|XC|XL)(V?I{,3}|IV|IX)$/


                                                                  Try it online!



                                                                  Outputs 0 (truthy) or nil (falsy).






                                                                  share|improve this answer









                                                                  $endgroup$
















                                                                    0












                                                                    0








                                                                    0





                                                                    $begingroup$


                                                                    Ruby, (-n) 56 bytes





                                                                    p~/^M{,3}(D?C{,3}|CM|CD)(L?X{,3}|XC|XL)(V?I{,3}|IV|IX)$/


                                                                    Try it online!



                                                                    Outputs 0 (truthy) or nil (falsy).






                                                                    share|improve this answer









                                                                    $endgroup$




                                                                    Ruby, (-n) 56 bytes





                                                                    p~/^M{,3}(D?C{,3}|CM|CD)(L?X{,3}|XC|XL)(V?I{,3}|IV|IX)$/


                                                                    Try it online!



                                                                    Outputs 0 (truthy) or nil (falsy).







                                                                    share|improve this answer












                                                                    share|improve this answer



                                                                    share|improve this answer










                                                                    answered 12 hours ago









                                                                    iamnotmaynardiamnotmaynard

                                                                    95349




                                                                    95349






























                                                                        draft saved

                                                                        draft discarded




















































                                                                        If this is an answer to a challenge…




                                                                        • …Be sure to follow the challenge specification. However, please refrain from exploiting obvious loopholes. Answers abusing any of the standard loopholes are considered invalid. If you think a specification is unclear or underspecified, comment on the question instead.


                                                                        • …Try to optimize your score. For instance, answers to code-golf challenges should attempt to be as short as possible. You can always include a readable version of the code in addition to the competitive one.
                                                                          Explanations of your answer make it more interesting to read and are very much encouraged.


                                                                        • …Include a short header which indicates the language(s) of your code and its score, as defined by the challenge.



                                                                        More generally…




                                                                        • …Please make sure to answer the question and provide sufficient detail.


                                                                        • …Avoid asking for help, clarification or responding to other answers (use comments instead).





                                                                        draft saved


                                                                        draft discarded














                                                                        StackExchange.ready(
                                                                        function () {
                                                                        StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodegolf.stackexchange.com%2fquestions%2f183014%2fmatch-roman-numerals%23new-answer', 'question_page');
                                                                        }
                                                                        );

                                                                        Post as a guest















                                                                        Required, but never shown





















































                                                                        Required, but never shown














                                                                        Required, but never shown












                                                                        Required, but never shown







                                                                        Required, but never shown

































                                                                        Required, but never shown














                                                                        Required, but never shown












                                                                        Required, but never shown







                                                                        Required, but never shown







                                                                        Popular posts from this blog

                                                                        GameSpot

                                                                        日野市

                                                                        Tu-95轟炸機