第一天練習node
puts "Pllease input a number between 0-99" a = rand(100) b = gets() c = b.to_i() while c != a if c < a puts "small" else puts "big" end puts "input again:" b = gets() c = b.to_i() end puts "Bingo"
次日練習編程
散列->數組:keys,values, to_a數組
練習1,ruby
a = (1..16).to_a數據結構
使用each併發
n = 1 a.each do |i| if n%4 != 0 print i print " " else puts i end n = n + 1 end
使用each_slice 框架
a.each_slice(4){|i| p i}
練習2函數
class Tree attr_accessor :children, :node_name def initialize(trees) @node_name = trees.keys[0] @children = [] if trees[@node_name] != nil @children = trees[@node_name].map {|k,v| Tree.new({k=>v})} end end def visit_all(&block) visit &block children.each {|c| c.visit_all &block} end def visit(&block) block.call self end end ruby_tree = Tree.new ({'grandpa' => { 'dad' => {'child 1' => {}, 'child 2' => {} }, 'uncle'=> {'child 3' => {}, 'child 4' => {} } } }) puts "Visiting a node" ruby_tree.visit {|node| puts node.node_name} puts puts "Visiting entire tree" ruby_tree.visit_all {|node| puts node.node_name}
map的使用見http://chrisholtz.com/blog/lets-make-a-ruby-hash-map-method-that-returns-a-hash-instead-of-an-array/學習
練習3ui
File.open("tree2.rb") do |file| file.each_line do |line| if line =~ /tree/ p "#{file.lineno}: #{line}" end end end
第三天練習
module ActsAsCsv def self.included(base) base.extend ClassMethods end module ClassMethods def acts_as_csv include InstanceMethods end end module InstanceMethods def read @csv_contents = [] file = File.new(self.class.to_s.downcase + '.txt') @headers = file.gets.chomp.split(', ') file.each do |row| @csv_contents << row.chomp.split(', ') end end attr_accessor :headers, :csv_contents def initialize read end def each len = @csv_contents.size i = 0 while i < len yield CsvRow.new(@csv_contents[i]) i = i + 1 end end end end class RubyCsv include ActsAsCsv acts_as_csv end class CsvRow attr_accessor :contents def initialize(contents) @contents = contents[0].split(',') #contents是一個相似["name, age, phone"]的字符串,因此分割它爲數組["name", "age", "phone"] end def method_missing name, *args numbers = {"one"=>1, "two"=>2, "three"=>3, "four"=>4} col = numbers[name.to_s].to_i - 1 @contents[col] end def to_ary @contents end end m = RubyCsv.new puts m.csv_contents.inspect puts m.each {|row| puts row} puts m.each {|row| puts row.two}
值得注意的是,若是沒有定義to_ary而定義的是to_s
def to_s @contents.join(", ") end
則
m.each {|row| puts row}
一句會出現「Can't convert CsvRow to Array(CsvRow#to_ary gives String)」的錯誤,提示給定String的狀況下未定義CsvRow。
但若是去掉to_ary跟method_missing的定義,只定義to_s,則能夠正常輸出。
這是由於:puts默認調用對象的to_ary函數,找不到時纔會試着調用to_s。因此只定義to_s的狀況下會正常輸出。而若是在定義了上述method_missing的狀況下再定義to_s,因爲優先尋找的是to_ary,它未定義,因此會用method_missing方法來代替它,但這裏我們定義method_missing返回值爲String(即@contents[col]),而to_ary的返回值要求是數組,這就是錯誤「Can't convert CsvRow to Array(CsvRow#to_ary gives String)」出現的緣由。
爲了驗證這一點,定義to_s函數跟空的method_missing函數,
class CsvRow attr_accessor :contents def initialize(contents) @contents = contents[0].split(',') end def method_missing name, *args end def to_s @contents.join(', ') end end
發現能正常調用to_s打印,說明空的method_missing不會阻止puts繼續尋找to_s函數,或者說method_missing返回值爲空的狀況下,puts會認爲to_ary沒有找到,進而繼續找to_s。
而若是定義
def method_missing name, *args a = ["b","c"] end def to_s @contents.join(', ') end
則會打印
b c
這是由於method_missing的返回值爲數組,知足to_ary返回值要爲數組的要求,從而puts直接調用method_missing做爲to_ary,再也不尋找to_s。
puts調用to_ary的緣由見http://stackoverflow.com/questions/8960685/ruby-why-does-puts-call-to-ary,這個網頁還有define_method的幾行代碼,也值得看。
一些點:
method_missing:能力太強大,直接會覆蓋診斷信息,不利於錯誤調試。相關鏈接:define_method。
method_missing建議:慎用,上面那個puts的錯誤就是由於這個。另外一方面,有時候能用它幹一些腦洞大開的事情,如XML框架builder容許用戶經過 method_missing 方法定義自定義標籤,以提供更加美觀的語法。