天天看點

ruby 2.x.x之新特性散談

    ruby入門看的是經典的《ruby程式設計語言》,可是那描述的是v1.9的老版本啊!下面聊一下ruby2.x.x的新特性,x是0-n都有可能啊。

1.關鍵字參數(keyword arguments)

在1.9的時候為了模拟這個功能,我們需要傳遞散列:

2.1.3 :044 > def foo(n,others)

2.1.3 :045?>   puts n

2.1.3 :046?>   puts others[:name]

2.1.3 :047?>   puts others[:age]

2.1.3 :048?>   end

 => :foo 

2.1.3 :049 > foo(11,name:"ks",age:11)

11

ks

但是如果要有預設值怎麼辦?新的關鍵字參數特性正好滿足這些功能:

2.1.3 :050 > def foo(n,name:"noname",age:11)

2.1.3 :051?>   puts [n,name,age]

2.1.3 :052?>   end

2.1.3 :053 > foo 100

100

noname

對于不帶預設值的參數如果在調用時省略會抛出異常的:

2.1.3 :054 > def foo(n,name:"noname",age:)

2.1.3 :055?>   puts [n,name,age]

2.1.3 :056?>   end

2.1.3 :057 > foo 11

argumenterror: missing keyword: age

from (irb):57

from /users/apple/.rvm/rubies/ruby-2.1.3/bin/irb:11:in `<main>'

2.字元串的#freeze優化

ruby中的字元串總是易變的,就算是内容相同的字元串,這在大量調用時會産生記憶體的浪費。不過在使用freeze後,會在當機字元串表中查找字元串,導緻内容相同的字元串會重複利用:

2.1.3 :058 > def rept

2.1.3 :059?>   "hello"

2.1.3 :060?>   end

 => :rept 

2.1.3 :061 > 3.times {puts rept.object_id}

70098312821560

70098312821460

70098312821400

 => 3 

2.1.3 :062 > def rept;"hello".freeze end

2.1.3 :063 > 3.times {puts rept.object_id}

70098312835340

3.def傳回方法的名字作為标示符

以前的def定義最後傳回的是nil,現在是傳回方法名字:

2.1.3 :064 > x=def foo; end

2.1.3 :065 > x

順面把原來的示例代碼貼出來解釋下:

<code>module</code> <code>around</code>

<code>  </code><code>def</code> <code>around(method)</code>

<code>    </code><code>prepend(</code><code>module</code><code>.</code><code>new</code> <code>do</code>

<code>      </code><code>define_method</code><code>(method) </code><code>do</code> <code>|*args, &amp;block|</code>

<code>        </code><code>send(:</code><code>"before_#{method}"</code><code>) </code><code>if</code> <code>respond_to?(:</code><code>"before_#{method}"</code><code>, </code><code>true</code><code>)</code>

<code>        </code><code>result = </code><code>super</code><code>(*args, &amp;block)</code>

<code>        </code><code>send(:</code><code>"after_#{method}"</code><code>) </code><code>if</code> <code>respond_to?(:</code><code>"after_#{method}"</code><code>, </code><code>true</code><code>)</code>

<code>        </code><code>result</code>

<code>      </code><code>end</code>

<code>    </code><code>end</code><code>)</code>

<code>    </code><code>method</code>

<code>  </code><code>end</code>

<code>end</code>

<code>class</code> <code>example</code>

<code>  </code><code>extend around</code>

<code>  </code><code>around </code><code>def</code> <code>call</code>

<code>    </code><code>puts </code><code>"call"</code>

<code>  </code><code>def</code> <code>before_call</code>

<code>    </code><code>puts </code><code>"before"</code>

<code>  </code><code>def</code> <code>after_call</code>

<code>    </code><code>puts </code><code>"after"</code>

<code>example.</code><code>new</code><code>.call</code>

這裡用到了module的prepend方法,和include和extend類似,prepend也是将子產品插入到類或其他子產品中,它擴充的是執行個體方法(這點和include更像),不過插入的位置不同:它會插入到被插入類的“前面”而不是“後面”,即是說,如果a中有方法foo,它被prepend到有同名方法的類b中,則a#foo會先調用。如果是include到類b中,則b#foo會先調用。

4 有理數和複數字面量

我們已經有了整數(1) 和浮點數(1.0) 字面量, 現在我們也有有理數(1r)和複數(1i)字面量了。

他們配合ruby的類型轉換機制可以輕松搞定數學操作,好比,一個數學上的有理數1/3可以在ruby中寫作1/3r。3i會生成複數0+3i。這意味着複數可以寫成标準的标準的數學符号, 2+3i 生成複數2+3i!

5 數組枚舉的#to_h方法

現在數組和任何包含枚舉的類都有#to_h方法了,so可以這樣寫代碼:

2.1.3 :067 &gt; [[1,2],[3,4]].to_h

 =&gt; {1=&gt;2, 3=&gt;4} 

2.1.3 :068 &gt; require "set"

 =&gt; true 

2.1.3 :069 &gt; set[[1,2],[3,4]].to_h

6 細粒度方法緩存

現在不會有這個問題了, ruby 2.1 使用基于類層次的方法緩存, 隻有讨論中的類和任意子類才會失效緩存。

一個已經加入到 rubyvm 類的方法會傳回一些方法緩存狀态的調試資訊。這個直接看示例代碼吧:

7 refinements

refine和using配合使用,它們可以在一個局部的作用域中擴充一個子產品或類而不污染全局空間,比如:

2.1.3 :091 &gt; module a

2.1.3 :092?&gt;   refine string do

2.1.3 :093 &gt;       def sh2

2.1.3 :094?&gt;       self*2

2.1.3 :095?&gt;       end

2.1.3 :096?&gt;     end

2.1.3 :097?&gt;   end

 =&gt; #&lt;refinement:string@a&gt; 

2.1.3 :098 &gt; module b

2.1.3 :099?&gt;   using a

2.1.3 :100?&gt;   "a".sh2

2.1.3 :101?&gt;   end

 =&gt; "aa" 

2.1.3 :102 &gt; "a".sh2

nomethoderror: undefined method `sh2' for "a":string

