在Ruby方法中,我应该创建一个proc还是一个方法?

时间:2014-07-17 07:42:53

标签: ruby methods proc

只想询问正确的做法是什么。

我的偏好是使用过程,因为我认为在方法中定义方法有点不整洁,应该只在必要时才进行。为了解决它,我只是使用过程。

这样做的正确/更好的方法是什么?为什么? (除了proc能够访问自己定义的主要方法变量之外)

def meth( params_prime )

  calculations = do_something_with_whatever

  def sub_meth( params_sub )
    do_something_with_params_sub
  end

  sub_meth_params(calculations) # is this better?

  proc1 = proc{ |params_sub| do_something_with_params_sub }
  proc1.call(calculations) # or is this?

end

3 个答案:

答案 0 :(得分:1)

目前尚不清楚你的具体用例是什么,但我肯定会选择procs或lambdas。在动态定义proc或lambda时,开销较少,因此它们是可通过的,因此如果需要,您可以返回它们,并且可以在函数外部使用它们。

使用“def”将方法公开为当前方法范围之外的实例方法(因此在包含类中,在您的情况下可能为Object)。这可能是也可能不是你想要的。如果要使用仅在本地范围内可用的匿名函数,请使用lambda。

Proc vs Lambda:我通常更喜欢使用lambdas,因为它们表现得更“可预测”,这意味着:正如您所期望的那样(检查传递的变量,并返回从lambda返回,proc从被调用的范围返回) 。但是从你的例子来看,很难推断出适用的内容。我认为关键的区别在于:lambas是可以传递的,因此表现得更加明智。如果这不是您的使用案例,请使用Proc :)(差异为write-up)。

答案 1 :(得分:0)

定义方法内部的方法是Ruby的一个特性,可能有其用途。但有些东西告诉我,当你还是初学者级别的Rubyist时,你会问一个非常高级的问题。你知道默认的定义是什么吗?如果没有,check this article by Yugui

Proc在Ruby中非常重要,但是新手倾向于使用它们而不是在适当的对象中定义方法,这就是我从你的问题中得到的确切气味。 Ruby系列OO语言中常用的方法是定义对象的方法:

class Foo
  def bar *params
    # do something with params
  end
end

由于您不理解在方法中定义方法的含义,因此请在接下来的6个月内不要这样做。一旦理解了对象,就可以再次开始尝试这个非常高级的功能。

附录:

由于您表现得很有兴趣,让我告诉您在顶级def使用def是一件容易接受的事情。通常,当您在某个类上定义方法而不进一步装饰时,它将成为该类的公共实例方法:

class X
  def foo; "foo" end
end

X.instance_methods.include? :foo
#=> true

def中使用def时,内部def的定义将为X

class X
  def bar
    def baz
      "baz"
    end
    "bar"
  end
end

执行上面的代码时,实例方法#bar在X上定义:

X.instance_methods.include? :bar
#=> true

但#baz还没有:

X.instance_methods.include? :baz
#=> false

只有在您至少调用#bar一次后才能在X上定义方法:

X.new.bar
#=> "bar"
X.instance_methods.include? :baz
#=> true

现在我想请你欣赏刚刚发生的可怕事情:一个实例刚修改了它的母班。这是违规行为。违反OO设计这样一个基本原则,我甚至不确定它有一个名字。这种技术非常适合混淆编码竞赛,但在制作中,它是禁忌。 Ruby让你可以自由地打破这种禁忌,为你提供绳索,但你不能在任何正常情况下做到这一点。

那么什么可能比类定义中def内的def更糟糕?答案是,def位于顶层的def内。让我告诉你原因。通常,当您在顶层定义def的方法时,默认的定义为Object,但顶级定义成为对象的私有实例方法。这是为了防止顶级def的意外后果,因为几乎所有Ruby对象都继承自Object。例如,如果您定义:

class Object
  def foo; "foo" end
end

现在你的所有对象都会响应foo:

foo        #=> "foo"
1.foo      #=> "foo"
[].foo     #=> "foo

当我们在顶层定义方法时,我们通常只打算在顶层使用该方法,并且不希望每个对象都继承它。因此,顶级def变为私有:

hello      #=> NameError: undefined local variable or method `hello' for main:Object
1.hello    #=> NoMethodError: undifined method 'hello' for 1:Fixnum

现在我们在顶层使用def

def hello; "hello" end

我们可以看到方法#hello尚未成为Object的实例方法:

Object.instance_methods.include? :hello
#=> false

神秘地,它成了它的私人方法:

Object.private_instance_methods.include? :hello
#=> true

这样,我们避免了为每个对象定义#hello方法的意外后果。但遗产就在那里。错误消息已更改:

1.hello    #=> NoMethodError: private method 'hello' called for 1:Fixnum

我们可以通过#send

强行调用该方法
1.send :hello
#=> "hello"

神秘地说,在顶层,我们可以在没有#send的情况下调用这个私有方法:

hello
#=> "hello"

现在,当您在def顶级def进行def bar def baz; "baz" end "bar" end 时会发生什么:

Object#bar

您以预期的方式定义私有实例方法Object#baz。但是当你召唤它时,顶级魔法不再起作用,而公共方法bar #=> "bar" 被定义:

#baz

这样,不仅仅是顶层,而且每个Ruby对象都被1.baz #=> "baz" Class.baz #=> "baz" 方法污染了:

{{1}}

这就是为什么我告诉你不要使用这个习惯用语,直到你从无意识无能的程度发展到有意识的无能水平。我建议您阅读有关top level methods in Ruby的更多信息。

答案 2 :(得分:0)

如果要使用sub_func从其他方法的调用中封装它,可以使用类将函数和sub_func组合在一起并使sub_func为private。否则,如果您希望将此函数作为参数进一步传递,则可以将其声明为lamda。

def func params_prime
  sub_func = ->(params_sub){do_something_with_params}  
  sub_func.call(params_prime)
end