Ruby惯用法

来源:百度文库 编辑:神马文学网 时间:2024/04/29 23:52:37
您还没有登录 !  登录 ·页面登录 .注册 .企业注册 |帮助

   首页     |   论坛   Java  Ruby  AJAX  Agile   |  文集  专栏  博客  圈子   |  招聘  服务 
论坛 ->Ruby -> Ruby惯用法
全部    ruby    rails    rake    DSL    RJS    YAML

精华:   主题:   Ruby惯用法
该帖已经被评为精华帖
作者 正文
cookoo
等级:

性别:
文章: 748
积分: 1883
来自: Montreal
圈子:猪宝斋动漫分号

时间: 2006-09-19 20:41    关键字:   ruby ruby idiom 惯用法 best-practice 引用 推荐 收藏
Ruby有不少惯用法,这里略作一些介绍,也方便阅读他人代码:
迭代
一般写法:
代码
for i in (1..10)
puts i
end
 
习惯写法:
代码
(1..10).each{|i| puts i}

1.upto(10){|i| puts i} # from njmzhang
 
||=赋值
一般写法:
代码
number = 1 if number.nil?
number = 1 unless number
习惯写法:
代码
number ||= 1
 
程序入口
代码
if __FILE__ == $0

if $PROGRAM_NAME == __FILE__
这个相当于main(), 逻辑判断的意思当程序名($0或另一个)和当前文件名(__FILE__)一致时,也就是当前文件是被单独执行的而不是被别的文件调用。这个方法还有个用法是作为unit test使用。
 
预设变量和特殊记号
类似$0的Perl风格预设常量还有很多,参见Programming Ruby p319
其中比较常用的如$:代表库搜索路径,修改方法常见有:
代码
$:.unshift(‘buildscript‘)  # from gigix

$: << File.join(File.dirname(__FILE__), ‘CurrentClass‘)
后一种方法使用了相对路径,因为Ruby的module不要求namespace和文件目录结构要对应一致,很多时候统统放一个目录里
 
%w格式化命令(from qiezi) 可以少打几个引号
代码
%w{a b c d} #等价 [‘a‘, ‘b‘, ‘c‘, ‘d‘]
 
``(~键下的撇号)用来执行shell外部命令,如:
代码
`help`
 
inject
一般写法:
代码
result = []
(1..10).each{|item| result << item}
习惯写法:
代码
(1..10).inject([]){|array, item| array << item}
inject有点难理解,相当于python的reduce和一些FP里的fold。inject的块变量有两个(这里是array和item),第二个变量(item)用来枚举被inject的集合(这里是(1..10)这个range), 而第一个变量(array)由inject的参数初始化(这里是[],可选),并在block被反复执行时保持持久(相当于静态变量),而item则在每次枚举时被更新为下一个值。我们再看一下inject的另一种通常用法就会更明白了:求和
代码
(1..10).inject{|sum, item| sum += item}
这个等于
(1..10).inject(0){|sum, item| sum += item}
也就是块变量sum被初始化成0然后反复迭代执行块的内容,最后返回sum
 
并行赋值
这个很多人都知道了,比如:
代码
a,b = 0, 1
a,b = b, a # 交换a,b
当然可以延伸出一些很诡异的变化,不提倡使用阿
 
还有一个用法是让函数返回“多个结果”(不是多个对象),如:
代码
def test
1,2
end
x, y = test #x = 1, y = 2
这个njmzhang说的很对,其实函数返回的是一个array,然后再并行匹配到变量上去。(所以我对多个结果特别加了引号)
这显然是个syntax sugar,你随便用逗号分割几个变量是不会自动组成array的。
 
注意这种并行匹配当两遍不平衡时会造成的问题:
代码
a,b = [1,2,3] # a = 1, b = 2, 3被丢弃
a,b,c = [1,2] # a = 1, b = 2, c = nil 被初始化成nil
 
*的匹配
一般来说*用于把一个array展开:
代码
a, *b = [1,2,3]  #a = 1, b = [2,3]
类似FP里的x:xs(haskell), x::xs(ocaml), [a | b] (erlang from 布娃娃)
 
rescue简单用法
代码
begin
1/0
rescue
puts ‘wrong‘
end
可以简化为
代码
1/0 rescue puts ‘wrong‘
 
命名参数的默认值
ruby有默认参数,但其实没有所谓keyword argument,而是提供一个syntax sugar用hash模拟。但是怎么像Rails的方法那样同时利用命名参数和默认参数值呢?
代码
def image(opt={})
default_opt = {:height => 25, :width => 10}
default_opt.merge! opt #opt中同样key的内容会覆盖default_opt中key的value
end
 
