为什么ruby循环是这样设计的?

时间:2011-02-22 10:59:07

标签: ruby

正如标题中所述,我很想知道为什么Ruby决定放弃经典的for循环,而是使用array.each do ...

我个人认为它的可读性稍差,但这只是我个人的意见。无需争论。另一方面,我认为他们是故意设计的,应该有充分的理由。

那么,以这种方式放置循环有什么好处?这个设计决定的“存在理由”是什么?

7 个答案:

答案 0 :(得分:7)

这个设计决策是Ruby如何结合面向对象和函数式编程范例的完美例子。这是一个非常强大的功能,可以生成简单易读的代码。

有助于了解正在发生的事情。当你跑:

array.each do |el|
  #some code
end

您正在调用each对象的array方法,如果您认为该变量名称,则该方法是Array类的实例。您正在将代码块传递给此方法(块相当于一个函数)。然后,该方法可以评估此​​块并使用block.call(args)yield args传递参数。 each只是遍历数组,对于每个元素,它调用您传入的块作为参数。

如果each是使用块的唯一方法,那么这不是那么有用,而是许多其他方法,您甚至可以创建自己的方法。例如,数组有一些迭代器方法,包括map,它们每个都做同样但返回一个包含块的返回值的新数组,select返回一个只包含元素的新数组块返回true值的旧数组。使用传统的循环方法做这些事情会很繁琐。

以下是如何使用块创建自己的方法的示例。让我们创建一个every方法,它有点像地图,但只适用于数组中的每个n项。

class Array #extending the built in Array class
  def every n, &block #&block causes the block that is passed in to be stored in the 'block' variable. If no block is passed in, block is set to nil
    i = 0
    arr = []
    while i < self.length
      arr << (   block.nil? ?  self[i] : block.call(self[i])   )#use the plain value if no block is given
      i += n
    end
    arr
  end
end

此代码允许我们运行以下内容:

[1,2,3,4,5,6,7,8].every(2) #= [1,3,5,7]   #called without a block
[1,2,3,4,5,6,7,8,9,10].every(3) {|el| el + 1 } #= [2,5,8,11]   #called with a block

块允许表达语法(通常称为内部DSL),例如,Sinatra web微框架。 Sinatra使用块的方法来简洁地定义http交互。 例如

get '/account/:account' do |account|
  #code to serve of a page for this account
end

如果没有Ruby的块,这种简单性很难实现。

我希望这能让你看到这种语言功能有多强大。

答案 1 :(得分:2)

我认为这主要是因为Matz有兴趣探索一种完全面向对象的脚本语言,当他构建它时会是什么样子;此功能主要基于CLU编程语言的迭代器。

结果证明它提供了一些有趣的好处;提供each方法的类可以“混合”Enumerable模块,为客户端提供各种预先制作的迭代例程,从而减少繁琐的样板阵列/列表/必须编写的hash / etc迭代代码。 (见过java 4 and earlier iterators?)

答案 2 :(得分:1)

当你提出这个问题时,我认为你有点偏颇。另一个人可能会问“为什么C for循环是这样设计的?”。想一想 - 如果我只想迭代数组的元素,为什么我需要引入计数器变量?比方说,比较这两个(都是伪代码):

for (i = 0; i < len(array); i++) {
  elem = array[i];
  println(elem);
}

for (elem in array) {
  println(elem);
}

为什么第一种感觉比第二种更自然,除了历史(几乎是社会学)的原因?

Ruby,高度面向对象,更进一步,使它成为一个数组方法:

array.each do |elem|
  puts elem
end

通过做出这个决定,Matz只是为多余的语法构造(foreach循环)使语言更轻,将其用途委托给普通的方法和块(闭包)。我最感谢Ruby就是出于这个原因 - 在语言特征方面真正理性和经济,但保留了表现力。

我知道,我知道,我们在Ruby中有for,但大多数人都认为这是不必要的。

答案 3 :(得分:0)

Ruby被设计为具有表现力,阅读就像是在说话......然后我认为它只是从那里发展而来。

答案 4 :(得分:0)

do ... end块(或{ ... })形成一个所谓的块(几乎是一个闭包,IIRC)。可以将块视为匿名方法,您可以将其作为参数传递给另一个方法。块在Ruby中经常使用,因此这种迭代形式很自然:do ... end块作为参数传递给方法each。现在,您可以为each编写各种变体,例如以反向或其他方式进行迭代。

还有语法糖形式:

for element in array
   # Do stuff
end

块也用于过滤数组:

array = (1..10).to_a
even = array.select do |element|
    element % 2 == 0
end
# "even" now contains [2, 4, 6, 8, 10]

答案 5 :(得分:0)

我认为这是因为它强调Ruby背后的“一切都是对象”的理念:在对象上调用each方法。

然后切换到另一个迭代器比更改例如for循环的逻辑要平滑得多。

答案 6 :(得分:0)

这来自Smalltalk,它将控制结构实现为方法,从而减少了关键字的数量并简化了解析器。因此,允许控制结构充当语言定义的概念。

在ST中,即使条件是方法,也是如此:

boolean.ifTrue ->{executeIfBody()}, :else=>-> {executeElseBody()}

最后,如果你忽略了你的文化偏见,那么更容易解析机器也会更容易自己解析。