• Pooh Bear (unregistered) in reply to Remy Martin
    Remy Martin:
    IMPORTANT ANNOUNCEMENT:

    Alex is in the hospital in an alcohol-poisoning-induced coma. He did not leave me with the access codes, so I cannot post a new article. I encourage you to try the "Random Article" link, or to post a few more implementations of the Roman Numeral conversion libraries in whatever esotaric language you would like.

    Oh, dear.

  • Jack (unregistered) in reply to Remy Martin
    Remy Martin (unregistered):
    IMPORTANT ANNOUNCEMENT:

    Alex is in the hospital in an alcohol-poisoning-induced coma. He did not leave me with the access codes, so I cannot post a new article. I encourage you to try the "Random Article" link, or to post a few more implementations of the Roman Numeral conversion libraries in whatever esotaric language you would like.

    Apparently he took your "real Remy Martin" password too. Or did the alcohol have something to do with that?

  • AGray (unregistered) in reply to Eric

    I shortened things up slightly, and for grins and giggles threw in my Reverse method.

        public class RomanToIndoArabic
        {
            private readonly Dictionary<char, int> _Romans = new Dictionary<char, int>
                                                             {
                                                                 {'I', 1},
                                                                 {'V', 5},
                                                                 {'X', 10},
                                                                 {'L', 50},
                                                                 {'C', 100},
                                                                 {'M', 1000}
                                                             };
    
            /// 
            /// This method gets called right after the recording has been started.
            /// It can be used to execute recording specific initialization code.
            /// 
            private void Init()
            {
                // 1978 indo-arabic...
                const string inNumber = "MCMLXXIIX";
                int number = ToArabic(inNumber);
    
                if(number == 1978)
                {
                    Console.WriteLine("Success!  " + inNumber + " converts to " + number);
                }
                else
                {
                    Console.WriteLine("The conversion did not work....");
                }
    
                Console.ReadLine();
            }
    
            private int ToArabic(string inRoman)
            {
                int currentResult = 0;
                int maximum = 0;
    
                inRoman = Reverse(inRoman);
                foreach(char thisDigit in inRoman)
                {
                    int currentValue = _Romans[thisDigit];
                    if(currentValue < maximum)
                    {
                        currentResult -= currentValue;
                    }
                    else
                    {
                        currentResult += currentValue;
                        maximum = currentValue;
                    }
                }
    
                return currentResult;
            }
    
            private string Reverse(string inString)
            {
                StringBuilder result = new StringBuilder(inString.Length);
                for (int i = inString.Length - 1; i >= 0; i--)
                {
                    result.Append(inString[i]);
                }
    
                Console.WriteLine(inString + " reversed is " + result);
                return result.ToString();
            }
        }
  • lipinger (unregistered) in reply to CarnivorousHippie

    Tokenize on (IV, IX, I, XL, XC, X, CD, CM, C, D, M), map to (4, 9, 1, 40, 90, 10, 400, 900, 100, 500, 1000)... ?!

  • (cs) in reply to lipinger
    lipinger:
    Tokenize on (IV, IX, I, XL, XC, X, CD, CM, C, _D_, M), map to (4, 9, 1, 40, 90, 10, 400, 900, 100, _500_, 1000)... ?!

    Perfect. But what's the underscores for?

  • (cs) in reply to Brendan
    Gurth:
    CD ÷ CXXVII is a close enough approximation of the value of π for day to day use.
    Nah -- CCCLV ÷ CXIII is much better. :)
    Brendan:
    That isn't the piece of code we're talking about.
    You don't need to see our compile listing. Move along.
  • doodle (unregistered) in reply to lipinger

    uberadmin@subdesk:~/kindergarden> expr $(sed 's/IV/4 + /ig; s/IX/9 + /ig; s/IL/49 + /ig; s/IC/99 + /ig; s/ID/499 + /ig; s/IM/999 + /ig; s/XC/90 + /ig; s/XD/490 + /ig; s/XM/990 + /ig; s/CD/500 + /ig; s/CM/900 + /ig; s/VI/6 + /ig; s/VII/7 + /ig; s/VIII/8 + /ig; s/I/1 + /ig; s/II/2 + /ig; s/III/3 + /ig; s/V/5 + /ig; s/X/10 + /ig; s/L/50 + /ig; s/C/100 + /ig; s/D/500 + /ig; s/M/1000 + /ig; s/$/0/;' <<<mcmlxxxix ) 1989 uberadmin@subdesk:~/kindergarden> !

  • (cs) in reply to Brendan
    Brendan:
    briverymouse:
    Which one would you most easily understand once you know what foldl1 does? Obviously the Haskell one: it's all there on one line, it folds 1 to 10 using addition.
    Wrong. The most natural solution to the problem is a loop ("foreach"). Ask someone who's never programmed anything before how they would do it and I can guarantee their answer won't involve recursion. Once you know what fold1 does it's "clever".
    This is thoroughly silly. Non-programmers or people with very little understanding of computer science often come up with algorithms and code that is bloated, hard to follow, and full of copypasta precisely because they don't know all the idioms and abstractions available to them. Such code lands on TDWTF, duh.

    If you know Haskell, functional constructs are natural. Just because many programming languages don't offer such constructs doesn't mean they are unnatural. The idea that someone who doesn't have proper training can write decent code is ridiculous on its face, as repeatedly demonstrated on this site. I don't know Haskell, but I know LISP, and I immediately knew what foldl must mean, even though LISP is not really a functional language at heart. But then I know more than a Joe Random Coder, perhaps.

    If you seriously think that lowest-level-of-abstraction code that only uses "natural" constructs from Algol is best for maintenance, you must have not ever seen how complex systems can benefit from high level abstractions available in the programming language used. I've seen both Caml and Erlang source that would grow at times by an order of magnitude if ported to any of the Algol-like languages.

  • diddle (unregistered) in reply to doodle

    root@box:~/tmp> expr $(sed 's/I([VXLCDM])/-1 + \1/ig; s/V([LCDM])/-5 + \1/ig; s/X([LCDM])/-10 + \1/ig; s/L([DM])/-50 + \1/ig; s/C([DM])/-100 + \1/ig; s/I/1 + /ig; s/V/5 + /ig; s/X/10 + /ig; s/L/50 + /ig; s/C/100 + /ig; s/D/500 + /ig; s/M/1000 + /ig; s/$/0/' <<<mcmlxxxix ) 1989 root@box:~/tmp>

  • dawdle (unregistered) in reply to diddle

    root@box:~/tmp> expr $(sed 's/I([VXLCDM])/-1 + \1/ig; s/V([LCDM])/-5 + \1/ig; s/X([LCDM])/-10 + \1/ig; s/L([DM])/-50 + \1/ig; s/C([DM])/-100 + \1/ig; s/M/DD/ig; s/D/CCCCC/ig; s/C/LL/ig; s/L/XXXXX/ig; s/X/VV/ig; s/V/IIIII/ig; s/I/1 + /ig; s/$/0/' <<<mcmlxxxix ) 1989 root@box:~/tmp>

  • daghdle (unregistered) in reply to dawdle

    root@box:~/tmp> sed 's/M/DD/ig;s/CD/CCCC/ig;s/D/CCCCC/ig;s/LC/L/ig;s/C/LL/ig;s/XL/XXXX/ig;s/L/XXXXX/ig;s/VX/V/ig;s/X/VV/ig;s/IV/IIII/ig;s/V/IIIII/ig;s/I/I /ig' <<<mcmlxxxix | wc -w 1989 root@box:~/tmp>

  • dddl (unregistered) in reply to daghdle

    root@box:~/tmp> tr /c-x/ /C-X/<<<mcmlxxxix|sed 's/M/DD/g;s/CD/CCCC/g;s/D/CCCCC/g;s/LC/L/g;s/C/LL/g;s/XL/XXXX/g;s/L/XXXXX/g;s/VX/V/g;s/X/VV/g;s/IV/IIII/g;s/V/IIIII/g;s/I$//g'|wc -m 1989 root@box:~/tmp>

  • Brendan (unregistered) in reply to briverymouse
    briverymouse:
    Brendan:
    Wrong. The most natural solution to the problem is a loop ("foreach"). Ask someone who's never programmed anything before how they would do it and I can guarantee their answer won't involve recursion. Once you know what fold1 does it's "clever".
    What do you think foldl1 does? It loops over a list from the left, combining the results using the provided function.

    In Haskell, "mapM_" is "foreach". Their "foldl1" is very different concept.

    For the list (1, 2, 3, 4) and the functions f(x) and g(x, y); "f(1); f(2); f(3); f(4);" is very different conceptually from "g(1, g(2, g(3, 4)));".

    Perhaps you need to read a book about Haskell?

    briverymouse:
    Ask someone who's never programmed anything before how they would do it and I can guarantee their answer won't involve a foreach loop either. They will probably say something like "add the first two, then add the next to it, and so on until the end", which is literally what foldl1 does, and not "make a temporary variable, set it to zero, then take every value from the list and add it to the temporary variable". That would be foldl by the way, which also exists.

    So you're saying that for the list (1, 2, 3, 4) a real person wouldn't do "3 + 4 = 5", then "2 + 5 = 7" then "1 + 7 = 8" to get the sum? You're saying that a real person would assume addition is an operator and not a function? You're saying a real person would do something like "temp = list.next(); while(list.hasNext) { temp += list.next(); }"?

    You're saying a real person wouldn't do anything like foldl1, but you thought they might because you don't know how foldl1 works?

    briverymouse:
    What I said was:

    What I said was "Haskell is poo because it forces people to use recursion". You replied with "modern compilers can do tailcall optimisation", completely missing the point that programmers still have to deal with the concept whether it makes sense for their problem or not (regardless of what the compiler generates). I played along and asked for a disassembly of the Haskell code we were talking about, with the intention of either showing that Haskell failed to use tail recursion to remove all of the recursion from it's own library functions or that the generated code is a huge bloated mess anyway, and you missed the point there too and showed C code where the compiler did do tail call optimisation. I restated my request for a disassembly of the Haskell code in question, and you've failed to produce it again.

    I'm not sure if, like me, you don't have Haskell installed on any of your computers and couldn't be bothered installing it (I couldn't blame you if that's the case), or if you're deliberately trying to avoid posting the disassembly because you're ashamed of it, or if you're honestly unable to follow the conversation.

  • Tud (unregistered) in reply to mwchase
    mwchase:
    That's not Haskell, that's Python. It's using a language feature that I was previously unaware of, oops. ([value] if [condition] else [alternative])

    Here's another cool feature: lambda forms.

    r = {"I":1,"V":5,"X":10,"L":50,"C":100,"D":500,"M":1000}
    r2d = lambda m, p="I": 0 if m=="" else r2d(m[:-1],m[-1])+r[m[-1]]*(1 if r[m[-1]]>=r[p] else -1)
    

    This one's kinda cheating though, you're not supposed to make them recursive.

    Also, check out my cool C one-liner:

    public string rom2num(string r){if(r == "I") return "1"; if (r == "II") return "2";  if (r == "III") return "3";  if (r == "IV") return "4";  if (r == "V") return "5";  if (r == "VI") return "6"; /*...*/  if (r == "MMXI") return "2011";  return "E"; }
    
  • Brendan (unregistered) in reply to Kuba
    Kuba:
    If you know Haskell, functional constructs are natural.

    For me (and I doubt I'm the only one here), inhaling toxic smoke seems natural. Does this mean inhaling toxic smoke actually is natural?

    After you know Haskell (or some other functional programming language) functional constructs may seem natural to you, but they won't seem natural before you get used to them, and won't seem natural to anyone else who hasn't become used to them.

    For an ideal language, you wouldn't have to get used to anything before it seems natural. There isn't a language that comes close to this (and I doubt there ever will be), but some are closer to this ideal than others.

  • (cs) in reply to Brendan
    Brendan:
    briverymouse:
    What do you think foldl1 does? It loops over a list from the left, combining the results using the provided function.

    In Haskell, "mapM_" is "foreach". Their "foldl1" is very different concept.

    For the list (1, 2, 3, 4) and the functions f(x) and g(x, y); "f(1); f(2); f(3); f(4);" is very different conceptually from "g(1, g(2, g(3, 4)));".

    Perhaps you need to read a book about Haskell?

    LOL, what? Did you look up "foreach in Haskell" on Google, read something about mapM_, and now think you're an expert on functional programming?

    This will probably go way over your head, but mapM_ maps a function returning a monad over a list, and returns a monad containing the empty tuple. It behaves roughly foreach-loop-like only for IO, and does something wildly different in e.g. the Maybe or list monad (fun exercise: see if you can find out what).

    foldl1 corresponds to the following foreach loop (pseudo-code):

    let accumulator = list[0]
    foreach element in list[1 ..]
        accumulator = func(accumulator, element)

    Most other common uses for a foreach are available as well. Having the different uses as separate functions makes reading code easier, as just the name of the function is enough to know what exactly is happening.

    Brendan:
    So you're saying that for the list (1, 2, 3, 4) a real person wouldn't do "3 + 4 = 5", then "2 + 5 = 7" then "1 + 7 = 8" to get the sum? You're saying that a real person would assume addition is an operator and not a function? You're saying a real person would do something like "temp = list.next(); while(list.hasNext) { temp += list.next(); }"?

    You're saying a real person wouldn't do anything like foldl1, but you thought they might because you don't know how foldl1 works?

    I'm very sorry, but you stopped making sense. Are you saying foldl1 starts from the right, even though the l stands for left? And what is the difference between an operator and a function in your mind?

    Brendan:
    briverymouse:
    What I said was:

    What I said was "Haskell is poo because it forces people to use recursion". You replied with "modern compilers can do tailcall optimisation", completely missing the point that programmers still have to deal with the concept whether it makes sense for their problem or not (regardless of what the compiler generates). I played along and asked for a disassembly of the Haskell code we were talking about, with the intention of either showing that Haskell failed to use tail recursion to remove all of the recursion from it's own library functions or that the generated code is a huge bloated mess anyway, and you missed the point there too and showed C code where the compiler did do tail call optimisation. I restated my request for a disassembly of the Haskell code in question, and you've failed to produce it again.

    I'm not sure if, like me, you don't have Haskell installed on any of your computers and couldn't be bothered installing it (I couldn't blame you if that's the case), or if you're deliberately trying to avoid posting the disassembly because you're ashamed of it, or if you're honestly unable to follow the conversation.

    Wait, so I misunderstood you, thinking you said Haskell code runs slowly, while you actually mean programming Haskell is too difficult for you? Instead of just clarifying what you meant, you decided you now need the assembly code generated from a Haskell program (without mentioning which program)? Is that it? Indeed, I don't have "Haskell" installed, as it's a programming language, not a piece of software, but I assume GHC will do? If so, here is the Roman numerals program in assembly form: http://pastebin.com/LNUSq68u Do let me know if either the compiler or the compiled source are not exactly what you wanted, and I'll see what I can do.

  • doidl (unregistered) in reply to daghdle

    root@box:~/tmp> awk -vr=mcmlxxxix '{gsub($1,$2,r); }END{printf r}' <<<'m dd cd cccc d ccccc lc l c ll xl xxxx l xxxxx vx v x vv iv iiii v iiiii' | wc -c 1989 root@box:~/tmp>

  • Eric Jablow (unregistered)

    Isn't the correct language for this problem Lingua::Romana::Perligata? That's a language that deserves its own wtf book!

  • daddley (unregistered) in reply to doidl

    root@box:~/tmp> awk -vr=mcmlxxxix '{gsub($1,$2,r); }END{print length(r)}' <<<'m dd cd cccc d ccccc lc l c ll xl xxxx l xxxxx vx v x vv iv iiii v iiiii' 1989 root@box:~/tmp>

  • Franz Kafka (unregistered) in reply to Brendan
    Brendan:
    What I said was "Haskell is poo because it forces people to use recursion". You replied with "modern compilers can do tailcall optimisation", completely missing the point that programmers still have to deal with the concept whether it makes sense for their problem or not (regardless of what the compiler generates). I played along and asked for a disassembly of the Haskell code we were talking about, with the intention of either showing that Haskell failed to use tail recursion to remove all of the recursion from it's own library functions or that the generated code is a huge bloated mess anyway, and you missed the point there too and showed C code where the compiler did do tail call optimisation. I restated my request for a disassembly of the Haskell code in question, and you've failed to produce it again.

    I'm not sure if, like me, you don't have Haskell installed on any of your computers and couldn't be bothered installing it (I couldn't blame you if that's the case), or if you're deliberately trying to avoid posting the disassembly because you're ashamed of it, or if you're honestly unable to follow the conversation.

    If you can't handle recursion reasonably well, you must not be much of a programmer. If you've got tail recursion in a language then of cours you're going to use it - it's the total shit, and combining it with functional constructs that replace calls with their results makes for a really simple and easy time of it.

    Shit, next you're going to tell me that obejcts are too hard.

  • (cs)

    All this talk about Roman Numbers reminds me that "MMX" is a trademark of Intel.

    We now return you the regularly scheduled programming concerning Perl, Haskel, Caml, C, C++, Fortran, Basic, APL, and Lisp. Carry on!

  • doadley (unregistered) in reply to daddley

    Hey, you hyper meta functional, objectional uberinformationist, a COMPETITION here: can you post your roman->natural haskell code via 1 sms or 1 tweet in readable form like me with bash/awk? Correct code - no failed test cases allowed?

    daddley:
    awk -vr=$1 '{gsub($1,$2,r); }END{print length(r)}' <<<'m dd cd cccc d ccccc lc l c ll xl xxxx l xxxxx vx v x vv iv iiii v iiiii'
  • David S. (unregistered)

    Whether or not MIM and the vast array of other disputed Roman forms are correct in some sense isn't relevant. Roman numerals are obviously going to be coming from human input, so we should be liberal in what we accept. What we output is debatable; personally, I think MIM is much more elegant and acceptable to most actual users of computer programs, as opposed to the long dead Romans.

    It's funny to hear complaints about needing to know Haskell before understanding code written in it from programmers who would write while(*a++ = *b++); without a second thought.

  • (cs) in reply to doadley
    doadley:
    Hey, you hyper meta functional, objectional uberinformationist, a COMPETITION here: can you post your roman->natural haskell code via 1 sms or 1 tweet in readable form like me with bash/awk? Correct code - no failed test cases allowed?

    Sure:

    r2n = sum . map f . groupBy (<) . map (fromJust . (`lookup` zip "IVXLCDM" [1,5,10,50,100,500,1000]))
    f [n] = n
    f [p,n] = n-p

    Not great code, and Haskell certainly isn't ideal for code golf, but it works.

  • Brendan (unregistered) in reply to briverymouse
    briverymouse:
    Brendan:
    For the list (1, 2, 3, 4) and the functions f(x) and g(x, y); "f(1); f(2); f(3); f(4);" is very different conceptually from "g(1, g(2, g(3, 4)));".

    Perhaps you need to read a book about Haskell?

    LOL, what? Did you look up "foreach in Haskell" on Google, read something about mapM_, and now think you're an expert on functional programming?

    Yes :-)

    briverymouse:
    It behaves roughly foreach-loop-like only for IO, and does something wildly different in e.g. the Maybe or list monad (fun exercise: see if you can find out what).

    So, the exact same thing behaves wildy differently in different contexts?

    briverymouse:
    foldl1 corresponds to the following foreach loop (pseudo-code):
    let accumulator = list[0]
    foreach element in list[1 ..]
        accumulator = func(accumulator, element)

    More like:

    int foldl1( (*someFunction)(int a, int b), int *list, int count)
    )
    {
        if(count <= 0) {
            return 0;
        } else if (count == 1)
            return list[0];
        } else {
            return someFunction(list[0], foldl1(someFunction, &list[1], count--));
        }
    }
    briverymouse:
    Brendan:
    So you're saying that for the list (1, 2, 3, 4) a real person wouldn't do "3 + 4 = 5", then "2 + 5 = 7" then "1 + 7 = 8" to get the sum? You're saying that a real person would assume addition is an operator and not a function? You're saying a real person would do something like "temp = list.next(); while(list.hasNext) { temp += list.next(); }"?

    You're saying a real person wouldn't do anything like foldl1, but you thought they might because you don't know how foldl1 works?

    I'm very sorry, but you stopped making sense. Are you saying foldl1 starts from the right, even though the l stands for left? And what is the difference between an operator and a function in your mind?

    Look at the "recursive C with function pointer" example above. It starts from the left, but (with "add()" as the function) the values on the right would be the first ones added.

    You are right though - I don't know if foldl1 starts from the right (so that the left values are added first) or if fold1 starts from the left (so that the right values are added first). This also implies that the "recursive C with function pointer" example above may be around the wrong way.

    Of course it's irrelevant what the order is. For the list (1, 2, 3, 4) a real person wouldn't do "3 + 4 = 7", then "2 + 7 = 9" then "1 + 9 = 10"; and they also wouldn't do "1 + 2 = 3" then "3 + 3 = 6" then "6 + 4 = 10". Try it on a calculator yourself and see how you do it - I'll bet you key in something like "1 + 2 + 3 + 4 =" (and rely on the calculator to remember its accumulator) and don't do "1 + 2 = " then "3 + 3 = " then "6 + 4 = ".

    To understand the difference between what I meant by "function" and what I meant by "operator", try passing the address of the '+' operator to the "recursive C with function pointer" example above. You'd have to write a function that does nothing more than "return a + b;" and then hope the compiler is smart enough to optimise out your counter-intuitive folly.

    briverymouse:
    Brendan:
    I'm not sure if, like me, you don't have Haskell installed on any of your computers and couldn't be bothered installing it (I couldn't blame you if that's the case), or if you're deliberately trying to avoid posting the disassembly because you're ashamed of it, or if you're honestly unable to follow the conversation.

    Wait, so I misunderstood you, thinking you said Haskell code runs slowly, while you actually mean programming Haskell is too difficult for you? Instead of just clarifying what you meant, you decided you now need the assembly code generated from a Haskell program (without mentioning which program)? Is that it?

    I haven't said Haskell (or code generated by GHC if you'd like to be pedantic) is slow. Speed is relative, and I'm sure the generated code is quite fast when compared to some languages. Being about 36 times faster than Perl is quite an achievement (even if Intel Fortran and GCC's C are about twice as fast).

    briverymouse:
    Indeed, I don't have "Haskell" installed, as it's a programming language, not a piece of software, but I assume GHC will do? If so, here is the Roman numerals program in assembly form: http://pastebin.com/LNUSq68u Do let me know if either the compiler or the compiled source are not exactly what you wanted, and I'll see what I can do.

    While I can confirm that this is bloated enough to have been compiled from Haskell, and can also confirm that it is part of the Roman Enumeration code; sadly it's incomplete. The interesting part of the code disappears into a "jmp _stg_ap_n_fast" that isn't present, and various other parts of the code aren't included either.

  • (cs) in reply to Gurth
    Gurth:
    PiisAWheeL:
    KattMan:
    CD/CXXVII = PI
    I. P is not a roman numeral.
    Better notation would have been CD ÷ CXXVII = π, yes. However:
    PiisAWheeL:
    II. Roman numerals didn't have decimal points.
    Who says they need to? If you work with traditional fractions, you don't need decimal points (or commas, or whatever). CD ÷ CXXVII is a close enough approximation of the value of π for day to day use.
    Accurate enough for the government to use for tax collecting purposes? Or did they just round up?
    Nagesh:
    Nobody notice that this code can only go up to 2011. Did program candidate think world going to end after that or did he think nobody is going to remember Roman Number greater than 2011?

    I would like to know his think process.

    You missed that boat on page 1

    PiisAWheeL:
    Toodle:
    Code fail on IIX
    8 being written as VIII aside, wouldn't it also fail on IV first?
    Black Bart:
    The new hire was obviously an Aztec, since he knew from hist calendar that there was no need for years beyond 2012.
    Wrong. That is a reason not to include 2013. You still need to include the last year that will exist. December 20th is most of the year.

    And now I have the 1 clincher that will ruin roman numerals for all of you:

    No way to represent 0. This means no binary. All other arguments are invalid.

  • (cs) in reply to Brendan
    Brendan:
    briverymouse:
    It behaves roughly foreach-loop-like only for IO, and does something wildly different in e.g. the Maybe or list monad (fun exercise: see if you can find out what).

    So, the exact same thing behaves wildy differently in different contexts?

    Interesting question. Monads are basically a way to have values with "context". This context can be failure (the Maybe monad), non-determinism (the list monad), contact with the outside world (the IO monad), and more. mapM_ executes all the "context", but returns no value. In the IO monad, this means it executes all the IO actions, and returns nothing interesting. This is more or less like a foreach loop doing IO and computing nothing in other languages. In the Maybe monad, mapM_ tries to compute all values, but only returns the context: whether one of them failed or not. In the list monad, it tries to compute all values, and sees if there is still non-determinism left. So, yes, in a way they're very different things, but the general idea is the same.

    Brendan:
    briverymouse:
    foldl1 corresponds to the following foreach loop (pseudo-code):
    let accumulator = list[0]
    foreach element in list[1 ..]
        accumulator = func(accumulator, element)

    More like:

    ...

    Yes, that is the recursive version. You seem quite fond of recursion!

    Brendan:
    Of course it's irrelevant what the order is. For the list (1, 2, 3, 4) a real person wouldn't do "3 + 4 = 7", then "2 + 7 = 9" then "1 + 9 = 10"; and they also wouldn't do "1 + 2 = 3" then "3 + 3 = 6" then "6 + 4 = 10". Try it on a calculator yourself and see how you do it - I'll bet you key in something like "1 + 2 + 3 + 4 =" (and rely on the calculator to remember its accumulator) and don't do "1 + 2 = " then "3 + 3 = " then "6 + 4 = ".

    Indeed! That's why "foldl1 (+) list", which means "throw a plus sign between the elements of the list", is more natural than a raw loop, where you explicitly write out all the steps.

    Brendan:
    To understand the difference between what I meant by "function" and what I meant by "operator", try passing the address of the '+' operator to the "recursive C with function pointer" example above. You'd have to write a function that does nothing more than "return a + b;" and then hope the compiler is smart enough to optimise out your counter-intuitive folly.

    Yes, but that's a C-specific concept. The only difference between an operator and a function in Haskell is the name (special characters or not), and that an operator is infix, while a function is prefix.

    Brendan:
    While I can confirm that this is bloated enough to have been compiled from Haskell, and can also confirm that it *is* part of the Roman Enumeration code; sadly it's incomplete. The interesting part of the code disappears into a "jmp _stg_ap_n_fast" that isn't present, and various other parts of the code aren't included either.

    So, what, you now want me to give you assembly code for all libraries used?

  • Brendan (unregistered) in reply to briverymouse
    briverymouse:
    More like:
    ...

    Yes, that is the recursive version. You seem quite fond of recursion!

    There are some things that are naturally recursive (traversing hierarchical trees for example), and I have no objection to recursion when it's the right tool for the job. This is not the case here - if I actually saw that code in a project being used to add a list of numbers I'd submit it to TDWTF (but it is about as close as I can get to mimicking Haskell's foldl1 (or foldr1?) in C).

    briverymouse:
    Indeed! That's why "foldl1 (+) list", which means "throw a plus sign between the elements of the list", is more natural than a raw loop, where you explicitly write out all the steps.

    http://zvon.org/other/haskell/Outputprelude/foldl1_f.html says: "it takes the first 2 items of the list and applies the function to them, then feeds the function with this result and the third argument and so on."

    If the function you use has side effects, then it seems obvious to me that it isn't equivalent to inserting an operator between the elements of the list. Perhaps this is another case of "behaves wildly different when you're using the wrong concept to describe it".

    briverymouse:
    Brendan:
    While I can confirm that this is bloated enough to have been compiled from Haskell, and can also confirm that it *is* part of the Roman Enumeration code; sadly it's incomplete. The interesting part of the code disappears into a "jmp _stg_ap_n_fast" that isn't present, and various other parts of the code aren't included either.

    So, what, you now want me to give you assembly code for all libraries used?

    Maybe "ghc -static -optl-static" would help with the missing pieces, while hopefully avoiding a lot of unnecessary/unused code.

  • RobTF (unregistered)

    My 5 minute attempt with handling for invalid values (e.g. IIIII).

            private static int Rom2Num(string value)
            {
                // HACK: this table should be static.. but for purposes of demo
                var values = new int[] { 1, 5, 10, 100, 500, 1000 };
                var dict = new Dictionary<char, int>();
                dict.Add('I', 0);
                dict.Add('V', 1);
                dict.Add('X', 2);
                dict.Add('C', 3);
                dict.Add('D', 4);
                dict.Add('M', 5);
    
                // check for null/empty input
                if (String.IsNullOrEmpty(value))
                {
                    throw new ArgumentOutOfRangeException("Input cannot be a null or empty string.", value, value);
                }
    
                value = value.ToUpperInvariant();
    
                // determine if string contains any invalid characters
                foreach (var c in value)
                {
                    if (!dict.ContainsKey(c))
                    {
                        throw new ArgumentException(String.Format("Input contains invalid character \"{0}\".", c), "value");
                    }
                }
    
                int total = 0;
                int charamt;
                int curvalueidx;
                int curfactor = values[0];
                char curchar;
    
                // work from the back of the string to the front
                for (int i = value.Length - 1; i >= 0; i--)
                {
                    curchar = value[i];
    
                    // current position of value in values array
                    curvalueidx = dict[curchar];
    
                    // value of the current character
                    charamt = values[curvalueidx];
    
                    if (charamt < curfactor)
                    {
                        // value has appeared after higer value character? subtract it
                        total -= charamt;
                    }
                    else
                    {
                        // add the value, change the current factor
                        total += charamt;
                        curfactor = charamt;
                    }
    
                    // are there characters worth more than the current character?
                    if (curvalueidx < (values.Length - 1))
                    {
                        /*
                            if the total of the value so far is greater than the value of the next character, the
                            user has used too many of the current character and should replace them with the next
                            character instead (e.g. IIIII => V).
                         */
                        if (total >= values[curvalueidx + 1])
                        {
                            throw new ArgumentException("value", String.Format("Value specified is not a valid roman numeral (too many \"{0}\" characters).", curchar));
                        }
                    }
                }
    
                return total;
            }
    
  • the beholder (unregistered) in reply to Nagesh
    Nagesh:
    Nobody notice that this code can only go up to 2011. Did program candidate think world going to end after that or did he think nobody is going to remember Roman Number greater than 2011?

    I would like to know his think process.

    Uh, I don't think he has one

  • (cs) in reply to the beholder
    the beholder:
    Nagesh:
    Nobody notice that this code can only go up to 2011. Did program candidate think world going to end after that or did he think nobody is going to remember Roman Number greater than 2011?

    I would like to know his think process.

    Uh, I don't think he has one

    Yes lack of think is causing major problems in universe.

  • (cs) in reply to Brendan
    Brendan:
    Kuba:
    If you know Haskell, functional constructs are natural.

    For me (and I doubt I'm the only one here), inhaling toxic smoke seems natural. Does this mean inhaling toxic smoke actually is natural?

    After you know Haskell (or some other functional programming language) functional constructs may seem natural to you, but they won't seem natural before you get used to them, and won't seem natural to anyone else who hasn't become used to them.

    For an ideal language, you wouldn't have to get used to anything before it seems natural. There isn't a language that comes close to this (and I doubt there ever will be), but some are closer to this ideal than others.

    All you trolls need to pay attention to this guy. He's got the art mastered.
  • Evil Hamster (unregistered)

    %m = qw/I 1 V 5 X 10 L 50 C 100 D 500 M 1000/; @n = map {$m{uc($)}} split //, $ARGV[0]; $r+=$n[$]*(($n[$+1] > $n[$]) ? -1 : 1) for (0..$#n); print $r , "\n";

  • (cs) in reply to Brendan
    Brendan:
    This is not the case here - if I actually saw that code in a project being used to add a list of numbers I'd submit it to TDWTF (but it is about as close as I can get to mimicking Haskell's foldl1 (or foldr1?) in C).

    Why wouldn't you use a for loop to implement foldl1 instead of recursion? In what way does your code differ from mine? Haskell has implemented foldl1 for you, so you don't need to worry about the implementation details. When you're using a fold, you're not using recursion. A for loop in C is implemented with jmp, but I'm not saying using a for loop means using goto.

    Brendan:
    http://zvon.org/other/haskell/Outputprelude/foldl1_f.html says: "it takes the first 2 items of the list and applies the function to them, then feeds the function with this result and the third argument and so on."

    If the function you use has side effects, then it seems obvious to me that it isn't equivalent to inserting an operator between the elements of the list. Perhaps this is another case of "behaves wildly different when you're using the wrong concept to describe it".

    What? Functions don't have side effects in Haskell. I hope you see how "a + b + c + d" is equivalent to "((a + b) + c) + d". It is the way most people would calculate it if they have no calculator, and it is also the way both C and Haskell will actually calculate it. If functions and operators could have side effects, say incrementing a variable "count", both "a + b + c + d" and "foldl1 (+) [a, b, c, d]" will result in count being incremented by 3. Or are you saying only functions can have side effects, while operators can't? That's not even true in C, if you think about the ++ and -- operators.

    Brendan:
    Maybe "ghc -static -optl-static" would help with the missing pieces, while hopefully avoiding a lot of unnecessary/unused code.

    Oh, you want me to actually disassemble the entire executable, not just my own code? Have fun with it: http://dl.dropbox.com/u/24516255/rom2num.asm

  • The Nitpicker (unregistered)

    And he totally missed that there are two ways to say 4: "IV" and "IIII"

  • Mike (unregistered)

    "I can only hope that the powers-that-be will determine that we are ineffective at training and shift the responsibility of teaching programmers to program to corporate training."

    Well, that would require HR to do something constructive for a change, instead of merely continuing to justify their existence. They're gods you know, and so much more efficient and knowledgeable than any IT person ever could be.

  • (cs) in reply to herby
    herby:
    We now return you the regularly scheduled programming concerning Perl, Haskel, Caml, C, C++, Fortran, Basic, APL, and Lisp. Carry on!
    Hey, you forgot about VB.
    herby:
    ... programming ...
    Or, maybe not.
  • Jay (unregistered) in reply to PiisAWheeL
    PiisAWheeL:
    And now I have the 1 clincher that will ruin roman numerals for all of you:

    No way to represent 0. This means no binary. All other arguments are invalid.

    Of course there's a way to write zero. It's the representation made up of everything from the following colon to the end of the line:

    That is, don't write any Roman numerals. That's zero.

    This does, of course, create the problem that there's no clear way to tell whether you mean the number zero, or you don't mean to convey any number at all. Of course we can't be too hard on the Romans: the idea of distinguishing a string with zero characters from a null string (or nothing string for you VB folks) is pretty recent, and still continues to confuse many programmers.

    Reminds me: In the book "Lest Darkness Fall", there's a seen where a time traveller is trying to explain the value of the digit 0 to people who use Roman numerals. One of them asks in bafflement, "You have a symbol for nothing?" And the time traveller replies, "Why not? You have a word for 'nothing'." (Quoting from decades-old memory, excuse me if it's not an exact quote.)

  • Jay (unregistered) in reply to Mike
    Mike:
    "I can only hope that the powers-that-be will determine that we are ineffective at training and shift the responsibility of teaching programmers to program to corporate training."

    Well, that would require HR to do something constructive for a change, instead of merely continuing to justify their existence. They're gods you know, and so much more efficient and knowledgeable than any IT person ever could be.

    Those who can, do. Those who can't, teach. Those who can't teach, recruit teachers.

  • Brendan (unregistered) in reply to briverymouse
    briverymouse:
    Brendan:
    This is not the case here - if I actually saw that code in a project being used to add a list of numbers I'd submit it to TDWTF (but it is about as close as I can get to mimicking Haskell's foldl1 (or foldr1?) in C).

    Why wouldn't you use a for loop to implement foldl1 instead of recursion? In what way does your code differ from mine? Haskell has implemented foldl1 for you, so you don't need to worry about the implementation details. When you're using a fold, you're not using recursion. A for loop in C is implemented with jmp, but I'm not saying using a for loop means using goto.

    I didn't use a for loop in that C example because I was trying to mimic Haskell, and therefore trying to avoid the use of mutable variables.

    I probably should have made that clearer:

    int foldl1( (*someFunction)(const int a, const int b), const int *list, const int count)
    )
    {
        if(count <= 0) {
            return 0;
        } else if (count == 1)
            return list[0];
        } else {
            return someFunction(list[0], foldl1(someFunction, &list[1], count - 1));
        }
    }
    briverymouse:
    Brendan:
    http://zvon.org/other/haskell/Outputprelude/foldl1_f.html says: "it takes the first 2 items of the list and applies the function to them, then feeds the function with this result and the third argument and so on."

    If the function you use has side effects, then it seems obvious to me that it isn't equivalent to inserting an operator between the elements of the list. Perhaps this is another case of "behaves wildly different when you're using the wrong concept to describe it".

    What? Functions don't have side effects in Haskell.

    D'oh - sorry, I'm too used to useful languages. Can a function use a monad (or whatever they call their "things that do have side-effects that aren't functions but were necessary because the language designers removed required functionality and had to reintroduce it as something different for their bait and switch trick to work")?

    briverymouse:
    I hope you see how "a + b + c + d" is equivalent to "((a + b) + c) + d".

    And I hope you'll be able to see that this "insert the operator" description only works to describe a limited number of cases that (I assume) foldl1 supports. E.g. when the function is "return a + b;" or "return a - b;", but not when the function is "return a / (a + b);" or even just "return b - a;".

    briverymouse:
    Brendan:
    Maybe "ghc -static -optl-static" would help with the missing pieces, while hopefully avoiding a lot of unnecessary/unused code.

    Oh, you want me to actually disassemble the entire executable, not just my own code? Have fun with it: http://dl.dropbox.com/u/24516255/rom2num.asm

    Hehee - knowing that horrendous mess came from just one line of Haskell made me laugh more than the original article did. Perhaps you could improve performance and executable size by replacing the one liner with:

    public string rom2num(string r)
    {
        if (r == "I") return "1";
        if (r == "II") return "2";
        if (r == "III") return "3";
        if (r == "IV") return "4";
        if (r == "V") return "5";
        if (r == "VI") return "6";
        if (r == "VII") return "7";
        if (r == "VIII") return "8";
        if (r == "IX") return "9";
        //
        // Snipped LOTS of "code" here
        //
        if (r == "MMVIII") return "2008";
        if (r == "MMIX") return "2009";
        if (r == "MMX") return "2010";
        if (r == "MMXI") return "2011";
        return "E";
    }
  • (cs) in reply to Brendan
    Brendan:
    I didn't use a for loop in that C example because I was trying to mimic Haskell, and therefore trying to avoid the use of mutable variables.

    Don't try to "mimic" Haskell in C, C wasn't designed for functional programming. If I tried to mimic C code in Haskell, the result would be equally messy. The C implementation of foldl1 would be a foreach loop, not recursion. The Haskell implementation uses recursion. This is fine: the underlying implementation does not matter to an everyday programmer, as long as the result is the same.

    What Brendan said:
    Can a function use a monad?

    Yes, a function can use a monad.

    What Brendan actually meant:
    Can a function use a monad to create side-effects?

    No.

    Brendan:
    a monad (or whatever they call their "things that do have side-effects that aren't functions but were necessary because the language designers removed required functionality and had to reintroduce it as something different for their bait and switch trick to work")

    Haskell didn't "remove" required functionality and "reintroduce" it using monads. Haskell introduced monads, and got that required functionality (and more) for free, where other programming languages have a half-working implementation for some of the things monads can do.

    Brendan:
    And I hope you'll be able to see that this "insert the operator" description only works to describe a limited number of cases that (I assume) foldl1 supports. E.g. when the function is "return a + b;" or "return a - b;", but not when the function is "return a / (a + b);" or even just "return b - a;".

    Indeed, foldl1 is more powerful. In general, it's equivalent to the foreach loop I mentioned earlier:

    let accumulator = list[0]
    foreach element in list[1 ..]
        accumulator = func(accumulator, element)

    For every left-associative binary operator, that's the same as just inserting the operator. In other cases, it's equivalent to a foreach loop, so it's just as "natural" as just writing that loop out, but a lot easier to read.

    Brendan:
    Hehee - knowing that horrendous mess came from just one line of Haskell made me laugh more than the original article did. Perhaps you could improve performance and executable size by replacing the one liner with:
    ...

    Glad I made you laugh. Look, if you want to argue that compiled Haskell code is a mess, the argument is over and you win. Obviously it is. It has its own little "runtime" to support lazy evaluation. Try to implement lazy evaluation in C, and see if you can do it in a cleaner way than the GHC people did. But anyone programming today that thinks executable size and performance are more important than maintainability and ease of programming, should not have a job with computers.

  • (cs) in reply to tundog
    tundog:
    Clearly this problem is screaming for a Perl REGEX!
    Certainly, sir:
    %m = qw(M 1000 D 500 C 100 L 50 X 10 V 5 I 1); scalar(<>) =~ /(?:(I(?=[MDCLXV])|V(?=[MDCLX])|X(?=[MDCL])|L(?=[MDC])|C(?=[MD])|D(?=M))(?{$a-=$m{$1}})|([MDCLXVI])(?{$a+=$m{$2}}))+/i; print $a;
  • (cs) in reply to ObiWayneKenobi
    ObiWayneKenobi:
    What the hell is it with people and having to return "error codes" from everything? THIS ISN'T VB6. There are sophisticated error handling in modern languages, you don't need to return ints or bools all over the place.

    Depends on the circumstances. When something is a predictable and expected occurrence, e.g. there's no more data to read, returning a value that indicates that is preferable to the expense of throwing an exception.

    Methods and properties that return bool often go hand-in-hand with ones that generate exceptions. Methods like int.Parse() in .NET have a corresponding int.TryParse() for a reason. Sometimes you don't know if your input is actually a valid value for the method; the Try* methods parse the input without generating an exception if it's not valid. Similarly Stream derived classes have things like the "bool CanRead" properties so you can avoid the exception that would occur if you did Read() on a stream that can't be read from.

    If you have a method that has the possibility to generate an exception, make sure you provide a way of avoiding the exception.

  • (cs)

    Any bets on how long before briverymouse figures out he's being trolled?

  • Joey (unregistered) in reply to Matt Westwood
    Matt Westwood:
    Instead they offer verbal examples culled from textbooks -- 'manipulate an algebraic expression', 'attack a problem', 'exploit a theorem' -- as evidence that mathematics is a nest of aggression, violence, domination and sexism.

    Has this person EVER interacted with women?

    I) Manipulation, via the emotions, is so common as to render this example ludicrous in itself.

    II) Exploit, just look at dating and the common assumption that the man will pay.

    III) Attack; apparently they've never been to a divorce-court. More oftan than not, the woman is downright vindictive.

  • blank (unregistered) in reply to D-Coder
    D-Coder:
    PoPSiCLe:
    Why would you call it a Roman Numerals to decimal numbers converter? A Roman Numeral can, by definition, not be a decimal (the Romans didn't have "0"), and neither did they use decimals (only whole numbers).

    A better title for the "project" would be Roman Numerals to Integers (although this would also be wrong, as "integers" both incorporates negative numbers and zero), so the absolute correct naming would be Roman Numerals to Natural numbers (the numbers we learn when we learn to count, which are normally 1,2,3,4,5,6,7,8,9,10 etc)

    I can always count on TDWTF for my daily dose of pedantic dickweedery.

    i think you meant dickweedal pedantry

  • Klaus (unregistered)

    TRWTF is that his "code" counts as "managed to solve the problem"...

  • Roy Rasmussen (unregistered) in reply to PaulaBean
    PaulaBean:
    easy peasy.
            private int toInt(string roman) {
                int lastnum = 0;
                int total = 0;
                for (int c = roman.Length - 1; c >= 0; c--) {
                    string number = roman.Substring(c, 1);
                    int value = singleRomanToInt(number);
                    if (value < lastnum) total -= value;
                    else {
                        lastnum = value;
                        total += value;
                    }
                }
                return total;
            }
    
        private int singleRomanToInt(string romanchar) {
            switch (romanchar.ToUpper()) {
                case "I": return 1;
                case "V": return 5;
                case "X": return 10;
                case "L": return 50;
                case "C": return 100;
                case "M": return 1000;
                default:
                    return Convert.ToInt32("PaulaBean!");
            }
        }
    

    Almost, this will work if you only set LASTNUM = VALUE when LASTNUM != VALUE

  • zirias (unregistered)

    Ok, this is a lengthy one, but just for the fun accepts ANYTHING remotely looking like a roman number (ignoring all non-roman-numerical characters) -- and also returns the canonical roman form.

    > cat r2l.c
    
    #include <stdio.h>
    
    int result = 0;
    int intermediate = 0;
    int current = 0;
    int next = 0;
    
    int rdigit(char r)
    {
        switch(r)
        {
            case 'i':
            case 'I':
                return 1;
            case 'v':
            case 'V':
                return 5;
            case 'x':
            case 'X':
                return 10;
            case 'l':
            case 'L':
                return 50;
            case 'c':
            case 'C':
                return 100;
            case 'd':
            case 'D':
                return 500;
            case 'm':
            case 'M':
                return 1000;
        }
        return 0;
    }
    
    int rformat_pat(int *num, int dec, size_t *c, char **pos, int count, char r1, char r2)
    {
        while (*num >= dec)
        {
            if (*c < count)
            {
                **pos = '\0';
                return 0;
            }
    
            *c -= count;
            *(*pos)++ = r1;
            if (count == 2) *(*pos)++ = r2;
            *num -= dec;
        }
        return 1;
    }
    
    int rformat(char *buf, size_t n, int num)
    {
        size_t c = n - 1;
        char *pos = buf;
    
        if (!rformat_pat(&num, 1000, &c, &pos, 1, 'M', '\0')) return 0;
        if (!rformat_pat(&num, 900, &c, &pos, 2, 'C', 'M')) return 0;
        if (!rformat_pat(&num, 500, &c, &pos, 1, 'D', '\0')) return 0;
        if (!rformat_pat(&num, 400, &c, &pos, 2, 'C', 'D')) return 0;
        if (!rformat_pat(&num, 100, &c, &pos, 1, 'C', '\0')) return 0;
        if (!rformat_pat(&num, 90, &c, &pos, 2, 'X', 'C')) return 0;
        if (!rformat_pat(&num, 50, &c, &pos, 1, 'L', '\0')) return 0;
        if (!rformat_pat(&num, 40, &c, &pos, 2, 'X', 'L')) return 0;
        if (!rformat_pat(&num, 10, &c, &pos, 1, 'X', '\0')) return 0;
        if (!rformat_pat(&num, 9, &c, &pos, 2, 'I', 'X')) return 0;
        if (!rformat_pat(&num, 5, &c, &pos, 1, 'V', '\0')) return 0;
        if (!rformat_pat(&num, 4, &c, &pos, 2, 'I', 'V')) return 0;
        if (!rformat_pat(&num, 1, &c, &pos, 1, 'I', '\0')) return 0;
        *pos = '\0';
        return 1;
    }
    
    char line[1024];
    
    int main()
    {
        while (!feof(stdin))
        {
            char *c = fgets(line, 1024, stdin);
            if (!c) return;
    
            do
            {
                next = rdigit(*c++);
                if (next > 0)
                {
                    if (intermediate > 0 && current < next)
                    {
                        intermediate = next - intermediate;
                        if (intermediate < 1)
                        {
                            puts("undefined format.");
                            goto nextline;
                        }
                    }
                    else
                    {
                        if (current > 0 && current != next)
                        {
                            result += intermediate;
                            intermediate = 0;
                        }
                        intermediate += next;
                    }
                    current = next;
                }
            } while (*c != '\0');
    
            result += intermediate;
            rformat(line, 1024, result);
    
            printf("%d [%s]\n", result, line);
    
    nextline:
            result = 0;
            intermediate = 0;
            current = 0;
            next = 0;
        }
    }

    Well, i still like C ...

  • Romain (unregistered) in reply to briverymouse
    briverymouse:
    Haskell:
    foldl1 (+) [1 .. 10]
    Python:
    sum(range(11))

    Which one would you most easily understand? Obviously the Python one. Which one would you most easily understand once you know what foldl1 does? Obviously the Python one.

Leave a comment on “Roman Enumeration”

Log In or post as a guest

Replying to comment #:

« Return to Article