Quantcast
Channel: Recent Questions - Stack Overflow
Viewing all articles
Browse latest Browse all 12111

Solving Roman numerals with Julia's metaprogramming

$
0
0

I solved the exercise with regular Julia code, wasn't too hard:

function to_roman(number::Int64)    0 < number < 4000 || error("not in range")    parts = Char[]    while number > 0        if number >= 1000            push!(parts, 'M')            number -= 1000        elseif number >= 500            push!(parts, 'D')            number -= 500        # ...            number -= 10        elseif number >= 5            push!(parts, 'V')            number -= 5        elseif number >= 1            push!(parts, 'I')            number -= 1        end    end    join(parts)end

This only solves naïve numerals, but keeps the example shorter. Now me being me, I'm appalled by the redundancy in this. So I made iteration 2:

const values = Dict('M' => 1000,'C' => 100,'X' => 10,'I' => 1,'D' => 500,'L' => 50,'V' => 5,)Base.:+(number::Integer, c::Char) = number + values[c]Base.:-(number::Integer, c::Char) = number - values[c]function to_roman(number::Int64)    0 < number < 4000 || error("must be between 1 and 3999 inclusive")    parts = Char[]    while number > 0        for digit in "MDCLXVI"            if number >= values[digit]                push!(parts, digit)                number -= digit                break            end        end    end    join(parts)end

That also does it, but got me thinking. Couldn't I create the redundant ifs above with metaprogramming? That's something I wanted to learn about anyway and this seems like a not too hard place to wet my feet. But it's also not overly simple, like an unless.

I'm aware that this is not what you use metaprogramming for. It is about the learning experience. Just as nobody needs to compute Fibonacci, but still we all learn to code it in FP.

But I can't even get close:

macro gen(number)    code = quote        parts = Char[]        while $(esc(number)) > 0        end    end    for (i,c) in enumerate("MDCLXVI")        exp = :(            if $(esc(number)) >= $(values[c])                push!(parts, $c)                $(esc(number)) -= $(values[c])            end        )        if i > 1            exp.head = :elseif        end        # This is not correct anymore because I introduced the while.        push!(code.args, exp)    end    push!(code.args, :(return parts))    dump(code)    return codeendfunction test(number)    @gen numberendtest(1111)

This is the best I could do, but then I noticed I need to have the while around (so this code doesn't work). Now I don't even know which .args to append to, because also there are several LineNumberNode nodes in there that shift the important ones around.

I got the feeling, I'm not approaching this from the right angle / am using the right tools.

So the overall question would be “How can I use metaprogramming to abstract away the if chain and redundancy of the primitive example in spirit of the second example?”


Viewing all articles
Browse latest Browse all 12111

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>