from (irb):102

2.1.3 :103 &gt; using a

 =&gt; main 

2.1.3 :104 &gt; "a".sh2

from (irb):104

8 string#scrub

字元串增加scrub方法用來剔除無效的字元,這個可以參考下ri的結果:

= .scrub

(from ruby site)

=== implementation from string

------------------------------------------------------------------------------

  str.scrub -&gt; new_str

  str.scrub(repl) -&gt; new_str

  str.scrub{|bytes|} -&gt; new_str

if the string is invalid byte sequence then replace invalid bytes with given

replacement character, else returns self. if block is given, replace invalid

bytes with returned value of the block.

  "abc\u3042\x81".scrub #=&gt; "abc\u3042\ufffd"

  "abc\u3042\x81".scrub("*") #=&gt; "abc\u3042*"

  "abc\u3042\xe3\x80".scrub{|bytes| '&lt;'+bytes.unpack('h*')[0]+'&gt;' } #=&gt; "abc\u3042&lt;e380&gt;"

9 $safe級别4被移除

設定$safe = 4目的是将ruby放入一個“沙箱”模型并且允許執行不受信任的代碼。 然而這并不是十分有效, 因為這需要代碼分散到整個ruby中,并且這幾乎從來沒有被用到,是以就被移除了。

2.1.3 :114 &gt; $safe=4

argumenterror: $safe=4 is obsolete

from (irb):114

10 process.clock_gettime

可以通過以上方法來通路系統的clock_gettime函數,進而擷取不同種類的時間值,比如一般系統都支援以下3個:

process::clock_realtime将傳回一個unix時間戳。這和time.now.to_f傳回值相同,但是因為它跳過建立時間執行個體,是以會更快一點。

process::clock_monotonic用途是通路一個單調時鐘,這個時鐘總是向前移動,無論系統時鐘如何調整。這對關鍵時序或者基準測試是完美的。

process::clock_process_cputime_id對基準測試是有用的,它的工作方式和單調時鐘相似,總是向前移動,隻有和另一個cpu time做參考時才有意義,但是它隻有在cpu工作的情況下,時間才向前移動。

