我用常规的 Julia 代码解决了这个练习,并不太难:
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
这只能解决简单的数字问题,但可以使示例更短。现在我就是我,我对这种冗余感到震惊。所以我进行了迭代 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
这也是如此,但让我思考。我不能用元编程创建上面多余的
if
吗?无论如何,我想对此进行一些学习。
但我什至无法靠近:
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 code
end
function test(number)
@gen number
end
test(1111)
这是我能做的最好的事情,但后来我注意到我需要有
while
(所以这个代码不起作用)。现在我什至不知道要附加到哪个 .args
,因为那里还有几个 LineNumberNode
节点可以移动重要的节点。
我有一种感觉,我没有从正确的角度处理这个问题/我使用了正确的工具。
休息一下,突然间,事情就开始起作用了。
我的问题是,我正在考虑一种自上而下的方法,手动改变
.args
的 Expr
并从示例中计算我需要哪些索引等等......太手动和复杂了。
回过头来,我意识到我需要自下而上,从
I
案例作为一个完整的 if 开始,然后始终将我迄今为止所拥有的内容插入到更新、更大的 if
else
中。
最后,将其全部打包在一个
while
中并处理好数组,完成。
macro generate_ifs(number)
values = Dict(
'M' => 1000,
'C' => 100,
'X' => 10,
'I' => 1,
'D' => 500,
'L' => 50,
'V' => 5,
)
code = :()
for c in "IVXLCDM"
code = :(
if $(esc(number)) >= $(values[c])
push!(parts, $c)
$(esc(number)) -= $(values[c])
else
$code
end
)
end
return quote
parts = Char[]
while $(esc(number)) > 0
$code
end
return join(parts)
end
end
function to_roman(number::Int64)
0 < number < 4000 || error("must be between 1 and 3999 inclusive")
@generate_ifs number
end
无论如何,这就是我想出的解决方案,我也能够解出正确的数字。
我仍然对评论和想法感兴趣。