七週七語言——Ruby

第一天練習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的幾行代碼,也值得看。

 

一些點:

  •  Ruby是解釋型、面向對象、動態類型(類型在運行時而非編譯時綁定)的語言,大部分時候表現得像是強類型語言(例外:好比你能夠在運行時改變類)。
  • Ruby是純面嚮對象語言,好比數字也是FixNum類的對象。Ruby中一切皆對象,而類自己也是對象,全部其餘類都是Class類的對象。函數也是對象。
  • 一等對象:可存儲於變量或數據結構中;可做爲參數傳遞給函數;可做爲返回值從函數返回;可在運行時建立。如C++中的「對象」是一等對象,但函數不是,由於C++中函數沒法在運行時建立。
  • 除nil跟false外,其餘值都是true,包括0。
  • 鴨子類型:對接口編碼,而不對實現編碼。好比String、Float類均可以調用to_i。鴨子類型不在意實際類型(內在實現)多是什麼,只要它像鴨子同樣走路,像鴨子同樣嘎嘎叫,那它就是隻鴨子。尤爲注意體會這種思想。
  • 符號與值,好比帶冒號的符號「:str」在程序中的標識是惟一的,儘管兩個同值字符串在物理上不一樣(object_id不一樣),但相同的符號倒是同一物理對象(object_id相同)。
  • yield:這個貌似在函數式語言中很重要,尤爲是要設計迭代器式的方法時。
  • Ruby命名規範,尼瑪不是通常的清晰,舉例看看:類以大寫字母開頭,採用駱駝命名法;類的實例變量(一個對象一個值)前加@,類變量(一個類一個值)前加@@;實例變量和方法以小寫字母開頭,下劃線命名法;條件判斷函數後面加"?"。因爲標識符不限定只使用「數字、字母、下劃線」,命名規範的選擇性比類C語言大多了,從而也更清晰多了,好比實例變量加@就比C++中前綴m_啥的強多了,條件判斷函數加"?"就比is_、has_、can_等前綴簡單清晰多了。
  • Ruby採用模塊來解決多重繼承的問題,相比於Java用接口來描述,Ruby的這種解決辦法相對約束性小,可是更加自由。體會26頁to_s定義在類中而不是模塊中這一點。
  • 體會mixin的編程方式:先定義類的主要部分,而後用模塊添加額外功能,即便用單一繼承結合mixin的方式,儘量合理地把各類行爲打包到一塊兒。
  • Ruby中兩個重要的mixin,枚舉(enumerable)和比較(comparable)。若是想讓類可枚舉,必須實現 each 方法;若是想讓類可比較,必須實現 <=> 操做符。體會這種思想。
  • 開放類:容許你隨時改變任何類的定義,經常使用於給類添加行爲,可認爲是鴨子類型思想的一種表現。這是一種能力,但注意別濫用,不然帶來的壞處比好處多,好比雜亂,甚至毀滅,好比你重定義Class類的new方法。始終銘記:自由越大,責任越重。
  • 開放類的一些用處:修改Ruby讓它方便於某個特定領域的編程。
  • method_missing:能力太強大,直接會覆蓋診斷信息,不利於錯誤調試。相關鏈接:define_method。

  • method_missing建議:慎用,上面那個puts的錯誤就是由於這個。另外一方面,有時候能用它幹一些腦洞大開的事情,如XML框架builder容許用戶經過 method_missing 方法定義自定義標籤,以提供更加美觀的語法。

  • Ruby經過模塊與類的結合來實現元編程。注意,類也是模塊。這個能力很強大,好比能夠動態地改變類。有時間能夠多學習體會下ActiveRecord。
  • Ruby的一些優勢:
    • 鴨子類型思想的盡致應用:根據對象可提供的方法,而不是對象的繼承層次,實現了更切合實際的多態設計
    • 模塊和開放類的能力
  • Ruby的一些缺點
    • 效率:一個核心的緣由——因爲容許開放類,一個類任什麼時候候都有可能改變。
    • 擴展性
    • 併發
    • 動態類型帶來的限制,如調試的困難等,這是任何事物的一體兩面性,對比着看。
  • Ruby應用場景:
    • 適用於要求快速迭代開發的任務
    • 適用於Web開發,得益於Rails框架。
    • 適用與對併發和擴展性要求高的場景。
    • 大型項目慎用,動態語言用在大型項目上確實會感到吃力。
相關文章
相關標籤/搜索