顯示具有 Ruby 標籤的文章。 顯示所有文章
顯示具有 Ruby 標籤的文章。 顯示所有文章

2009年7月22日 星期三

學程式語言的樂趣

國三時為了做北市數學科展,實習老師教我使用 Microsoft Word,這是我首次體會到電腦在遊戲之外的樂趣。為了學會怎麼灌 Windows,高一時參加了社團,結果社團學長 Scott 說:「灌 Windows 太簡單了,你來學 C 吧。不過我們這裡只有 Red Hat Linux,就邊學 C 邊學 Linux 吧。」傻傻的我,以為 Linux 裡只有 Vi,就四處翻 Linux 入門書,看完各本書裡 Vi 部份,還寫了篇 Vi 的教學。日漸熟悉 Vi、bash 指令後,覺得玩系統、寫程式真有趣。於是上課無聊就偷看電腦書,下課耍自閉繼續看書,中午放學都跑去社團混,就這樣渡過我的高一。不知不覺,我打下日後學習能力最重要的基礎。

高一一時想不開,同時學 C 和 Perl,用 Perl 寫東西頗有趣的,特別是費盡心思把程式擠到一行作完一件煩人的小工作時,有說不出來的成就感。可是我基礎太弱,無法明白兩者更深層的差異,只能當作兩個不同工具看。兩者同時學的下場,學一陣子仍不熟兩邊的語法。於是我先專心學 C,之後 Perl 也忘光了。那段日子最大的收獲是,不要一次同時學兩個語言。另外,多虧 Scott 心理不正常,用 K&R 的《C 程式語言》當入門書,我直接學會標準語法,和 C 的典型寫法。這個習慣一直伴隨我到現在,每學一個新語言時,我傾向直接找能教我典型寫法的書,省去先花時間學一堆語法,再去蕪存菁地寫出典型風格。

寫了三年的 C,大一接觸到 Java 後,覺得 OOP 很有趣,這麼簡單又優雅地,讓我能同時使用兩個 Stack!雖然一開始寫著滿滿的 C-like Java,程式裡都是 static method,看了些 OOP 的書,開始轉型寫著自以為是 OOP 的怪東西。時至今日,我仍然不明白要怎麼寫出真正的 OOP。至少我發現自己寫得不是 OOP,和大學時相比,算是進步不少。

有些課要求用 C++,我就會用 Java 學到的 OOP 觀念和 C 的經驗來寫。每次要寫 C++ 就重翻一下 C++ 快速入門的書,寫完後又忘光一切。大三做專題時,我和朋友們打算參加一個國外的比賽,由於主要贊助商是 Microsoft,我們決定用 .NET Framework 開發。就這樣,我有個好機會寫 C#。亂翻一些 ASP.NET 的書把殼刻一刻後,我找了《C# Essentials》來看。這本書如其名,薄又都是重點,很快地明白 C# 的特色,讓我覺得 C# 挺漂亮的。不過寫完專題後沒繼續寫 C#,現在什麼也不記得了。而且,當時我的底不夠深,仍無法感受到各語言的特色。

除 C++、C# 兩個插曲外,大學時主要用 Java 和 PHP。用途很簡單,接網站案子用 PHP,其它用 Java。我練習 PHP 的方式是不斷改寫自己的網站,還有接些小案子,可是我覺得 PHP 實在不怎麼美,沒動力學好它,能用就好。

大學中途有試著寫了一點 Python、Scheme,可惜無法持續。一直到碩一一時興起,一方面是厭煩了用 PHP 寫重覆的東西,另一方面想試看看當紅的 Ruby on Rails,就邊查邊寫完成一個小案子。寫得過程裡,覺得 Ruby 很奇妙,於是在寒假花了一週讀完《Programming Ruby》第一部份,這才正式開始我學的第三個語言。

自那之後,我只有寫 Ruby,Java、PHP和其它語言碰也沒碰,這樣直到碩士畢業。沒錯,我的碩士論文也是用 Ruby 寫的,這才深深體會到 Ruby 有多慢有多肥,幸好實驗室的機器很壯,不然我得改用 Java 完成碩論。這段期間,把一個論文裡用到的程式零零總總地加起來,大概有兩千多行,也驚覺 Ruby 幫我省了不少寫程式的時間,相信用 Java 的話,我無法在不到一個月的時間裡寫完自己的演算法、數個比較對象的演算法、前置處理、評估分數等程式。不過卻辛苦交接的學妹,這樣瘋狂用一個不熟語言趕出來的程式碼,原以為不會繼續用的說。