精细duck typing控制
duck typing的精神就是行为决定类型,而不是相反
代码
a = []
#不用
if a.kind_of? Array then a << 1
if a.instance_of? Array then a << 1
#而用
if a.respond_to? :<< then a << 1
 
获取metaclass
这也比较常见了,各种动态伎俩的开始
代码
sing = class << self; self; end
 
符号转换到Proc
一般写法:
代码
(1..10).map{|item| item.succ}
习惯写法:
代码
(1..10).map(&:succ)
map(fun(x))般的FP风格
 
注意这是Rails特有的,通过ActiveSupport对Symbol插入to_proc方法。
不用Rails怎么办呢?一种办法是借助Ruby Facets库(gem install facets):
代码
require ‘facet/symbol/to_proc‘
 
Facets库包括大量对Ruby核心类的扩展,是个有趣而又危险的大杂烩,也许我以后会另外再专门介绍一下。
[code]
声明:JavaEye文章版权属于作者,受法律保护。没有作者书面许可不得转载。若作者同意转载,必须以超链接形式标明文章原始出处和作者。
相关文章:   关于RoR无法成为企业应用开发的主流的讨论   ruby on rails为什么暂时无法成为企业应用开发的主流?
返回顶端  1楼   最后更新:2006-10-21 10:22       加入在Ruby & Rails 专栏 专栏

qiezi
等级:

性别:
文章: 599
积分: 700
圈子:D语言
专栏:D语言专栏

时间: 2006-09-19 20:54    评级:   (0位会员评分) 引用 推荐 收藏
这么全啊。inject我看了几次没理解,干脆不看了。
ruby函数调用可以省括号,方便是方便,不过一些FP用法也看不见了,这方面python还比较好。
从facet可以看出来,ruby里面使劲往一个类里塞东西还是有传统的。
返回顶端  2楼   最后更新:2006-09-19 21:28   我来评分:  请先 登录

buaawhl
等级:

性别:
文章: 1845
积分: 4644
圈子:分享生活,分享乐趣

时间: 2006-09-19 20:55    评级:   (0位会员评分) 引用 推荐 收藏
第一份收藏品。学习中。受益匪浅。great thanks。
另外,源文件/目录/namespace 管理、引入等有什么best practice?
我知道一个require,load 啥的。
还看到有些人实现了ruby import module。
另外,一点疑惑。
引用
(1..10).inject([]){|array, item| array << item}
inject有点难理解,相当于python的reduce和一些FP里的fold。inject的块变量有两个(这里是array和 item),第二个变量(item)用来枚举被inject的集合(这里是(1..10)这个range), 而第一个变量(array)由inject的参数初始化(这里是[],可选),并在block被反复执行时保持持久(相当于静态变量),而item则在每次枚举时被更新为下一个值。是不是越看越晕呼呢?哈哈哈
 
这个地方虽然没有太看懂,但是感觉和List Comprehension有些像。因为这里产生的结果是一个Array ( like List)。
而Reduce, Fold的结果一般是积累成一个总值。比如,sum。
当然,这只是我的印象。我对ruby inject, Reduce/Fold, List Comprehension了解都不深。
提出疑惑,盼高手解惑。
再看一眼,仿佛这Ruby Inject很符合Reduce/Fold的形式,都是把当前结果和下一个元素作为两个参数继续向下执行。
respond to 确实是个 best practice。
引用
命名参数的默认值
ruby其实没有所谓keyword argument,而是提供一个syntax sugar用hash模拟。但是怎么像Rails的方法那样同时利用命名参数和默认参数值呢?
 
def image(opt={})
default_opt = {:height => 25, :width => 10}
default_opt.merge! opt
end
 
这个解答了我看RoR book 时候的疑惑,keyword argument 果然是 hash 模拟的。重点学习。
引用
获取metaclass
这也比较常见了,各种动态伎俩的开始
sing = class << self; self; end
 
没看懂,重点学习。
 
返回顶端  3楼   最后更新:2006-09-19 21:09   我来评分:  请先 登录

gigix
等级:资深会员

性别:
文章: 1477
积分: 2649
来自: 北京

时间: 2006-09-19 21:23    评级:   (0位会员评分) 引用 推荐 收藏
buaawhl 写道
第一份收藏品。学习中。受益匪浅。great thanks。
另外,源文件/目录/namespace 管理、引入等有什么best practice?
我知道一个require,load 啥的。
还看到有些人实现了ruby import module。
 
