October 25,2006

[程式] Ruby 實用小技巧

Ruby On Rails 中文社區論壇 看到的 Ruby慣用法

大陸那邊的資源比我們多好多阿!!

Ruby有不少慣用法,這裏略作一些介紹,也方便閱讀他人代碼:

1.
一般寫法:
代碼:
for i in (1..10)
puts i
end

習慣寫法:
代碼:
(1..10).each{|i| puts i}

1.upto(10){|i| puts i} # from njmzhang


2.
一般寫法:
代碼:
number = 1 if number.nil?
number = 1 unless number

習慣寫法:
代碼:
number ||= 1


3.
代碼:
if __FILE__ == $0

if $PROGRAM_NAME == __FILE__

這個相當於main(), 邏輯判斷的意思當程式名($0或另一個)和當前檔案名(__FILE__)一致時,也就是當前檔是被單獨執行的而不是被別的檔調用。這個方法還有個用法是作為unit test使用。


4.
類似$0的Perl風格預設常量還有很多,參見Programming Ruby p319
其中比較常用的如$:代表庫搜索路徑,修改方法常見有:
代碼:

$:.unshift('buildscript') # from gigix

$: << File.join(File.dirname(__FILE__), 'CurrentClass')

後一種方法使用了相對路徑,因為Ruby的module不要求namespace和檔目錄結構要對應一致,很多時候統統放一個目錄裏


5.
一般寫法:
代碼:
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


6.
這個很多人都知道了,比如:
代碼:
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


7.
一般來說*用於把一個array展開:
代碼:
a, *b = [1,2,3] #a = 1, b = [2,3]

類似FP裏的x s(haskell), x: s(ocaml), [a | b] (erlang from 布娃娃)


8.
代碼:
begin
1/0
rescue
puts 'wrong'
end

可以簡化為
代碼:
1/0 rescue puts 'wrong'


9.
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



10.
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



11.
一般寫法:
代碼:
(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‘


Posted by monkuo at 樂多Roodo! │13:44 │回應(0)引用(0)程式設計 - Ruby
樂多分類:網路/3C 共同主題:程式設計 工具:編輯本文
Ads by Roodo! 

引用URL

http://cgi.blog.roodo.com/trackback/2364093