用 Ruby 讓我有了第二次衝擊,就像當年從 C 到 Java 發現可以同時有兩個 Stack 那般驚奇,沒想到 iterator、code block 這麼好用,腦裡想什麼,手就可以直接敲出來,不用寫一寫還要把游標移來移去。而且,Ruby 可以寫出很短又好讀的程式,我從來沒想過程式可以寫到這麼精簡,而寫到這麼精簡後,又更容易理解。

碩士畢業去一家公司實習時,該公司不用 Ruby,我藉機來試看看 Python。於是,對照先前一年多寫 Ruby 的經驗,我明白很多事。有時選擇某個語言,沒有什麼明確的理性依據,單純是個人偏好。高中時我一直想知道 Python 和 Perl 的差異,到底學那個比較划算,如今又多了一個 Ruby 列入抗爭。現在來看,即使我可以找到三者在語言設計、執行速度、社群、函式庫、上手度等各方面的比較,到頭來,選擇何者反而像是信仰。

附帶一提,若要推廣一個程式語言,良好的互動直譯器、快速隨手查的文件、精簡易讀的入門書,三者缺一不可。 Ruby 大概就互動直譯器弱了一些,而 Python 三者都完美無缺,非常容易上手。至於 Java,即使到現在,我仍不知道要推薦別人看什麼書入門。最後附上我對各語言入門方式的看法,入門書書單是針對已學過一種語言的人:

語言 互動式直譯器 隨手查的文件 入門書
C man page 《C 程式語言》
Ruby 設定後的 irb ri 《Programming Ruby》
Python ipython ipython 《Python Essential Reference》
Java CHM格式的 Java doc

( 待續 )

2008年7月13日 星期日

查 Python 函式的方法

學程式語言的首要問題就是如何查函式,個人認為有良好的查函式介面,才容易推廣程式語言。

像 Java 有 JavaDoc,可從 Sun 官網查詢(個人喜歡用別人包好的 Windows Help 檔);PHP 官網也有便利的查詢介面;C 在 Unix 上可用 man page(硬漢工程師才用 C,啃點 man page 不是問題!); 個人覺得Ruby的官網文件不怎麼好查,不過用 Ruby 直接查到是滿快的。比方我記得有個 p 開頭的函式可以消掉陣列最後一個元素,開個 irb(interactive interpreter)這麼打即可:

[].methods.grep /p/

查到正確名稱後,再用 ri 查詳細用法,相當順手。

Python 在這方面弱了一些,這裡整理一下我目前的做法(從 Scott 那學來的)。首先,一定要裝 ipython(功能強大的 interactive interpreter),之後就可以利用 code completion 來查名稱,例如:

n [2]: a = “”

In [3]: a.sp
a.split a.splitlines

In [3]: a.split?
Type: builtin_function_or_method
Base Class:
String Form:
Namespace: Interactive
Docstring:
S.split([sep [,maxsplit]]) -> list of strings

Return a list of the words in the string S, using sep as the
delimiter string. If maxsplit is given, at most maxsplit
splits are done. If sep is not specified or is None, any
whitespace string is a separator.

注意,直接打 “”.sp 再按 TAB 是沒用的,因為 ipython 要按下 ENTER 後才能猜測先前輸入過的變數型別為何。名稱後加「?」可以叫出 __doc__ 內的說明。

若想像 Ruby 那樣搜尋特定函式名稱,得透過 __dict__ 或直接用 built-in function dir(),__dict__ 位存有 class 內的 methods 和 fields,下面的效果和 Ruby 相同:

print [s for s in dir([]) if "p" in s]

2008年5月1日 星期四

初始化 graph 的教訓,不熟的語法別亂用

昨晚跑了個程式,今天醒來不久接到york的電話,說連swap 在內資源都被我吃光了。

這個程式有三個步驟:

  1. 從資料庫裡取出一些資料轉成 graph G(V, E)
  2. 將 G(V, E) 轉成 G’(V, E’),E’ = { (u, v) | (v, u) in E }
  3. 利用 G, G’ 算HITS

原以為是 graph 大小超出我的預料,做了一些縮減後就重跑,吃完午餐回來看,不對,怎麼又停在產生 G’ 的部份。接著在一些錯誤的地方最佳化,最後找到問題的源頭。

