Ruby中类似于JavaScript自执行函数的等效写法

5

我经常在 JavaScript 中这样做

some_var || some_var = function(){ return "blah"}();

我想知道Ruby中的等效方法,以便我可以执行。
some_var ||= # sequence of operations

编辑

我注意到Proc.new.call,但我也在别人的代码中发现了这个:

a ||= begin
  # do some stuff
  # return some stuff
end

这是否等同于使用 Proc.new.call ??
编辑2 人们似乎对我想要实现的目标感到困惑。在JavaScript中想象一下:
function someExpensiveFunction(){
  # do some really expensive stuff
  return "some expensive calculations"
}

a || a = someExpensiveFunction();

显然,一旦设置a...调用一次昂贵的函数...在这种情况下我不关心作用域,我只需要我的返回值是一系列事件的计算序列而不是一个单一的值。

我相当确定我上面的示例a ||= begin; ... end;是等效的...


你究竟想要实现什么?Ruby具有块级作用域,这与JavaScript具有的词法作用域不同。通常你会在JS中看到匿名函数以避免作用域问题。 - Matt
不用考虑作用域,只需使用 ||= 干净的语法来设置一个涉及多行代码的变量。 - brad
6个回答

2
根据您的评论:
不关心作用域...只是希望使用“||=”来设置一个涉及多行代码的变量的干净语法
我不确定您为什么觉得需要使用 ||= 和 lambda。例如,您可以使用:
if(some_var.nil?)
   # do some stuff
   some_var = result_of_doing_some_stuf
end

或者,就像你在例子中所说的那样:
a ||= begin
  # do some stuff
  # return some stuff
end

我不清楚你为什么一定要使用proc或lambda。

但如果你坚持使用||=和lambda,可以这样做:

calculate = lambda { 1 + 1 }
some_var ||= calculate.call

我实际上正在尝试避免使用Proc/lambda,它只是作为一个例子被提出来,这就是为什么我引用了它。 - brad

1
s = Proc.new { 5 + 5 }.call

我刚刚看到了这样的代码:a ||= begin # some code end你见过吗?这段代码等价于什么? - brad
格式出了点问题,我会编辑我的帖子。 - brad

0

一开始我不确定你在问哪个部分。逻辑运算符||确实会影响你期望的操作。在Ruby中,等价于:

somevar = "blah" unless somevar;

如果 somevar 是 nil 值或 false 值,则更改为 "blah"。 如果 somevar 不是 nil 或 true,则该行不会被执行。 相反的操作是:
somevar = "blah" if somevar;

如果somevar为真或不为空,则将其分配为“blah”。

Ruby具有类似于自执行函数的语言特性,许多JavaScript库受到这些特性的启发。

查看此页面以获取更多信息:

http://www.ruby-doc.org/docs/ProgrammingRuby/html/tut_containers.html

在这里,“块和迭代器”部分会很有趣。此外,“事务块”和“块可以是闭包”也很重要。

基本上,Ruby的块和lambda表达式是最接近JavaScript自执行函数的东西。


我理解 ||= 的作用,我想要对一些函数进行 ||= 操作。 - brad
现在看到您的2个编辑,更清楚您想要完成什么。您已经接受了答案,因此我不会尝试编辑它。 - Berin Loritsch

0

你可以使用Lambda块来完成它

some_var ||= lambda { "something" }.call

或者

some_var ||= Proc.new { "something" }.call

在JS中,自执行函数通常用于避免污染作用域,因此您可以在内部使用局部变量但不会将其公开。不幸的是,在Ruby 1.9之前使用块时,情况并非如此,其中块没有自己的作用域。
# Ruby 1.8.7
x = "some initial value"
some_var ||= Proc.new { x = 10; x + 2 }.call #=> 12
x #=> 10

如果是这样的话,你可能有更好的解决方案来实现你想做的事情。Ruby 不是 Javascript

编辑:抱歉,我忘记了作用域内变量定义和变量赋值的区别。更新代码片段以反映这一点。


Ruby 1.8.6在上面的block范围示例中会导致x未定义。我所做过和阅读过的关于Ruby的所有内容都表明它几乎总是支持块作用域。 - Matt
是的,我想展示变量遮蔽。请看我的更新答案,你对块级作用域的理解是正确的。我想知道OP试图用那种编程风格做什么。 - Chubas
我还是有点困惑——在你上面的代码片段中没有变量遮蔽。事实上,你展示的是相反的情况。 - Matt
例如,在JS中,您可以通过在内部/匿名函数中使用var声明变量来获得具有相同名称但不同作用域的变量。如果您没有使用var标记它,则会通过闭包假定其为外部作用域的标识。在Ruby中,您永远不会像在JS中那样“声明”变量。内部块在闭包中捕获外部变量。如果它是阴影,结果将是“一些初始值”,但它并没有 - 结果是10,显示没有阴影,只是一个闭包。 - Matt
我的观点是,在 Ruby 中无法在块作用域中声明局部变量。请参考 https://gist.github.com/3687b09f40b6a1194999 。无论如何,我刚看到 OP 尝试做的事情,所以我们可以把讨论挪到聊天室里吗? - Chubas

0

你可以做:

def run
  @a ||= begin
    x = 1 + 1
    puts "Calculated x"
    x
  end
end
puts "run = #{run}" # => Calculated x, run = 2
puts "run = #{run}" # => run = 2

如果您想传递一个变量,可以使用lambda表达式

def run
  @a ||= lambda do |i|
    x = i + i
    puts "Calculated x"
    x
  end.call(1)
end
puts "run = #{run}" # => Calculated x, run = 2
puts "run = #{run}" # => run = 2

如果您要立即调用lambda函数,使用方法会更加清晰易读,而不是像魔法一样神秘。

def calc(i)
  x = i + i
  puts "Calculated x"
  x
end

def run
  @a ||= calc(1)
end
puts "run = #{run}" # => Calculated x, run = 2
puts "run = #{run}" # => run = 2

0

块(begin ... end)、proc 和 lambda 之间的区别在于它们处理返回值的方式。在块中不允许使用 return。在 Proc 中,return 会从定义它的位置返回;而在 Lambda 中,return 则会按预期执行。

因此,

def f()
  a = Proc.new do
          return 5 # exit from f
      end.call
  # never called
  a+10
end 

返回 5(而不是 15)

def g()
  a = lambda do
        return 5
  end.call
  a+10
end

返回15(如预期)

def f()
  a = begin
        return 5
  end 
  a+10
end

无法编译。

如果您的块中没有使用return,则可以使用begin...do(我喜欢它)。


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