Ruby七天入門(2 函數,數組和類),ruby七天
DAY 2
第二天學習繼續,今天重點學習函數,集合和類等編程的基本要素。
2.1 函數2.1.1 簡單定義
def tell_me puts true end
- 定義一個簡單的函數(無參無傳回值)很簡單,使用def end包裹函數體即可。
2.1.2 參數
def tell_me(a)puts aendtell_me 11=> nil
2.1.2.1 參數預設值
同樣可以指定參數預設值:
def tell_me(a='1',b)puts aputs bend=> niltell_me 212=> niltell_me 3,434=> nil
我們看到指定預設值的方式跟其他語言類似。
有些苦惱的是,看起來我們需要記住每一個函數的參數數量和含義。
2.1.2.2 可變參數
Ruby同樣支援可變參數,而且因為不受類型的限制,使用相當自由:
def test(*tt) for i in 0...tt.length puts tt[i] endend=> niltest 1,212=> 0...2test 'a'a=> 0...1
- 我們看到只需要將參數前加*即可,收到的參數就將是一個數組。
2.1.3 傳回值2.1.3.1 預設傳回值
- 昨天的學習裡看到過,其實所有的Ruby語句都有傳回值,那麼其實我們及時不給函數指定傳回值,函數也會返回他執行的最後一個語句的值。
看下面的例子:
def give_metrueend=> nili=give_me=> truei=> true
這個函數我們並沒有指定傳回值,但是函數還是返回了最後執行的語句true的值。
2.1.3.2 return語句
$ irbdef testa=1b=2return a,bend=> nilvar=test=> [1, 2]var.class=> Arrayvar=> [1, 2]
2.2 代碼塊和yield2.2.1 代碼塊的使用
代碼塊是沒有名字的函數。
3.times{puts 'hi'}
- times是Fixnum類的方法,它可以執行後面的代碼塊n次。
- 後面大括弧中內容就是代碼塊,代碼塊既可以用{}來包裹,也可以用do/end來包裹,就像下面的的代碼。
3.times doputs 'hello'end
昨天在區間一節中提到的each函數後面其實也是代碼塊:
names=['lee','tom','jim']names.each{|a| puts a}
2.2.2 yield執行代碼塊
下面我們來看下自訂的方法來執行代碼塊:
def my_times(i=5) while i>0 i=i-1 yield endendmy_times(3){puts 'a'}
我們看到這個函數根據傳入參數,調用了一定數量的代碼塊。
2.2.3 代碼塊作為函數參數
我們也可以將代碼塊作為參數一直傳遞下去。
def call_block(&block) block.callenddef pass_block(&block) call_block(&block)endpass_block{puts 'hello'}
- 在Ruby中,參數名之前加一個“&”,表示將代碼塊作為閉包傳遞給函數。閉包就是把函數以及變數包起來,使得變數的生存周期延長。
2.3 數組
animals = ['lions', 'tigers', 'bears']puts animals[1]puts animals[-1]puts animals[0..1]
- 上面這段代碼有個文法糖,-1表示返回倒數第一個元素;當然1和其他語言一樣,表示第二個元素
- animals[0..1]用了昨天學過的區間,表示從0到1個元素
irb(main):002:0> [].class=> Arrayirb(main):003:0> a =[]=> []irb(main):004:0> a[1]=> nilirb(main):005:0> b[1]NameError: undefined local variable or method `b' for main:Object from (irb):5 from /usr/bin/irb:12:in `<main>'irb(main):006:0> a[1] = 1=> 1irb(main):007:0> a[0] = 'zero'=> "zero"irb(main):009:0> a[2]=['2','3',[4,5]]=> ["2", "3", [4, 5]]irb(main):010:0> a=> ["zero", 1, ["2", "3", [4, 5]]]irb(main):011:0> a[2][2]=> [4, 5]irb(main):012:0> a[2][2][1]=> 5irb(main):021:0> a=[]=> []irb(main):022:0> a[1]=8=> 8irb(main):023:0> a[3]=6=> 6irb(main):024:0> a=> [nil, 8, nil, 6]
比起大多數語言的數組,Ruby的數組使用很自由,從上面這些代碼可以看出來。
- 數組元素不必有相同類型
- 可以只定義部分數組元素,其餘位置將預設為nil值
- 需要注意的是,在給數組元素賦值時,需要首先使用a =[]類似文法讓a的類型變為數群組類型,否則出現上面直接運行 b[1]時報的錯誤
irb(main):025:0> a.push(4)=> [nil, 8, nil, 6, 4]irb(main):026:0> a.pop=> 4irb(main):027:0> a=> [nil, 8, nil, 6]
數組同樣支援push和pop,棧頂位於數組尾部。
2.3.4 散列表
散列表就是索引值對,對應java裡面的map容器。
irb(main):032:0> map={1=>'one',2=>'two'}=> {1=>"one", 2=>"two"}irb(main):033:0> map=> {1=>"one", 2=>"two"}irb(main):034:0> map[1]=> "one"irb(main):035:0> map[3]=> nil
其實和數組類似,關鍵的不同就在於一個=>符號,符號前面是鍵,後面是值。
map={1=>'one',2=>'two'}a = map.to_aputs a
- 散列表可以轉化為數組:其中每個元素是一個key, value組成的數組
map.each { |k,v| p "#{k}=>#{v}" }
散列表同樣可以遍曆
2.5 類
所有的類都有一個共同的祖先,BasicObject。
class TreeTestattr_accessor :children,:node_namedef initialize(name,children=[]) @children = children @node_name = nameendendruby_tree = Tree.new("ruby",[Tree.new('1'),Tree.new('2')])
上面代碼定義了一個簡單的類。
- 類命名一般使用駱駝命名法,變數和方法名採用小寫底線命名法,而常量採用全大寫形式;用於邏輯測試的函數一般用?結尾
- 執行個體變數前需要加@(類似java的this?),而類變數前需要加@@(類似java的類名?)
- attr關鍵字可用來定義執行個體變數。它有幾種版本,其中最常用的版本是attr和attr_accessor。attr定義執行個體變數和訪問變數的同名方法,而attr_accessor定義執行個體變數、存取方法和設定方法。
- initialize方法有特殊含義,類在初始化一個新對象的時候,會調用這個方法
- 類初始化時採用類名加.new的方法來初始化
2.6 小結
今天主要學習了Ruby的函數,類,數組的基本使用,最大的感觸是Ruby的實現充滿了符合直覺的特性,在掌握了基本文法後,很多代碼只需要按照嘗試著寫就可以執行。
2.7實踐
今天實踐在前面Tree類的基礎上加上遍曆訪問的功能,同時我修改了初始化方法,接受散列表和數組嵌套的結構:
class Treeattr_accessor :children,:node_namedef initialize(map={}) @children = [] map.each do |k,v| @node_name = k v.each { |key,value| @children.push(Tree.new({key=>value}))} if v!=nil endenddef visit_all(&block) visit(&block) children.each{|c| c.visit_all(&block)}enddef visit(&block) block.call(self)endendruby_tree = Tree.new({'grandpa' => { 'dad' => {'child 1' => {}, 'child 2' => {} }, 'uncle'=> {'child 3' => {}, 'child 4' => {} } } } )ruby_tree.visit {|node| puts node.node_name}ruby_tree.visit_all {|node| puts node.node_name}
visit方法傳遞了代碼塊,調用了代碼塊並用self做參數(所以看起來代碼塊是通過這種方式獲得參數?),其實就是列印了調用執行個體的node_name。
visit_all先對當前節點調用了visit方法,然後對子節點進行了遞迴。