在初始graph時, 我以前是這麼寫的:

ur = (1..user_ids.size).map { {} }

產生一個長度為 user_ids.size 的陣列,並在每一個元素內填入一個 hash table (i.e. {}),也就是類似 adjacency list 的存法,擁有 adjacency matrix 和 adjacency list 的好處。這可是過去試了許久找到最滿意的存法,改天再補上遲遲沒寫的 graph 表示法的心得吧。

上面那段程式以前用得好好的,後來我想試試新語法,改成這麼寫:

ur = [{}] * user_ids.size

這個寫法是原自下面這個 idiom code:

array = [0] * n

一般初始陣列時可以這麼寫,會得到一個長度為 n ,初始值為 0 的陣列。

可是 [{}] * n 表示所有元素都指向同一個 hash table (這行程式只產生了一個 hash table),於是災難發生了,超大的 hash table 導至超糟的效率,更糟的是,我又用這個 graph 產生 G’,使得 G’ 變成幾乎 complete graph,然後 memory 就爆了 ( |V| 很大,但原本是 sparse graph)。

附帶一提, Ruby Cookbook 裡有提過這問題,當時有看懂,但沒完成參透啊!比方說當 hash 內元素不存在時,要自動產生一個陣列的話,標準錯誤寫法如下:

table = Hash.new([])

因為陣列只有被初始化一次,存在 table 內,當 table[key] 不存在時, 不管 key 為何,table 都會傳回那一個陣列。詳見以下的例子:

irb(main):088:0> table = Hash.new([]) irb(main):089:0> p table {} irb(main):090:0> table[0].push 5 irb(main):091:0> table[1].push 10 irb(main):092:0> p table {} irb(main):093:0> p table[2] [5, 10]

正確寫法如下:

table = Hash.new { |h,k| h[k] = [] }

這故事告訴我們,語法要學熟,不然等痛過後就會記熟了。

2008年4月13日 星期日

IR and DM algorithms codes

http://www.igvita.com/。好站一推,不止有code教學,連觀念的講解都超清楚的。比方說這兩篇:

看來以後用Ruby寫code會愈來愈方便啊!不過愈是了解Ruby運作的方式,愈是不敢對它的效率抱以期望。

2008年4月7日 星期一

module_eval: dynamic code generation

Ruby的動態性真是超乎想像的炫,先看一個簡單的例子,code改自 “10 Things Every Java Programmer Should Know About Ruby”裡的“Item #7 Ruby is Way More Dynamic Than You Expect”