可以檢查process常量查詢是否支援其他時鐘,比如我的mac os 上有:

2.1.3 :118 &gt; process.constants

 =&gt; [:wnohang, :wuntraced, :status, :prio_process, :prio_pgrp, :prio_user, :rlim_saved_max, :rlim_infinity, :rlim_saved_cur, :rlimit_as, :rlimit_core, :rlimit_cpu, :rlimit_data, :rlimit_fsize, :rlimit_memlock, :rlimit_nofile, :rlimit_nproc, :rlimit_rss, :rlimit_stack,

:clock_realtime, :clock_monotonic, :clock_process_cputime_id, :tms, :uid, :gid, :sys] 

使用很簡單:

2.1.3 :121 &gt; process.clock_gettime(process::clock_realtime)

 =&gt; 1417521526.4812741 

2.1.3 :122 &gt; start = process.clock_gettime(process::clock_monotonic)

 =&gt; 30244.170570458 

2.1.3 :123 &gt; sleep 1

 =&gt; 1 

2.1.3 :124 &gt; process.clock_gettime(process::clock_monotonic) - start

 =&gt; 14.592389030996856 

2.1.3 :125 &gt; start = process.clock_gettime(process::clock_process_cputime_id)

 =&gt; 0.600038 

2.1.3 :126 &gt; sleep 1

2.1.3 :127 &gt; process.clock_gettime(process::clock_process_cputime_id) - start 

 =&gt; 0.01080200000000009 

11 更新rubygems

gem install的--file(或者-g)選項不再要求依賴檔案的檔案名,它将自動檢測gemfile。如果檔案不存在,gem install将産生gemfile.lock,如果檔案存在将遵循特定的版本。

<a target="_blank" href="http://www.open-open.com/lib/view/open1401153582230.html#">?</a>

1

2

3

4

5

6

7

8

<code>$ </code><code>ls</code>

<code>gemfile</code>

<code>$ gem </code><code>install</code> <code>-g</code>

<code>fetching: going_postal-0.1.4.gem (100%)</code>

<code>installing going_postal (0.1.4)</code>

<code>gemfile.lock</code>

12 程序标題

添加新的方法process.setproctitle用于設定程序的标題,不用再設定$0。還增加了相關的方法process.argv0用于檢視$0的初始值,如果$0被設定的話。

2.1.3 :129 &gt; $0

 =&gt; "irb" 

2.1.3 :130 &gt; $0= "irb_love"

 =&gt; "irb_love" 

2.1.3 :131 &gt; $0

2.1.3 :132 &gt; process.argv0

 =&gt; "/users/apple/.rvm/rubies/ruby-2.1.3/bin/irb" 

apple@kissair: ruby_src$ps 

  pid tty           time cmd

 1497 ttys000    0:00.11 -bash

  501 ttys001    0:00.38 -bash

  595 ttys002    0:00.18 -bash

 1251 ttys002    0:00.62 irb 

  633 ttys003    0:00.17 -bash

  775 ttys004    0:00.17 -bash

  794 ttys005    0:00.16 -bash

  856 ttys006    0:00.17 -bash

apple@kissair: ruby_src$ps

 1251 ttys002    0:00.63 irb_love 

13 修複了eval作用域解析錯誤

當eval, instance_eval 或者 module_eval 解析的字元串中含有不帶參數的private,protected,public或module_function時,方法的可見作用域變成了它調用處的作用域,如下面這個例子 foo 将會是private的。

<code>class</code> <code>foo</code>

<code>  </code><code>eval </code><code>"private"</code>

<code>  </code> 

<code>  </code><code>def</code> <code>foo</code>

<code>    </code><code>"foo"</code>

這種情況已經在2.1中得到了修正。是以,在這個例子中,foo應該是public的.

14 #untrusted?現在是#tainted?的别名

之前,ruby有兩套方法來辨別/檢查對象是否是untrusted,第一套是#tainted?,#taint和#untaint,另一套是#untrusted?,#untrust, 和#trust。這些方法行為都一樣,但是分别設定了辨別,是以一個對象可能是 untrusted,但不是tainted。

