昨天,杰克(Jack)隔壁的那个孩子过来讲述他的不幸经历,想知道我是否可以帮助他解决他的问题。他一直在独自学习Ruby(在我的帮助下),并且经过几天的辛苦工作后才有了一个程序。在保存源代码之前,他不小心将其删除了。
幸运的是,他确实有打印输出。不幸的是,当他在学校时,他的妹妹用剪刀剪了他的打印输出。这是杰克(Jack)用她的手作拍摄的照片(在他用胶带将一些碎屑粘在一起之后)。
Jack希望我帮助他将程序重新组合在一起。我请他描述他正在研究的问题,但他非常沮丧,以至于他的回答完全是一团糟。
我整理了来自插条的信息,如下所示。
code = [
["def doit *args"], # position fixed
["y = [a,c,d].reduce", :block],
[:left, :right],
[:left, :right],
["args.rotate!"],
["z = yield(a,b)"],
["args.reverse!"],
["a-2*b+7*c-3*d+30*e-2*y+6*z"], # position fixed
["end"], # position fixed
["doit(1,21,13,4,55)", :block] # position fixed
]
第一行和最后三行的顺序很明显,但是我不知道其他行的顺序。钻屑中的两个必须在作业的左侧,两个在作业的右侧,两个是块。
在上面的数组中,由:left
,:right
和:block
表示的六个插值在此哈希中给出:
pos = {
left: ["a,b,*_,c =", "d,e ="],
right: ["args", "args.reverse"],
block: ["{ |x,y| x+y }", "{ |x,y| x-y }"]
}
杰克说,唯一的切口#=> 275
是他执行该方法时的返回值,所以我们可以写
rv = 275
我考虑过尝试不同的组合,但这似乎是没有希望的任务。当然,必须有某种方法可以使代码的重构自动化。
有人可以帮助我吗?建议可以,但我真的很想看看代码。
答案 0 :(得分:2)
由于您坚持认为这是一种代码编写服务,并且您希望看到一些代码,所以我有时间消磨时间,并且我喜欢生成代码的代码,而且我们都不关心信誉得分,这是我重建小杰克辛勤工作的尝试:
# jack_gen.rb
["a,b,*_,c =", "d,e ="].permutation do |l1, l2|
["args", "args.reverse"].permutation do |r1, r2|
["{ |x,y| x+y }", "{ |x,y| x-y }"].permutation do |b1, b2|
[
" y = [a,c,d].reduce #{b1}",
" #{l1} #{r1}",
" #{l2} #{r2}",
" args.rotate!",
" z = yield(a,b)",
" args.reverse!"
].permutation do |lines|
source = [
"def doit *args",
*lines,
" a-2*b+7*c-3*d+30*e-2*y+6*z",
"end",
"doit(1,21,13,4,55) #{b2}"
].join("\n")
rv = Object.new.instance_eval(source) rescue nil
puts "\n#{source}\n#=> #{rv}" if rv == 275
end
end
end
end
程序创建permutation
的2个左侧,2个右侧,2个块和6条非固定线(2!×2!×2!×6!= 5,760种可能性) 。然后,将它们组合成由方法定义和方法调用组成的源字符串(以及固定行)。创建一个新对象,并使用instance_eval
在该对象的上下文中评估该字符串,以抢救由于未定义变量而可能发生的异常(大约80%的生成方法是错误的)。
(方法调用的)结果为275
时,将打印相应的源代码(以及结果)。
我们有8个这样的程序:
$ ruby jack_gen.rb
def doit *args
args.reverse!
a,b,*_,c = args
args.rotate!
d,e = args.reverse
y = [a,c,d].reduce { |x,y| x-y }
z = yield(a,b)
a-2*b+7*c-3*d+30*e-2*y+6*z
end
doit(1,21,13,4,55) { |x,y| x+y }
#=> 275
def doit *args
args.reverse!
a,b,*_,c = args
args.rotate!
d,e = args.reverse
z = yield(a,b)
y = [a,c,d].reduce { |x,y| x-y }
a-2*b+7*c-3*d+30*e-2*y+6*z
end
doit(1,21,13,4,55) { |x,y| x+y }
#=> 275
def doit *args
args.reverse!
a,b,*_,c = args
args.rotate!
z = yield(a,b)
d,e = args.reverse
y = [a,c,d].reduce { |x,y| x-y }
a-2*b+7*c-3*d+30*e-2*y+6*z
end
doit(1,21,13,4,55) { |x,y| x+y }
#=> 275
def doit *args
args.reverse!
a,b,*_,c = args
z = yield(a,b)
args.rotate!
d,e = args.reverse
y = [a,c,d].reduce { |x,y| x-y }
a-2*b+7*c-3*d+30*e-2*y+6*z
end
doit(1,21,13,4,55) { |x,y| x+y }
#=> 275
def doit *args
args.reverse!
a,b,*_,c = args
args.rotate!
d,e = args.reverse
y = [a,c,d].reduce { |x,y| x-y }
z = yield(a,b)
a-2*b+7*c-3*d+30*e-2*y+6*z
end
doit(1,21,13,4,55) { |x,y| x+y }
#=> 275
def doit *args
args.reverse!
a,b,*_,c = args
args.rotate!
d,e = args.reverse
z = yield(a,b)
y = [a,c,d].reduce { |x,y| x-y }
a-2*b+7*c-3*d+30*e-2*y+6*z
end
doit(1,21,13,4,55) { |x,y| x+y }
#=> 275
def doit *args
args.reverse!
a,b,*_,c = args
args.rotate!
z = yield(a,b)
d,e = args.reverse
y = [a,c,d].reduce { |x,y| x-y }
a-2*b+7*c-3*d+30*e-2*y+6*z
end
doit(1,21,13,4,55) { |x,y| x+y }
#=> 275
def doit *args
args.reverse!
a,b,*_,c = args
z = yield(a,b)
args.rotate!
d,e = args.reverse
y = [a,c,d].reduce { |x,y| x-y }
a-2*b+7*c-3*d+30*e-2*y+6*z
end
doit(1,21,13,4,55) { |x,y| x+y }
#=> 275
这些程序中的 4个实际上是重复的,因为#{l1} #{r1}
/ #{l2} #{r2}
(Register Sole的提示)–足以使右边的变量,即" a,b,*_,c = #{r1}"
/ " d,e = #{r2}"
。
其余4个程序仅在z = yield(a, b)
的位置不同。假设杰克按字母顺序分配变量,我们可以选择一个z =
之后 y =
的变量:
def doit *args
args.reverse!
a,b,*_,c = args
args.rotate!
d,e = args.reverse
y = [a,c,d].reduce { |x,y| x-y }
z = yield(a,b)
a-2*b+7*c-3*d+30*e-2*y+6*z
end
doit(1,21,13,4,55) { |x,y| x+y }
#=> 275