4

所以,也许我把一些不那么难的事情复杂化了,但是就这样吧。

在 Ruby 中,有一种称为 .each 的循环方法。我认为这很酷——但我发现不太酷的是关于它之后的管道(或任何其他 Ruby 中的 do-type 循环,看起来)的东西的数量。有时管道中只有一件事:

basket.each do |fruit|
    puts "This is a #{fruit}.\n"
end

但有时,这个管道中有两件事,就像这样:

contacts.each do |name, profession|
    puts "#{name} is a #{profession}.\n"
end

所以我现在想知道的是,该管道中是否有可能有两个以上的项目?就像我有一个巨大的、又大又丑的多暗阵列?

如果我将东西添加到我的管道并且它们不存在怎么办?它会在管道中给出零​​值吗?还是会抛出错误?

再说一次,对不起,如果这对长期的 Ruby 爱好者来说是显而易见的,但我来自严格类型变量的领域,我现在也离开 PHP 领域。:)

编辑

那么,如果我有这样的事情怎么办:

categories = [["Bathroom", "Bathroom Fixtures", "Plumbing"],
                      ["Ceiling Fixtures", "Chandeliers", "Flush Mounts", "Mini Chandeliers", "Semi-Flush Mounts", "Pendants", "Track Lighting", "Mini Pendants", "Directional", "Island/Pool Table", "Recessed Lighting"],
                      ["Outdoor", "Exterior", "Landscape Lighting"],
                      ["Fans", "Fans", "Fan Accessories"],
                      ["Lamps", "Lamps", "Shades"],
                      ["Wall Fixtures", "Sconces", "Foyer/Hall Lanterns"],
                      ["Specialty", "Undercabinet", "Light Bulbs", "Lighting Controls", "Glass", "Specialty Items", "Utility"],
                      ["Home Decor", "Decor/Home Accents", "Furniture"]]

我可以这样循环吗?

categories.each do |category, subcats|
    puts "The main category is #{category} and the sub categories are: "
    subcats.each do |subcat|
        puts "#{subcat}, "
    end
end
4

4 回答 4

5

让我们从方法的分解开始each

a = [1,2,3,4,5]

a.each do |num|
  puts num
end

# 1
# 2
# 3
# 4
# 5

do ... end部分称为block

该块接受一个参数(数组中的一个元素)

将参数传递给块的方式是使用|'s

如果您向块提供多个参数:

a.each do |num, x|
  puts num
  puts x
end

# 1
# 
# 2
# 
# 3
# 
# 4
# 
# 5 
# 

xnil每次迭代。

让我们自己编写一个使用块的方法,以便您了解它们是如何工作的。

def my_each(a=[])
  a.each do |x|
    yield x if block_given?
  end
end

my_each(a) do |num|
  puts num
end

yield x就是说,执行提供的块并传递x给它。

如果您将另一个参数传递给您的block,它是nil. 为什么?

我们的实现对my_each第二个参数一无所知,所以它什么都不知道,yield所以它仍然存在nil

于 2012-07-06T17:14:53.963 回答
4

当你有一个简单的数组时,会发生以下事情:

arr = [1,2,3,4]

arr.each do |x|
  p x
end
1
2
3
4
=> [1,2,3,4]

arr.each do |x,y|
  p x
  p y
end
1
nil
2
nil
3
nil
4
nil
=> [1,2,3,4]

因此,如果 ruby​​ 不知道将什么放入 block 参数中,它只是将其设置为nil. 现在考虑一个嵌套数组:

arr = [[1,2],[3,4],[5,6]]

arr.each do |x|
  p x
end
[1, 2]
[3, 4]
[5, 6]
=> [[1,2],[3,4],[5,6]]

arr.each do |x,y|
  p x
  p y
end
1
2
3
4
5
6
=> [[1,2],[3,4],[5,6]]

在这种情况下,ruby 假设您要将内部数组的两个元素分配给块变量xy。同样的事情也适用于哈希,其中 Ruby 将键和值分配给xand y

hash = {1 => 2, 3 => 4, 5 => 6}

hash.each do |x,y|
  p x
  p y
end
1
2
3
4
5
6
=> {1=>2,3=>4,5=>6}

当嵌套数组中没有足够的元素时nil,确实会分配块变量。当它们太多时,它们被简单地丢弃:

arr = [[1,2,3],[4,5],[6]]

arr.each do |x,y|
  p x
  p y
end
1
2
4
5
6
nil
=> [[1,2,3],[4,5],[6]]

很简单!

编辑

至于您编辑的问题:不,您不能将此 1:1 应用于 Ruby 代码,您必须手动将 splat 运算符 ( *) 应用于subcats. 这样,ruby 将所有剩余元素分配给 'splatted' 块变量:

categories.each do |category,*subcats|
    puts "The main category is #{category} and the sub categories are: "
    subcats.each do |subcat|
        puts "#{subcat}, "
    end
end

虽然我会生成一个逗号分隔的子类别列表,如下所示:

categories.each do |category,*subcats|
    puts "The main category is #{category} and the sub categories are: "
    puts subcats.join(', ')
end

编辑 2

哦,你不会通过为它的元素定义很多块参数来处理一个巨大的丑陋邪恶的多维数组。您可能会像在几乎所有其他语言中一样使用嵌套循环遍历它,只是因为您永远不知道它包含多少元素。

于 2012-07-06T17:21:16.310 回答
1

您正在谈论的管道是块“变量”的参数列表。实际上那是某种函数指针,管道标记了参数列表。

检查array.each的描述。

这不是魔术,参数的数量是在块中定义的,你不能添加更多,如果你这样做了,它们将不会得到一个值。原因是“有时”可能不止一个,因为它可能是一个 hash.each,它有两个参数,一个键和一个值。

您可以使用块参数创建自己的函数,请阅读此

对于您的迭代问题,您可以使用哈希,也可以编写自己的迭代器。

于 2012-07-06T17:12:08.373 回答
1

一个块的多个参数

Array#each遍历一个数组对象,并将单个对象传递到块中或返回一个枚举器。您可以重新定义此行为,但如果您一次需要多个值,#each 是错误的方法;请参阅Enumerator#each_slice以获取替代方案。

数据结构

使用正确的数据结构会更容易解决您的问题。您应该考虑使用散列而不是数组。例如:

categories = 
  {"Bathroom"=>["Bathroom Fixtures", "Plumbing"],
   "Ceiling Fixtures"=>["Chandeliers", "Flush Mounts", "Mini Chandeliers"]}

categories.each do |key, value|
  puts "#{key}:"
  value.each { |v| puts "\t%s" % v }
end

这将返回:

Bathroom:
    Bathroom Fixtures
    Plumbing
Ceiling Fixtures:
    Chandeliers
    Flush Mounts
    Mini Chandeliers
于 2012-07-06T19:13:44.640 回答