這些方法已經被統一成了對單個的辨別設值或取值。#tainted?等是推薦的用法,而#untrusted?等會産生警告。

<code>string = </code><code>"foo"</code>

<code>string.untrust</code>

<code>string.tainted?   </code><code>#=&gt; true</code>

産生的警告:

<code>example.rb:</code><code>2</code><code>: warning: untrust is deprecated </code><code>and</code> <code>its behavior is same as taint</code>

不過在2.1.3上測試并沒有發現警告

15 lambda 中的return總是從lambda傳回

lambdas 不同于内部使用了return的lambda并從lambda傳回的procs/blocks,它不是封閉方法. 但是有一個例外,如果傳給某個方法的lambda帶有&amp;并且被yield調用. 這一例外目前已經被移除了。

2.1.3 :155 &gt; def call_lambda

2.1.3 :156?&gt;   yield

2.1.3 :157?&gt;   end

 =&gt; :call_lambda 

2.1.3 :158 &gt; def foo

2.1.3 :159?&gt;   call_lambda(&amp;lambda {return "from lambda"})

2.1.3 :160?&gt;   "from method"

2.1.3 :161?&gt;   end

2.1.3 :162 &gt; foo

 =&gt; "from method" 

上面的例子在ruby 2.0.0 之前的版本會傳回"from lambda"。

16 擷取網絡接口位址

目前可以通過socket.getifaddrs擷取系統的網絡接口詳細資訊。将傳回socket::ifaddr對象數組。

2.1.3 :177 &gt; info = socket.getifaddrs.find do |ifaddr|

2.1.3 :178 &gt;       (ifaddr.flags &amp; socket::iff_broadcast).nonzero? &amp;&amp;

2.1.3 :179 &gt;         ifaddr.addr.afamily == socket::af_inet

2.1.3 :180?&gt;   end

 =&gt; #&lt;socket::ifaddr en0 up,broadcast,running,notrailers,multicast,0x800 192.168.0.3 netmask=255.255.255.? (7 bytes for 16 bytes sockaddr_in) broadcast=192.168.0.255&gt; 

2.1.3 :181 &gt; info.addr.ip_address

 =&gt; "192.168.0.3" 

17 queue,sizedqueue和conditionvariable性能提升

queue,sizedqueue和conditionvariable已經在c語言中加速實作。

18 set

set 中加入了#intersect? 和 #disjoint? 方法。當接收者和參數兩者之間至少有一個共同值的時候#intersect? 會傳回true,反之,傳回false。#disjoint? 則恰恰相反,如果集合之間沒有相同的值,傳回true,反之,傳回false。

9

10

12

13

<code>require </code><code>"set"</code>

<code>a = set[</code><code>1</code><code>,</code><code>2</code><code>,</code><code>3</code><code>]</code>

<code>b = set[</code><code>3</code><code>,</code><code>4</code><code>,</code><code>5</code><code>]</code>

<code>c = set[</code><code>4</code><code>,</code><code>5</code><code>,</code><code>6</code><code>]</code>

<code>a.intersect?(b)   </code><code>#=&gt; true</code>

<code>b.intersect?(c)   </code><code>#=&gt; true</code>

<code>a.intersect?(c)   </code><code>#=&gt; false</code>

<code>a.disjoint?(b)   </code><code>#=&gt; false</code>

<code>b.disjoint?(c)   </code><code>#=&gt; false</code>

<code>a.disjoint?(c)   </code><code>#=&gt; true</code>

set的另一個主要的變化是,調用一個 set 的 #to_set 會傳回它自身,而不是它的拷貝。

<code>set = set[</code><code>"foo"</code><code>, </code><code>"bar"</code><code>, </code><code>"baz"</code><code>]</code>

<code>set.object_id          </code><code>#=&gt; 70286489985620</code>

<code>set.to_set.object_id   </code><code>#=&gt; 70286489985620</code>

<code></code>

19 #include 和 #prepend 現在是public的了

module 和 class 中的 #include 和 #prepend 方法現在是public的了

2.1.3 :202 &gt; module m

2.1.3 :203?&gt;   def sh

2.1.3 :204?&gt;     self*2

2.1.3 :205?&gt;     end