另外,一点疑惑。
引用
(1..10).inject([]){|array, item| array << item}
inject有点难理解,相当于python的reduce和一些FP里的fold。inject的块变量有两个(这里是array和 item),第二个变量(item)用来枚举被inject的集合(这里是(1..10)这个range), 而第一个变量(array)由inject的参数初始化(这里是[],可选),并在block被反复执行时保持持久(相当于静态变量),而item则在每次枚举时被更新为下一个值。是不是越看越晕呼呢?哈哈哈
 
这个地方虽然没有太看懂,但是感觉和List Comprehension有些像。因为这里产生的结果是一个Array ( like List)。
而Reduce, Fold的结果一般是积累成一个总值。比如,sum。
当然,这只是我的印象。我对ruby inject, Reduce/Fold, List Comprehension了解都不深。
提出疑惑,盼高手解惑。
再看一眼,仿佛这Ruby Inject很符合Reduce/Fold的形式,都是把当前结果和下一个元素作为两个参数继续向下执行。
respond to 确实是个 best practice。
引用
命名参数的默认值
ruby其实没有所谓keyword argument,而是提供一个syntax sugar用hash模拟。但是怎么像Rails的方法那样同时利用命名参数和默认参数值呢?
 
def image(opt={})
default_opt = {:height => 25, :width => 10}
default_opt.merge! opt
end
 
这个解答了我看RoR book 时候的疑惑,keyword argument 果然是 hash 模拟的。重点学习。
引用
获取metaclass
这也比较常见了,各种动态伎俩的开始
sing = class << self; self; end
 
没看懂,重点学习。
 
 
inject就是reduce,map就是map……后面这句是废话。
源文件管理的惯用法如下
ruby 代码
# to import buildscript/javascripttest.rb ...
$:.unshift(‘buildscript‘)
require ‘javascripttest‘
 
返回顶端  4楼   最后更新:2006-09-19 21:35   我来评分:  请先 登录

njmzhang
等级: 初级会员

文章: 57
积分: 94

时间: 2006-09-19 21:30    评级:   (0位会员评分) 引用 推荐 收藏
loop的写法有多种,
1.upto(10) {...}
10.downto(1) {...}
1.step(10, 2) {...}
返回顶端  5楼   最后更新:2006-09-19 21:30   我来评分:  请先 登录

buaawhl
等级:

性别:
文章: 1845
积分: 4644
圈子:分享生活,分享乐趣

时间: 2006-09-19 21:42    评级:   (0位会员评分) 引用 推荐 收藏
gigix 写道
inject就是reduce,map就是map……后面这句是废话。
源文件管理的惯用法如下
[code=Ruby]
# to import buildscript/javascripttest.rb ...
$:.unshift(‘buildscript‘)
require ‘javascripttest‘
[/code]
 
这个unshift不错。
有点类似于Python的
from Sound.Effects import echo
不过,Python还可以直接 import 到 Function 级别 (对应Java的Class Static Method? )
from Sound.Effects.echo import echofilter
搜索了一下,ruby unshift,结果出来很多 array unshift 的结果。
到底是没有找到 $:.unshift(‘buildscript‘) 的具体说明。
返回顶端  6楼   最后更新:2006-09-19 21:42   我来评分:  请先 登录

njmzhang
等级: 初级会员

文章: 57
积分: 94

时间: 2006-09-19 21:48    评级:   (0位会员评分) 引用 推荐 收藏
buaawhl 写道
搜索了一下,ruby unshift,结果出来很多 array unshift 的结果。
到底是没有找到 $:.unshift(‘buildscript‘) 的具体说明。
 
这个不就是把buildscript加到$:路径里吗
返回顶端  7楼   最后更新:2006-09-19 21:48   我来评分:  请先 登录

firebody
等级:

性别:
文章: 797
积分: 2741
圈子:美女PP&&PLMM