irb(main):001:0> puts 3.even? NoMethodError: undefined method `even?' for 3:Fixnum from (irb):1 irb(main):002:0> 3.class.module_eval "def even?() (self & 1).zero? end" irb(main):003:0> puts 3.even? false

第二行即時塞入數字class一個method,更炫的是,你甚至不用知道數字的 class 叫做 Integer,Item #7有太多神祕的功能,目前還不能理解它們的價值有多高。

再來這個例子很實用,但比較複雜,改自《Programming Ruby 2/e》p402。有時我們想將某些方法改用lazy initialization,這些值只會算一次。一般的做法,就是另設個private field,先檢查該field是否已設值,是的話就直接傳回,否的話先算再傳回,像singleton就是一個例子。

若有10個method想改怎麼辦?若改完後有特殊需求想改回來怎麼辦?若用C/C++、Java,自然是得改source code再重編譯code。先不提程式必須停止的影響,至少要反覆改code就不太方便。Ruby的話,可以寫兩個method,一個叫 once(),一個叫 unonce(),接著要做的,就是執行 once/unonce 即可,程式碼見這裡,說明如下:

  1. once接受任意數量的參數,參數是instance method name,once會將instance method加上一個cache,使得該method只會計算一次。
  2. 所有class都是Module的subclass,如此一來,所有新舊class都多了一個method once()。
  3. 對Ruby來說,class內的code等同於「執行」,所以 line 36 的 once :r 並不是什麼神奇的語法,而是在 class T 的內部,執行 method once 和參數 :r。
  4. line 40 顯示出,method r() 已被改變了,只會算一次 4. line 43 和 line 36 執行相反的行為。
  5. line 6 ~ 10 是核心程式,line 9 的 “[0]” 是用來處理 method 傳回多個值的情況,傳回多個值時,若 ‘=’ 左方只有一個變數,傳回值會自動變成陣列。

以上的例子說明了什麼呢?

這個例子揭露了Ruby強大的動態能力,原本我們想將method改成有cache或沒cache,得修改原始碼才行,若有10個method就要改10次,而透過module_eval的技巧,只要寫一次 once(),用一行code執行一次 once(),全部搞定!程式相當地有彈性,其它類似的雜事像 setVariable/getVariable也可以透過類似的技巧解決。Ruby內建的 attr_reader/attr_writer/attr_accessor 應該是類似的產物。

其它附帶的好處是程式可以在執行中即時修改既有的任何程式碼,而不用停止程式,事先寫好一些操作的話,除ruby interpreter更新外,程式不需要停止。

2008年3月26日 星期三

Learn To Program

Learn To Program這個站真是超酷的,由一個 ruby program 寫成的 CGI,用來即時產生書的內容(見” About the Original Tutorial”),好處是確保範例code不會有錯,亂數或時間之類的還可以每次看到不同內容。更炫的是,code、code執行結果可以自動產生,有興趣可以對照看一下“Flow Control”裡Branching 開頭的code,包含source code、兩個執行結果和中間的remark,是由下面這段code產生的:

run1 = {:input => ['Chris']} run2 = {:input => ['Chewbacca'], :remark => 'But if we put in a different name...'} progN run1, run2 do <<-END_CODE puts 'Hello, what\\'s your name?' name = gets.chomp puts 'Hello, ' + name + '.' if name == 'Chris' puts 'What a lovely name!' end END_CODE end

至至於內文寫得好不好,我就沒注意啦。花點時間看這份code挺有收獲的。

2007年8月23日 星期四

試用JRuby失敗,只好用Java讀圖檔

Ruby查不到怎麼讀圖檔,只好從Java下手,原本想用jruby來搞定的,結果碰上jruby的bug,jruby還未成為完全體嗎?Java的解法見這裡,寫好的code在這裡。以下是用jruby的測試情況:

# jruby -v ruby 1.8.5 (2007-06-07 rev 3841) [amd64-jruby1.0] # cat read_bmp.rb include Java import java.awt.image.BufferedImage; import javax.imageio.ImageIO; f = File.new("test.bmp"); img = ImageIO.read(f) # jruby read_bmp.rb RubyFile(test.bmp, 0, 3) :-1: no read with arguments matching [class org.jruby.RubyFile] on object JavaUtilities (NameError)

ImageIO.read()無法讀出BufferedImage,只好重操老本行寫Java,輸出的method打成puts…,幸好Java的compiler很強,就算很久沒寫Java,想寫出能動的code還是很快,大不了多compile幾次罷了。

2007年8月16日 星期四

半年來用Ruby的觀感

原本想把《Programming Ruby》K一K後再來寫心得,轉念想想,正因為學得不夠深,心境貼近,才能寫出未入門也感興趣的心得文;另一方面,照我在技術方面待學清單來看,不知那天才會輪到讀這本書,還是先寫心得吧。

今年寒假時,受到《Beyond Java》的鼓吹,對Ruby燃起興趣,那段期間一邊學Ruby一邊學Rails,做完一個網站,賺一筆工讀費,接著在過年回家期間花了一週啃完《Programming Ruby》Part I,之後開始漫長的實戰之旅。

寒假讀了這些書後,覺得投資Ruby前景看好,依我個人寫程式習慣,可分為三類:

  1. 處理文件雜事的小程式,一般用Perl、awk、shell script等雜項工具打發。
  2. 寫個人網頁、接case,一般用PHP打發。
  3. 寫作業、做研究用途,一般用C或Java,規模小的用C,大的用Java。

但Ruby將三個願望一次滿足!Ruby身為Perl的後續者,Perl有的能力差不多都有,只是lib比不上CPAN,不過前景看好,不用擔心這個;Rails這殺手級framework不需多介紹,用PHP寫一堆重覆零亂的程式後,我已厭倦用PHP寫網頁;至於Java,唯一的不滿是笨重,在《Beyond Java》的心得裡有些討論。

這半年來我確實全都用Ruby搞定要做的事,用Ruby寫比賽用的網頁和後端程式、用Ruby抓Web data、用Ruby實作研究用的演算法、用Ruby處理文字檔。each和map愈用愈多,去掉語意不明的for loop,改用語意明確的一行code搞定,還有配合irb這個runtime interpreter,即時測試method用法,或處理小資料,相當方便。半年前投資Ruby的決定至少幫我省下一半coding時間,當然,愉快度也提高一倍以上。唯一的不滿是Ruby沒有直接支援UTF-8,相較於Python,這是很大的弱點。

接下來要找個時段好好地讀《Programming Ruby》,學習更進階的用法,發揮Ruby更大的功效。

另外,我不覺得Python不好,之前和Scott討論後,覺得兩者大同小異,只是剛好寫慣了Ruby,暫時不會嘗試Python,個人稍微嫌Python要打的code多了一點,寫起來爽快度低了一點,不過玩Python也是遲早的事,唔...還有在淹沒在待學清單裡的Haskell。

ps

用一個語言與否,個人認為主要考量是語言的特性、既有lib的質與量,以及開發社群。以C#來說,開發社群是MicroSoft,以Perl來說,是廣大的OpenSource族群,開發社群決定語言的未來。這半年來JRuby有不少消息,透過良好的商業發展,Ruby和JVM的完美結合指日可待,JRuby的完成,等於立即幫Ruby裝上數卡車的火藥,威力倍增,Java有的lib都能被Ruby使用!所以這半年來不論從那個方面來看,我對Ruby的信心又更高了。

2007年7月13日 星期五

Parse invalid HTML by Hpricot (2)

關於Hpricot的基本介紹,可以參見前篇,Hpicot的API文件可以參見這篇這篇,怪的是好像沒在官網看到連到這些API網站的鏈結,我從Google找到的。Hpicot雖然好用,但文件似乎沒有很齊,這裡簡單說明crawl web pages會用到的部份。

開檔

可以從檔案或URL開啟,用法:

require 'uri' require 'open-uri' require 'rubygems' require 'hpricot' doc = Hpricot(open('http://fcamel.twbbs.org/'))

會得到一個Hpricot::Doc物件,接著可以使用search()找出特定部份的資料,我都是用CSS path,配合Web Developer查CSS path,容易使用。

Search

example code:

doc.search("//div#sidebar.list/ul/li/a").each do |t| ... end

這個例子裡,search回傳Hpricot::Elements,Elements是類似陣列的資料結構,每個元素的型別是Hpricot::Elem。t.inner_text的型態是String,tag內的純文字;t.attributes[…]是Hash,存放各屬性的值,比方用t.attributes[’href’]取出超鏈結指向的URL。

Hpricot::Elements可以繼續search,可以先取出一大區塊的HTML後,再配合if, else來找資料:

doc.search("//div#sidebar.list/").each do |e| if e.search("//h3.label").inner_text =~ /Introduction/ # find data in <h3 class='label'>Introduction</h3> ... elsif e.search("//h3.label").inner_text =~ /Related Work/ # find data in <h3 class='label'>Related Work</h3> ... end end

2007年7月4日 星期三

在Rails裡用AJAX Observers

Rails預設即有內建prototype.js,並提供一些語法操作prototype.js,會將rails code轉為prototype的function call。參照“Agile Web Development with Rails” ch18,有用Observer實作livesearch的例子,但這份code有點小問題。

in observe.rhtml:

<label for="search">Search term:</label> <%= text_field_tag :search %> <%= observe_field(:search, :frequency => 0.5, :update => :results, :with => 'term', :url => { :action => :search }) %> <div id="results"></div>

注意observe_field()要放在追蹤的field_id之後,observe_field()放到text_field_tag()之前會失效。

in xxx_controler.rb:

def search @phrase = request.raw_post || request.query_string ... end

這段code裡的request.raw_post會從POST取得form內的資料,但如同raw_post的名稱一般,它取得的是如同GET後附上”var=value”的內容,並且,observe_field()會把追蹤的field_id內的內容取來當做key,若使用者在text field裡輸入”hello”,raw_post內的值會是”hello=”,而不是預想的”search=hello”

因此,解決方法有兩種:

  1. 修改controller:改成request.raw_post[0,request.raw_post.length-1],去掉’=’
  2. 修改view (observe.rhtml):改成

    <%= observe_field(:search, :frequency => 0.5, :update => :results, :with => 'search', :url => { :action => :search }) %>

    參照官方文件,:with會補上key值,再將追蹤的field_id內的值填入key內。

照理說,預設把追蹤的field_id和它的值設為”field_id=value”再傳給:url指定的method才合理吧?

2007年5月25日 星期五

用Ruby畫圖表

gruff會用到librmagick,librmagic會使用libmagick,畫圖要用到字型,所以一共要裝gruff、librmagick (自動裝libmagick)、字型。畫出來的圖很漂亮,預設配色就很讚了,這裡有些輸出例子。

documents頗少的,幸好用法很直覺,麻煩的是要裝libmagick,還有需要字型,之前Gnuplot也要字型,但不知怎麼搞的,雖然有錯誤訊息,還是能畫圖,但這回沒辦法,在freebsd without X的server上用gruff會有這樣的error message:

/usr/local/lib/ruby/gems/1.8/gems/gruff-0.2.8/lib/gruff/base.rb:965:in `get_type _metrics’: Can’t measure text. Are the fonts installed? Is the FreeType library installed? (RuntimeError)
from /usr/local/lib/ruby/gems/1.8/gems/gruff-0.2.8/lib/gruff/base.rb:965 :in `calculate_caps_height’

我不想裝 X ,也不清楚單獨裝字型的方法,於是跳槽到已裝有 X 的debian上用 XD 。debian上用gem安裝rmagick失敗,只好用apt-get裝,但我找不到package,後來在york協助下,才發現我搞錯名稱,是 librmagick-ruby1.8,不是libmagick-ruby1.8,我漏打一個 ‘r’ ,難怪找不到。

官網上已有畫line chart的例子,附上pie chart的例子:

require 'rubygems' require 'gruff' g = Gruff::Pie.new g.title = "My Graph" # gurff會把data內的值加起來輸出相對比例 g.data("Apples", 3) g.data("Oranges", 2) g.data("Watermelon", 4) g.data("Peaches", 1) g.write('my_fruity_graph.png')

2007年4月10日 星期二

Parse invalid HTML by Hpricot

XML parser滿天飛,但能處理不符格式的HTML parser沒幾個,Ruby Cookbook裡有一些說明,用內建的REXML parsing XML,可以用DOM,也可以用SAX,看Cookbook或google一下就有範例code。

Cookbook的《Recipe 11.5. Parsing Invalid Markup》介紹幾個lib,試用後都失敗,無法處理我遇到的invalid HTML,到del.icio.us找了一下,一堆人推Hpricot ,試用後驚為天人,處理速度快又正確,支援XPath或CSS Path的方式索引HTML,可以處理XML和HTML,真是太威了。

以下為官網的說明:

若要觀察XPath或CSS Path的話,可以用firefox的extension:Web Developer ,裝好後按個Ctrl+Shift+Y,把滑鼠移到網頁上會看到該塊的CSS Path。

2007-04-11 Updated

REXML Tutorial

2007年2月22日 星期四

目前對Ruby的觀感,以alias的功能為例

有很多特別的東西值得一提,但這些東西都太簡單太容易看到,稍微學一下Ruby就會體會到,反而覺得沒必要特意提。

很多優點要有些概念才能明白到底有多方便,舉例來說,alias可以改變method,而不用inheritance去改。原本OOPL裡會用 interface/class 先包好,在良好的設計下,可以簡單地抽換行為,比方對method做log。注意,要有良好的設計,還有隨之的規範

一個簡單的應用是,若我們能改寫輸出方法,我們就能記錄輸出訊息,方便Unit Test,而不用改寫被測試的 函式/物件。

視語言的不同,可以用巨集或wrapper object實作,這裡通稱 fake_puts。fake_puts會記錄輸出的字串,再真的呼叫 printf / cout / System.out,於是我們用的所有物件得先遵守這個設計,使用fake_puts,而不是直覺的 printf / cout / System.out。

這有很多問題:不方便、已寫好的code很難改、沒有source code無解,但Ruby可以直接改變既定的method,非常簡單

$record = [] alias old_puts puts def puts(str) $record << str old_puts str end

只要在程式裡加入上段的code,所有程式不用更動,行為照舊,而且所有用puts輸出的訊息都有存到形態為Array的全域變數$record。Ruby就是這麼簡單又強悍。

Programming Ruby

書藉基本資料:Programming Ruby

如果曾經學過其它程式語言,這本書就是最佳選擇。

作者開頭指出他很想寫本倒著寫的書,我們已學過很多東西,沒必要讓讀者耐著性子從基礎語法慢慢看。這真是太對我胃口了,但作者嘗試以後,發現沒有語法什麼也講不了,top down approach根本無法開始,於是作者決定仍用top down approach,但一邊帶出需要用到的基本語法。

全書分5部份,我目前讀完Part I — Facets of Ruby,Ruby的快速入門。一路讀下來充滿驚喜,馬上就能看到Ruby特別之處,也能立即試寫一番,省下讀重覆概念的時間。說明清楚、例子舉得好、文筆幽默,真是不可多得的好書。The Pragmatic Programmers這家公司很有趣,出版的書是名付其實的”實務”,也會提供線上觀看版本,《Programming Ruby》 1/e已免費放出,網路上有不少人做中文化的工作。我讀的是2/e,讀得過程有太多心得想寫,轉念一想,反而覺得大家自己讀較有效率,就不一一提出。

看完Part I後,Ruby給我的感覺很好,內建Profiler、Debugger、Unit Test,加上Ruby強悍的架構,讓Unit Test寫起來很輕鬆,初期引入的抵抗很小,以Java的Unit Test來說,要先找JUnit用的Jar檔,再import Jar檔,Java寫小東西很瑣碎,而Unit Test本質上就是一堆小class、小method,用Java寫起來很煩。我沒用過C的Unit Test,但沒Reflection的語言,Unit Test勢必要更多設定,連試的念頭都沒有。

這裡挑書裡幾段和技術無關的有趣內容分享:

Test::Unit comes with a number of fancy GUI test runners. As real programmers use the command line, however, these aren’t described here. Again, see the documentation for details.
Programming Ruby 2/e, ch12 

Sad to say, it is possible to write buggy programs using Ruby. Sorry about that.
Programming Ruby 2/e, ch13 

A subprocess changes an environment variable, and this change is inherited by a process that it then starts. However, the change is not visible to the original parent. (This just goes to prove that parents never really know what their children are doing.)
Programming Ruby 2/e, ch14 

2007年2月21日 星期三

Ruby + Rails + TextMate的demo影片和Ruby + VIM

眼見為憑,這裡這裡有些demo影片,似乎移動滑鼠讓人有罪惡感似的。學會這些工具後,確實如Brooks在《人月神話》裡的《再論沒有銀彈》所說一般,沒有萬能的方法或工具,但當我們把多個方法和工具合在一起時,軟體開發仍有可能達到數量級的提升。初看感到驚喜,接著讓我感到噁心,為什麼我要學這些東西?看來我有些工程師的屬性,但不適合走這條路。

看了幾個demo影片後,個人推薦“Inserting HTML Tags”,可惜只有Mac有TextMate,而且處理中文仍有問題。VIM應該有類似的補tag、補Ruby code的功能才對,不過我目前連Ruby在VIM內的indent都沒弄好,愈來愈懶得弄工作環境了。

Updated

打入def後,看到沒有end出來,忍不住找了一下VIM相關plugin。

  • Vim/Ruby Configuration Files:懶人包,把一堆plugin和doc合在一起,我沒試。
  • rubycomplete.vim :ruby omni-completion。看起來強的樣子,打Ctrl+X Ctrl+O會有選單,但要VIM7.0 + ruby interface(?),我只會在ports下打make install裝VIM,沒法試。
  • ruby-macros.vim:macros for the ruby language。至少打def後會有end了,當然if、for也有,單雙引號之類沒寫好,反而難用,我把這部份的設定註解掉。
  • rails.vim: Ruby on Rails: easy file navigation, enhanced syntax highlighting, and more。看起來頗強的,前兩項在insert mode用,這在ex mode用,用自訂的命令可以在VIM內做些shell下的事,像是rake、ri。附加些強大指令,像是extract view內幾行,另存成subview檔(_XXX.rhtml),並且附有詳細文件。但我懶得學這些指示,還是IDE較方便。

結論:至少打def後會有end了。

2007年2月18日 星期日

Ruby:max, min, substring

string methods裡沒看到substr之類的,google一下發現又是一個搞錯方向的問題,因為ruby的array operator太強了, 不需要substr。例:

“flying camel”[-5..-1] # results to “camel”

用新語言時常遇到這種事,其它語言的常見問題,在另一個世界裡完全不是問題,反而找不到解法,在那世界的人壓根就沒煩惱過這事,自然沒有文件。

找不到Math.max之類的methods也是一樣的情況,因為Math.max本身就是”不直覺”的用法。應該要這麼寫:

[a, b].max
[a, b, c, d, e].max

實務來看,得先生成array,再用array的method “max”,沒有效率,但這樣寫起來其實較合物件的想法,不過我還是覺得max(a, b, c, d)的寫法較對味。

2007年2月14日 星期三

Ruby:and, &&, or, ||的差別

有些重要觀念讀過後就懂了,也沒必要記到Wiki上,記到Wiki上被看到的機率也較低,還是寫在Blog上好了。

FAQ 6.10 What’s the difference between “or'’ and “||'’?

Q: “p(nil || “Hello”)'’ prints “Hello”, while “p(nil or “Hello”)'’ gives a parse error.

A: || combines terms within an expression. Because the first term in this case is nil, the second term is evaluated.

or is used to combine expressions in conditionals. Ruby is not expecting a conditional statement in an argument list.

結合terms時要用&&、||,結合expression時要用and、or。

2007年2月12日 星期一

Ruby Symbol (:string)

我和skylight在學Ruby時(或說用Rails時)都被冒號開頭的”字串”困惑許久,看到兩篇解釋得很清楚的文章。

2007年2月5日 星期一

Ruby on Rails + Apache2 on FreeBSD

Rails是Ruby開發Web的超強framework,簡稱為RoR,國內用RoR最有名的站是HEMiDEMi,RoR的強大之處可以參閱這則新聞:"網站開發快10倍-探索Ruby on Rails的高速魔法"

google “ruby on rails”可以找到官方網站,有WEBrick的安裝和使用教學,入門文件可以參閱:" Rolling with Ruby on Rails",相當容易,用WEBrick玩RoR後,覺得RoR很有潛力,若對Ruby有疑慮,可以改用CakePHP - 用PHP寫成的RoR。

RoR也可以搭配apache使用,文件很多,這篇是我安裝完寫的筆記:“Ruby on Rails + Apache2 on FreeBSD”

原本只是想用RoR,卻把apache2、php5、php5-externsion、phpMyAdmin、CGI、FastCGI、RoR的安裝和運作流程都摸了一下,這就是玩系統的悲哀啊。

2007年1月30日 星期二

Ruby初試心得,以統計單字數為例

Problem Definition

大致來說,就是輸入兩個檔案,一個stop word list,表示不想統計的單字;一個text file,統計的文本。任何以空白分隔的字元就是單字。從參數列讀入stop word list和text file的檔名,最後輸出每個單字出現的次數。

用C style寫成的Ruby

#!/usr/local/bin/ruby if ARGV.length < 2 exit end # read stop words stop = File.new(ARGV[0]) stop_words = Hash.new() while line = stop.gets for e in line.split stop_words[e] = 1 end end stop.close # read text text = File.new(ARGV[1]) count = Hash.new(0) while line = text.gets for e in line.split count[e] += 1 if stop_words[e] == nil end end text.close # dump results for key in count.keys print count[key], ' ', key, "\n" end

使用Code Block + Iterator的Ruby

這種特色是稱為Closure吧?

#!/usr/local/bin/ruby exit if ARGV.length < 2 # read stop words stop = File.new(ARGV[0]) stop_words = Hash.new() while line = stop.gets line.split.each { |e| stop_words[e] = 1} end stop.close # read text text = File.new(ARGV[1]) count = Hash.new(0) while line = text.gets line.split.each { |e| count[e] += 1 if stop_words[e] == nil } end text.close # dump results count.keys.each { |key| puts "#{count[key]} #{key}" }

結論

Ruby把一些loop的動作簡化為method call,寫起來更快,可讀性更好。效率測試上聽說不會輸Java太多,我自己的例子是差不多啦。另外內建的文件查詢程式 ri 很好用,好比可以用 man 查 C function,可以用 perldoc 查 perl ,初用的感覺比這兩者更方便,最重要的是有例子。好的文件查詢系統可以大幅增加初學者留下來的機率。

Programming Ruby 2nd裡強調,這不單單是把code block傳入callback裡,而是”互讓” ( yeild ),至於互讓隱含的好處,我還不能參透。

書裡對yeild的說明範例如下,yeild若有參數的話,就是對應到 |PARAMETER| 裡。code:

def call_block puts "Start of method" yield yield puts "End of method" end call_block { puts "In the block" }

produces:

Start of method In the block In the block End of method