锁定老帖子 主题:Ruby惯用法
该帖已经被评为精华帖
|
|
---|---|
作者 | 正文 |
发表时间:2006-09-19
迭代 一般写法: 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核心类的扩展,是个有趣而又危险的大杂烩,也许我以后会另外再专门介绍一下。 声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |
发表时间:2006-09-19
这么全啊。inject我看了几次没理解,干脆不看了。
ruby函数调用可以省括号,方便是方便,不过一些FP用法也看不见了,这方面python还比较好。 从facet可以看出来,ruby里面使劲往一个类里塞东西还是有传统的。 |
|
返回顶楼 | |
发表时间:2006-09-19
第一份收藏品。学习中。受益匪浅。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 没看懂,重点学习。 |
|
返回顶楼 | |
发表时间:2006-09-19
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……后面这句是废话。 源文件管理的惯用法如下 # to import buildscript/javascripttest.rb ... $:.unshift('buildscript') require 'javascripttest' |
|
返回顶楼 | |
发表时间:2006-09-19
loop的写法有多种,
1.upto(10) {...} 10.downto(1) {...} 1.step(10, 2) {...} |
|
返回顶楼 | |
发表时间:2006-09-19
gigix 写道 inject就是reduce,map就是map……后面这句是废话。 源文件管理的惯用法如下 [code=Ruby] # to import buildscript/javascripttest.rb ... $:.unshift('buildscript') require 'javascripttest' 这个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') 的具体说明。 |
|
返回顶楼 | |
发表时间:2006-09-19
buaawhl 写道 搜索了一下,ruby unshift,结果出来很多 array unshift 的结果。 到底是没有找到 $:.unshift('buildscript') 的具体说明。 这个不就是把buildscript加到$:路径里吗 |
|
返回顶楼 | |
发表时间:2006-09-19
(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进行再解析了 |
|
返回顶楼 | |
发表时间:2006-09-19
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] |
|
返回顶楼 | |
发表时间:2006-09-19
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." |
|
返回顶楼 | |