2.1.3 :206?&gt;   end

 =&gt; :sh 

2.1.3 :207 &gt; string.include m

 =&gt; string 

2.1.3 :208 &gt; "a".sh

2.1.3 :209 &gt; numeric.include m

 =&gt; numeric 

2.1.3 :210 &gt; 1.sh

 =&gt; 2 

20 module/class #singleton_class?

module 和 class 中引入了一個#singleton_class? 方法,用來傳回接收器是否是一個單類。這個不用解釋吧

<code>  </code><code>singleton_class?     </code><code>#=&gt; false</code>

<code>  </code><code>class</code> <code>&lt;&lt; </code><code>self</code>

<code>    </code><code>singleton_class?   </code><code>#=&gt; true</code>

21 method#original_name

method 和unboundmethod 中添加了一個#original_name方法,來傳回非别名。

<code>  </code><code>alias</code> <code>bar foo</code>

<code>example = example.</code><code>new</code>

<code>example.method(</code><code>:foo</code><code>).original_name            </code><code>#=&gt; :foo</code>

<code>example.method(</code><code>:bar</code><code>).original_name            </code><code>#=&gt; :foo</code>

<code>example.instance_method(</code><code>:bar</code><code>).original_name   </code><code>#=&gt; :foo</code>

22 整數/大數 #bit_length

調用integer的#bit_length方法會傳回一個代表該數二進制數的位數的數字。

2.1.3 :218 &gt; 15.bit_length

 =&gt; 4 

2.1.3 :219 &gt; 12**12.bit_length

 =&gt; 20736 

2.1.3 :220 &gt; 12**120.bit_length

 =&gt; 35831808 

2.1.3 :221 &gt; 12**1200.bit_length

 =&gt; 743008370688 

23 pack/unpack 本機位元組存儲次序 long long

array#pack和string#unpack中添加了使用q_/q!和_/q!指令來處理本機位元組存儲次序的能力.

<code># output may differ depending on the endianness of your system</code>

<code>unsigned_long_long_max = [</code><code>2</code><code>**</code><code>64</code> <code>- </code><code>1</code><code>].pack(</code><code>"q!"</code><code>)   </code><code>#=&gt; "\xff\xff\xff\xff\xff\xff\xff\xff"</code>

<code>signed_long_long_min = [-</code><code>2</code><code>**</code><code>63</code><code>].pack(</code><code>"q!"</code><code>)        </code><code>#=&gt; "\x00\x00\x00\x00\x00\x00\x00\x80"</code>

<code>signed_long_long_max = [</code><code>2</code><code>**</code><code>63</code> <code>- </code><code>1</code><code>].pack(</code><code>"q!"</code><code>)     </code><code>#=&gt; "\xff\xff\xff\xff\xff\xff\xff\x7f"</code>

<code>unsigned_long_long_max.unpack(</code><code>"q!"</code><code>)   </code><code>#=&gt; 18446744073709551615</code>

<code>signed_long_long_min.unpack(</code><code>"q!"</code><code>)     </code><code>#=&gt; -9223372036854775808</code>

<code>signed_long_long_max.unpack(</code><code>"q!"</code><code>)     </code><code>#=&gt; 9223372036854775807</code>

24  tempfile.create

tempfile 現在有了一個類似與new的create方法,不同的是,他并不是傳回一個當對象被回收後使用finaliser來清理檔案的tempfile執行個體,而是得到一個塊内的普通檔案對象,并在塊結束時清理該檔案。

2.1.3 :225 &gt; require 'tempfile'

2.1.3 :226 &gt; tempfile.create("foo") do |f|

2.1.3 :227 &gt;     puts f.path

2.1.3 :228?&gt;   end

/var/folders/z2/n3vz292s0z7f995w0_bphm780000gn/t/foo20141202-1251-1hghnr0

 =&gt; nil 

2.1.3 :229 &gt; file.exist? "/var/folders/z2/n3vz292s0z7f995w0_bphm780000gn/t/foo20141202-1251-1hghnr0

2.1.3 :230"&gt; "

 =&gt; false 

25 curses 庫被移除了

curses 已經被從标準庫中移除了,現在需要單獨下載下傳gem了。