Posts Tagged ‘ruby’
深入浅出Ruby的blocks, Procs和 methods
original from: understanding-ruby-blocks-procs-and-methods
author: eliben AT gmail.com
译者:g.zhen.ning
译注:此文章已得到eliben的同意翻译为中文,如果各位认位此文章有用,可以转载,译文的地址给不给出没所谓,但希望你能把原文的链接给出。
PS:此文我没校对,所以错译可能挺多,欢迎捉虫并进行修改。
简介
Rub借鉴了动态函数式编程的特性,如:闭包(closures)、高阶函数(high-order functions)、第一类函数(first-class functions),借此给程序员提供了一整套强大的特性。这些特性通过Ruby的代码块(code blocks)来实现。Proc对象和方法(都是对象—概念非常相近但有丝许差别。其实我对这个概念也感到非常困惑,极其难理解block,proc和method的分别,也不知道在哪种情况下用哪个来处理的效果比较好。另外,我虽也使用过Lisp和拥有多年Perl开发经验,但我不肯定Ruby的概念和其它编程语言的类似术语有什么关系,如Lisp的functions和Perl的subroutines。我阅读了大量新闻组的贴子后发现很多人都有这疑问,并且相当一部分新手被此折磨着。
在这篇文章中,我将展开我理解这些Ruby问题之旅,其中我参考了一些Ruby的书藉、文档和网站(comp.lang.ruby),我真诚地希望这篇文章能帮助到你。
Procs
我从Ruby文档中窥看到Procs被定义为:Proc对象是一组绑定了本地变量的代码块,,一旦绑定了,这代码可以在不同的上下文仍能访问这些变量。
一个实用的例子:
def gen_times(factor)
return Proc.new {|n| n*factor }
end
times3 = gen_times(3)
times5 = gen_times(5)
times3.call(12) #=> 36
times5.call(5) #=> 25
times3.call(times5.call(4)) #=> 60
Procs在Ruby里担任函数的角色。更准确的命名是函数对象,因为所有东西在Ruby里都是对象。如对象俗称-functors。Functor被定义为对象去invoked或者called,如果它是普通函数,通常这伴有相同的句法,这就是Proc。
从例子和上面的定义,显然Ruby的Procs可以作为closures。在Wikipedia,closure被定义为提交到词汇上正文变量的函数(function that refers to free variables in its lexical context)。注意它和Ruby定义的绑定到一组本地变量的代码块有多接近。
再论Procs
Procs在Ruby是first-class objects,因为他们可以在运行期(runtime)创建,储存在数据结构里,作为能数传递到其它函数并且作为其它函数的值返回。实际上,gen_times例子证明所有的这些例子,除了“作为参数传递到其它函数”,这个问题可以如下表述:
def foo (a, b)
a.call(b)
end
putser = Proc.new {|x| puts x}
foo(putser, 34)
这是一个创建Proc的简化符号-内核(kernel)方法lambda(不久将变为methods,但现在假设这kernel方法是全局函数的同族,这能在代码的任何地方被调用)[we’ll come to methods shortly, but for now assume that a Kernel method is something akin to a global function, which can be called from anywhere in the code]。使用lambda,之前例子的Proc对象创建能被重写为:
putser = lambda {|x| puts x}
实际上,lambda和Proc.new有两个微小分别。1、参数的检查,Ruby文档关于lambda的描述:除了当调用时Proc对象检查传递参数的个数。这个例子论证这一点:
lamb = lambda {|x, y| puts x + y}
pnew = Proc.new {|x, y| puts x + y}
# works fine, printing 6
pnew.call(2, 4, 11)
# throws an ArgumentError
lamb.call(2, 4, 11)
2、返回值的不同。Proc.new的返回值从封装(enclosing)方法(就如在block里返回一样,下面有详细的对比:)
def try_ret_procnew
ret = Proc.new { return "Baaam" }
ret.call
"This is not reached"
end
# prints "Baaam"
puts try_ret_procnew
当从lambda返回时更循从惯例,返回它的调用者。
def try_ret_lambda
ret = lambda { return "Baaam" }
ret.call
"This is printed"
end
# prints "This is printed"
puts try_ret_lambda
因此,我建议使用lambda来代替Proc.new,除非后者的行为严格要求(unless the behavior of the latter is strictly required)。两个参数的少一点意外(addition to being way cooler a whopping two characters shorter, its behavior is less surprising).
Methods
简单输出,method也是块代码,然后,它不像Procs,方法没有绑定到一组本地变量。正因为这样,他们绑定到一些对象并且可以访问它的实例变量。
class Boogy
def initialize
@dix = 15
end
def arbo
puts "#{@dix} ha\n"
end
end
# initializes an instance of Boogy
b = Boogy.new
# prints "15 ha"
b.arbo
有用的习惯用法是在方法发送消息。(A useful idiom when thinking about methods is sending messages.)。给接收者-拥有一些方法定义的对象,我们可以向它发送消息-通过调用这方法,是否有能数是可选的。在上面的例子,调用arbo是类似通过不带参数发送消息“arbo”。Ruby支持更直接发送习惯用法消息,通过包含send method在类object(object是Ruby类的父类)。所以下面这行代码都是arbo方法的调用:
# method/message name is given as a string
b.send("arbo")
# method/message name is given as a symbol
b.send(:arbo)
注意这方法能定义在“顶层”(top-level)区域,没有在任何类中。例子如下:
def say (something)
puts something
end
say "Hello"
这看来是“free-standing”,但不是。当方法如此被定义时,Ruby暗地里把它们挤进到Ojbect类中。但真的不要紧。and for all practical purposes say can be seen as an independent method. Which is,顺便说一下,一些编程语言(像C和Perl)他们称这为”function”。下面的Proc是,在许多地方有类似之处。
say = lambda {|something| puts something}
say.call("Hello")
# same effect
say["Hello"]
[]结构是Proc的上下文的同义字(The [] construct is a synonym to call in the context of Proc)
Methods,然而,它比procs更通用,支持更多重要Ruby特性,这我将在解释完什么是Block之后来阐述。
Blocks
Blocks和Procs的关系如此接近以致于许多新手为弄清他们的真正区别而头痛不已。我现在尝试用一些比喻来理解它(希望我的比喻不是那么差吧)。我感觉Block是未诞生的Procs(unborn Procs)。Blocks是其幼虫,Procs是成型后的昆虫。Block不是依靠自身生存-它准备的的代码是为了当它真正存活时而设的,并且只有当它绑定并且转换成Proc时,它才存活:
# a naked block can't live in Ruby
# this is a compilation error !
{puts "hello"}
# now it's alive, having been converted
# to a Proc !
pr = lambda {puts "hello"}
pr.call
就这样,这就是谜团所在,然后呢?不,不全是这样。Ruby的设计者,Matz留意到当传递Procs到methods(传递到其它Procs)很不错并且允许高阶函数和所有奇异功能的东西(stuff),有一个普遍案例比其他案例要重要—-传递一个单独的代码块到一个方法,这方法能从中做出有用的东西,例如迭代(iteration)。并且作为一个富有才干的设计者,Matz认为值得花时间去使这个特别案例(special case)变得更简单,更高效。
Passing a block to a method
无需怀疑的哪些至少花费过数小时在Ruby的程序员已经写出下面这些Ruby荣耀的例子(或者非常类似的代码):
10.times do |i|
print "#{i} "
end
numbers = [1, 2, 5, 6, 9, 21]
numbers.each do |x|
puts “#{x} is ” + (x >= 3 ? “many” : “few”)
end
squares = numbers.map {|x| x * x}
(注意:do |x| … end和{ |x| …} )
这些代码就是IMHO,Ruby的整洁、便于阅读、神奇、部分原因就在于此。幕后的情形非常简单,或者绝对可以用这种简单的方式来说明。或许Ruby并没有以我所描述的方式来实现,因为还存在其它有效的优化措施,但是这样的比方显然足够接近事实真相。
只要method调用时附加了block,Ruby会自动把此block转换为一个没有明确命名的Proc对象。不过,此method可以通过yield语句来访问该Proc对象。看下面这个例子来说明这一切。
def do_twice
yield
yield
end
do_twice {puts "Hola"}
这方法do_twice定义并且附加block调用。尽管这方法没有明确请求block到参数列中,但yield能调用此block。这也可以必为更明确的方法,使用Proc参数:
def do_twice(what)
what.call
what.call
end
do_twice lambda {puts "Hola"}
这和先前的例子是等同的,但使用yield来调用blocks更简洁并且当只有block传递到block是最优化的,想要明确的话,使用Proc的方法吧。下面这例子就是用参数来传递的:
def do_twice(what1, what2, what3)
2.times do
what1.call
what2.call
what3.call
end
end
do_twice( lambda {print "Hola, "},
lambda {print "querido "},
lambda {print "amigo\n"})
这对那些讨厌通过block传递,而用Procs来代替的朋友来说是很重要的例子。这基本原理:block参数是固定的,它会遍历(look through)整个方法来查看是否有calls在这里调用,当Proc明确立刻在参数列里发现。虽然这只是个人品味取向,但明白这二者的方法是有必要的。
&符号的使用 Read more »
用ruby完成c程序设计书的习题
最大公约数和最小公倍数
m对n求余为a, 若a不等于0
则 m <- n, n <- a, 继续求余
否则 n 为最大公约数
最小公倍数 = 两个数的积 / 最大公约数
a=gets.to_i
b=gets.to_i
max=a*b
if a
b,a=a,b
end
re=a%b #remainder
while re!=0
a,b=b,re
re=a%b
end
print “Greatest common divisor: “,b,”\n”,”lease common multiple: “,max/b
水仙花数
for number in 100..999
hundred=number/100
ten=(number-hundred*100)/10
a=(number-hundred*100)%10
total=hundred**3+ten**3+a**3
if number==total
print "#{number} is right\n"
end
end
一个球从
x=100.0
y=0.0
10.times do
y+=x/2+x
x=x/2
end
puts y,x
猴子吃桃
=begin
# recursion
def monkey(n)
if n==1
c=1
else
c=(monkey(n-1)+1)*2
end
c
end
puts monkey(10)
=end
x=1
10.times do
puts x
x=(x+1)*2
end
求素数
for m in 1..300000
k=Math.sqrt(m)
is=true
for i in 2..k
if m%i==0
is=false
break
end
end
if is
print m," "
end
end
