在Ruby中使用变量名=value的格式

3
我想为我的简单Ruby函数添加一些调试,我编写了如下的函数:
def debug(&block)
  varname = block.call.to_s
  puts "#{varname} = #{eval(varname,block)}"
end

debug {:x} #prints x = 5
debug {:y} #prints y = 5

我知道eval是有害的。所以我有两个问题。

  1. 有没有不使用eval的方法来编写debug方法?如果没有,有没有首选方法来实现这一点?
  2. 有没有办法将参数列表传递给此方法?我理想情况下希望使用debug {:x, :y.:任意数量的变量}。但我无法弄清如何在debug方法中实现这一点(即接受参数列表)
2个回答

11

只需使用数组。 您可以使用Array方法确保始终有一个数组,即使有人仅传递单个值:

def debug(&block)
  Array(block[]).each do |var| puts "#{var} = #{eval var.to_s, block}" end
end

x, y = 3, 5

debug {:x} # => "x = 3"
debug {[:x, :y]} # => "x = 3" "y = 5"

顺便提一句:在 Ruby 1.9 中,将块传递为绑定不再起作用。(尽管文档说它可以工作。)您必须显式调用 Proc#binding 以获得该 ProcBinding 对象:

def debug(&block)
  Array(block.()).flatten.each do |var|
    puts "#{var} = #{eval var.to_s, block.binding}"
  end
end

幸运的是,在Ruby 1.8中,这个已经可以工作,所以你可以通过包含它来使你的代码具有未来性。

另一种选择是完全放弃块。我的意思是,你已经强制使用debug的用户使用通过块传递参数而不是括号的陌生习惯语。为什么不强制他们只传递绑定呢?

def debug(*vars, bnd)
  vars.each do |var|
    puts "#{var} = #{eval var.to_s, bnd}"
  end
end

x, y = 3, 5

debug :x, binding # => "x = 3"
debug :x, :y, binding # => "x = 3" "y = 5"

这种方法具有更大的灵活性,因为它们实际上可以传递一个与调用点不同的绑定,例如,如果他们想在应用程序的不同部分中调试代码,则可以使用它。


顺便说一下:Ruby 1.9.2的参数内省功能很有趣(Proc#parameters):

def debug(&block)
  block.parameters.map(&:last).each do |var|
    puts "#{var} = #{eval var.to_s, block.binding}"
  end
end

x, y = 3, 5

debug {|x|} # => "x = 3"
debug {|x, y|} # => "x = 3" "y = 5"

@Jörg,干得好。你认为Array(stuff)比[*stuff].flatten更易懂吗? - Wayne Conrad
@Wayne Conrad:很可能。坦白地说,我没有想到这一点,因为splat-flatten习惯用法已经深深烙印在我的脑海中。如果传递多个参数,Array()会出错,这就是为什么我避免使用它的原因,但在这种情况下,语言规范保证rest参数将始终是一个数组。 - Jörg W Mittag
非常好的写作,Jörg。这帮助我理解了一些有关块的内容,这些内容让我困惑了很长时间。+1 - maček
Jörg,你能解释一下 bindingdef debug(*vars, bnd) 示例中的作用吗? - maček
1
@smotchkiss:......在定义的地方,因此我们需要获取那个地方的“绑定(Binding)”。在这个示例中,我们将Binding明确地作为参数传递到方法中。在其他示例中,我们使用一个Proc对象(或更准确地说,我们使用一个块,并用&符号将其转换为Proc)。在Ruby中,块是闭包(即它们“记住”在定义它们的地方存在的变量,而不是它们执行的地方),这意味着它们携带着一个Binding。因此,我们可以从块中提取Binding而不是将其传递。 - Jörg W Mittag
显示剩余3条评论

0

我该如何使用这些方法从每个循环中获取变量的名称和值,将它们作为嵌套哈希表的键和值?

*attributes数组作为方法的参数传递。 我想遍历它,并将每个变量的名称用作嵌套哈希表的键,变量的值作为值。

我得到了这个:

def get_varname(&block)
    varname = block.call.to_s
    return varname
end

def create_instance_hash(env_attrs_obj, *attributes)
    if not env_attrs_obj.has_key?("instances")
        env_attrs_obj["instances"] = ""
    end
    attributes.each do |attr|
        attr_name = get_varname{:attr}
        env_attrs_obj["instances"][attr_name] = attr
    end
end

instance_nat_ip_pub = "ip_pub_addr"
instance_nat_ip_prv = "ip_addr"
instance_nat_ami = "AMI_NAME"
instance_nat_aws_ssh_key_id = "AWS_SSH_KEY_ID"
instance_nat_id = "instance_id"

env_hash = {}

create_instance_hash(env_hash, *[instance_nat_ip_pub, instance_nat_ip_prv, instance_nat_ami, instance_nat_aws_ssh_key_id, instance_nat_id])

但是:

def attrs_each_test(*attributes)
    attributes.each do |attr|
        puts get_varname{:attr}
    end
end

并且它输出:

attr
attr
attr
attr
attr

当我运行这个create_instance_hash时出现错误

Line 12:in `[]=': string not matched (IndexError)
    from t.rb:12:in `create_instance_hash'
    from t.rb:10:in `each'
    from t.rb:10:in `create_instance_hash'
    from t.rb:24

我该如何纠正这个错误并实现我的目标?


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接