【问题标题】:How can I make a local variable in string interpolation inaccessible from outside?如何使字符串插值中的局部变量无法从外部访问?
【发布时间】:2018-05-24 03:27:12
【问题描述】:

在这段代码中:

s = "#{a = 2; b = 3; a * b}" # =>"6"
a # => "2"

局部变量ab 在字符串插值内创建,并且可以在字符串插值之外访问,因为字符串插值不是块,尽管有花括号。

我想在字符串插值中创建局部变量,这些变量仅在代码中需要,在其他地方不需要,在字符串插值完成后应将其删除,以尽量减少副作用和潜在的变量冲突。

我尝试制作一个代码块来实现变量的局部性,但没有成功:

s = "#{{a = 2; b = 3; a * b}}"

do...end 相同。

s = "#{do a = 2; b = 3; a * b; end}"

【问题讨论】:

  • 字符串插值的花括号--{ 前面有一个井号--与块无关。 Matz 也可以将其定义如下:n = 3; "#|n|".
  • 你需要一个单独的作用域,比如使用一个方法或者过程。例如,您可以写成s = ->{ "#{a = 2; b = 3; a * b}" }.call #=> "6"; a #=> NameError: undefined local variable or method 'a' for main:Object
  • s = "#{->() { a = 2; b = 3; a * b }.()}"
  • @CarySwoveland 你可能想把它们变成答案。
  • 因此,此时的问题应该是,如果像命名函数/方法这样不那么神秘的东西可能不会对代码的任何未来读者(包括您自己)更有利。

标签: ruby local-variables string-interpolation


【解决方案1】:

只是出于好奇。以下代码不会删除局部变量,但它会有效地取消设置它们的值,因此在此之后调用a(或b)将返回nil

%Q|#{
  a = 2
  b = 3
  (a * b).tap do 
    binding.local_variable_set(:a, nil)
    binding.local_variable_set(:b, nil)
  end
}|

下面的方法将在这些语句之后访问a,而不是引发NameError: undefined local variable or method 'a' ...

"#{->() { a = 2; b = 3; a * b }.()}"

# or, credits to @engineersmnky
"#{->(a = 2, b = 3) { a * b }.()}"
"#{->(a, b) { a * b }.(2, 3)}"

还有:

"#{instance_eval { a = 2; b = 3; a * b }}"

【讨论】:

  • 在我看来更干净一点是"#{->(a=2,b=3) {a * b}.()}""#{->(a,b) { a * b}.(2,3)}"
【解决方案2】:

这是我的尝试:

> s = "#{def yielder; yield; end; yielder do a=2; b=3; a*b; end;}"
#=> "6" 

> a
#=> NameError: undefined local variable or method `a' for main:Object
> b
#=> NameError: undefined local variable or method `b' for main:Object

块通常使用定义块时存在的绑定,除非现有绑定与块变量冲突并且使用块变量。可以在块中定义额外的本地绑定,当块执行完成时这些绑定将丢失。

【讨论】:

  • 管道变量|a,b| 在这种情况下毫无意义,因为yield 只是将控制权传递给块,但没有传递任何参数
  • @engineersmnky :是的,你是对的,让我删除不必要的管道变量
【解决方案3】:

也是一种使用新的 Ruby 2.5 yield_self 特性的方法

"#{ 2.yield_self{|a| 3.yield_self{|b| a*b}} }"

【讨论】:

  • 这有点道理,因为如果你预先知道这些值(如果你不知道,上面的内容根本不可能),你可以使用 "#{2 * 3}" 达到同样的效果。
  • 当然,在实际程序中,我会使用变量名而不是文字来通过计算得出其他值。因此,如果我的程序中有一些名为“c”和“d”的其他变量,我可以这样写: s="#{ c.yield_self{|a| e=1.0/a; d.yield_self{|b| a*e }} }"
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-04-15
  • 2020-07-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多