跳至内容

方法参数

这是方法参数和调用参数的正式规范。

方法定义的组成部分

方法定义包含以下部分

  • 必需和可选的位置参数
  • 一个可选的 splat 参数,其名称可以为空
  • 必需和可选的命名参数
  • 一个可选的双 splat 参数

例如

def foo(
  # These are positional parameters:
  x, y, z = 1,
  # This is the splat parameter:
  *args,
  # These are the named parameters:
  a, b, c = 2,
  # This is the double splat parameter:
  **options
)
end

它们中的每一个都是可选的,因此一个方法可以没有双 splat、没有 splat、没有命名参数以及没有位置参数。

方法调用的组成部分

方法调用也包含一些部分

foo(
  # These are positional arguments
  1, 2,
  # These are named arguments
  a: 1, b: 2
)

此外,调用参数可以包含 splat (*) 或双 splat (**)。Splat 将 元组 扩展为位置参数,而双 splat 将 命名元组 扩展为命名参数。允许多个参数 splat 和双 splat。

调用参数如何与方法参数匹配

在调用方法时,将调用参数与方法参数匹配的算法如下

  • 首先,位置调用参数与位置方法参数匹配。它们的数量必须至少等于没有默认值的位置参数数量。如果有一个带有名称的 splat 参数(没有名称的情况在下面解释),则允许更多位置参数,并且它们将被捕获为一个元组。位置参数永远不会匹配到 splat 参数之后。
  • 然后,命名参数将按名称与方法中的任何参数匹配(它可以位于 splat 参数之前或之后)。如果一个参数已经被位置参数填充,那么它是一个错误。
  • 多余的命名参数将被放置在双 splat 方法参数中,作为 命名元组(如果存在),否则它是一个错误。

当一个 splat 参数没有名称时,这意味着不能再传递位置参数,并且任何后续参数都必须作为命名参数传递。例如

# Only one positional argument allowed, y must be passed as a named argument
def foo(x, *, y)
end

foo 1        # Error, missing argument: y
foo 1, 2     # Error: wrong number of arguments (given 2, expected 1)
foo 1, y: 10 # OK

但是,即使一个 splat 参数有名称,它后面的参数也必须作为命名参数传递

# One or more positional argument allowed, y must be passed as a named argument
def foo(x, *args, y)
end

foo 1             # Error, missing argument: y
foo 1, 2          # Error: missing argument; y
foo 1, 2, 3       # Error: missing argument: y
foo 1, y: 10      # OK
foo 1, 2, 3, y: 4 # OK

还有一种方法,它只接收命名参数(并列出它们),方法是在开头放置星号

# A method with two required named parameters: x and y
def foo(*, x, y)
end

foo            # Error: missing arguments: x, y
foo x: 1       # Error: missing argument: y
foo x: 1, y: 2 # OK

星号后的参数也可以具有默认值。这意味着:它们必须作为命名参数传递,但不是必需的(所以:可选命名参数)

# x is a required named parameter, y is an optional named parameter
def foo(*, x, y = 2)
end

foo            # Error: missing argument: x
foo x: 1       # OK, y is 2
foo x: 1, y: 3 # OK, y is 3

因为 splat 参数之后(没有默认值)的参数必须按名称传递,所以具有不同必需命名参数的两个方法会重载

def foo(*, x)
  puts "Passed with x: #{x}"
end

def foo(*, y)
  puts "Passed with y: #{y}"
end

foo x: 1 # => Passed with x: 1
foo y: 2 # => Passed with y: 2

位置参数始终可以按名称匹配

def foo(x, *, y)
end

foo 1, y: 2    # OK
foo y: 2, x: 3 # OK

外部名称

可以为方法参数指定一个外部名称。外部名称是作为命名参数传递参数时使用的名称,而内部名称是在方法定义内部引用参数时使用的名称

def foo(external_name internal_name)
  # here we use internal_name
end

foo external_name: 1

这涵盖了两个用例。

第一个用例是使用关键字作为命名参数

def plan(begin begin_time, end end_time)
  puts "Planning between #{begin_time} and #{end_time}"
end

plan begin: Time.local, end: 2.days.from_now

第二个用例是在方法体内部使方法参数更具可读性

def increment(value, by)
  # OK, but reads odd
  value + by
end

def increment(value, by amount)
  # Better
  value + amount
end