时间: 2006-09-19 22:18    评级:   (1位会员评分) 引用 推荐 收藏
代码
(1..10).inject([]){|array, item| array << item}
翻译成java code:
代码
package com.redsoft.jpa.query.nativequery;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
/**
*
* @author firebody
* @since 2006-9-19 22:02:29
*
*/
public class Injecter {
Iterator iter;
public Injecter(Iterator iter) {
super();
this.iter = iter;
}
public void kickoff(Class type, InjecterCodeBlack executeBlack) {
Object instance = null;
try {
instance = type.newInstance();
} catch (InstantiationException e) {
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
while (iter.hasNext()) {
executeBlack.executeCodeBlack(instance, iter.next());
}
}
interface InjecterCodeBlack {
void executeCodeBlack(Object arg1, Object iteratedItem);
}
public static void main(String[] args) {
new Injecter(Arrays.asList(new String[] { "1", "2", "3" }).iterator())
.kickoff(ArrayList.class, new InjecterCodeBlack() {
public void executeCodeBlack(Object arg1,
Object iteratedItem) {
((List) arg1).add(iteratedItem);
System.out.println(arg1);
}
});
}
}
 
另外对于那个 函数可以返回多个值 。
我想乱弹一下,哈哈,大家别笑话我这个ruby门外汉 。
这是我和某某人乱弹经过:
引用
倪宏业 说:
def test
 
1,2
end
x, y = test #x = 1, y = 2
firebody(啊翔) 说:
感觉他名次上搞了一些技巧
firebody(啊翔) 说:
其实我认为这不是返回两个结果
倪宏业 说:
x,y=test 是赋值 ?
firebody(啊翔) 说:
对,这是一个分别赋值
倪宏业 说:
其实是这么理解的
x,y = test #;
x = 1;
y = 2;
三句?
firebody(啊翔) 说:
我分析有两种可能
firebody(啊翔) 说:
一种是 ruby本身有更进一步的分析引擎,分析出的单元执行语句就是你上面提到的
倪宏业 说:
晕#是注释吧?
firebody(啊翔) 说:
晕倒,当然了
倪宏业 说:
我还当成一个交换符,哈哈。。。
firebody(啊翔) 说:
一种是 ruby本身对这种并行负值作了特殊的支持 ,
firebody(啊翔) 说:
比如 x , y 是一个特殊对象,表示 一个变量集合域
firebody(啊翔) 说:
1,2表示一个对象,是一个值域
firebody(啊翔) 说:
x,y = 1,2
firebody(啊翔) 说:
两边是相同的对象类型(域类型)
firebody(啊翔) 说:
然后=在这个类型上作了特殊的定义
firebody(啊翔) 说:
可能就分别赋值得语义
firebody(啊翔) 说:
以为ruby可以把任意的对象从新组合成一个新的对象
firebody(啊翔) 说:
在这个新的对象上执行新的操作,符号赋写就被赋予更特殊的含义
firebody(啊翔) 说:
所以,后面的分析 我觉得更有可能一些
firebody(啊翔) 说:
如果是 后面的那种分析的话
倪宏业 说:
感觉复杂了
firebody(啊翔) 说:
cookoo说返回两个对象,就是感觉是一种“名次的欺骗? “
firebody(啊翔) 说:
其实就是返回一个对象
firebody(啊翔) 说:
哦,也可能是我胡思乱想,哈哈
firebody(啊翔) 说:
可能就是 ruby进行再解析了
 
返回顶端  8楼   最后更新:2006-11-30 18:07   我来评分:  请先 登录

buaawhl
等级:

性别:
文章: 1845
积分: 4644
圈子:分享生活,分享乐趣

时间: 2006-09-19 22:30    评级:   (1位会员评分) 引用 推荐 收藏
a,b = 0, 1
a, *b = [1,2,3] #a = 1, b = [2,3]
这两条可以合起来看.
a,b = 0, 1
右边的 0, 1 类似于一个 tuple ( 可以理解为定长数组 )
对应到FP是,
{a, b} = {0, 1}
a, *b = [1,2,3]
对应到FP是,
[a | b] = [1, 2, 3]
 
返回顶端  9楼   最后更新:2006-09-19 23:36   我来评分:  请先 登录

njmzhang
等级: 初级会员

文章: 57
积分: 94

时间: 2006-09-19 22:42    评级:   (0位会员评分) 引用 推荐 收藏
firebody 写道
另外对于那个 函数可以返回多个值 。
 
 
 
函数不是返回多个值,
在Pickaxe里有写道:
"if you give return multiple parameters, the method returns
them in an array. You can use parallel assignment to collect this return value."
返回顶端  10楼   最后更新:2006-09-19 22:42   我来评分:  请先 登录


论坛 ->Ruby -> Ruby惯用法
跳转论坛:   Java Ruby AJAX 敏捷开发和项目管理 综合技术 招聘求职 海阔天空 入门讨论 行业解决方案
快速回复         引用上一条消息 (Alt+s)  提交

广告服务   |  JavaEye黑板报   |  网站地图   |  关于我们   |  服务条款  |  联系我们
© 2003-2007 JavaEye.com.   All rights reserved.上海炯耐计算机软件有限公司 [沪ICP备05023328号 ]