楼主: AlexQin

《 笨方法學 Ruby 》(Learn Ruby The Hard Way)

[复制链接]
论坛徽章:
1056
紫蜘蛛
日期:2015-09-22 15:53:22紫蜘蛛
日期:2015-10-15 13:48:52紫蜘蛛
日期:2015-10-15 14:45:48紫蜘蛛
日期:2015-10-15 14:47:47紫蜘蛛
日期:2015-10-15 14:48:45九尾狐狸
日期:2015-09-22 15:53:22九尾狐狸
日期:2015-10-15 13:50:37九尾狐狸
日期:2015-10-15 14:45:48九尾狐狸
日期:2015-10-15 14:47:47九尾狐狸
日期:2015-10-15 14:48:45
21#
 楼主| 发表于 2013-11-15 15:06 | 只看该作者
習題 12: 模組 (Module)

看看這段 code

  1. require 'open-uri'

  2. open("http://www.ruby-lang.org/en") do |f|
  3.   f.each_line {|line| p line}
  4.   puts f.base_uri         # <URI::HTTP:0x40e6ef2 URL:http://www.ruby-lang.org/en/>
  5.   puts f.content_type     # "text/html"
  6.   puts f.charset          # "iso-8859-1"
  7.   puts f.content_encoding # []
  8.   puts f.last_modified    # Thu Dec 05 02:45:02 UTC 2002
  9. end
复制代码

在第一行是 require。這是一個 Ruby 中在你所寫的腳本中加入其他來源(如:Ruby Gems 或者是你寫的其他東西)的功能(features) 的方法。與其一次給你所有功能,Ruby 會問你你打算使用什麼。這可使你的程式保持輕薄,又可當做之後其他程式設計師閱讀你的程式時的參考。

等一下!功能 (Features) 還有另外一個名字

我在這裡稱呼他們為「功能(features)」。但實際上沒人這樣稱呼。我這樣做只是取了點巧,使你在學習時先不用理解「行話」。在繼續進行之前你得先知道它們的真名 modules(模組)。

從現在開始我們將把這些我們 require 進來的功能稱作 modules(模組)。我會這樣說:「你想要 require open-uri module。」也有人給它另外一個稱呼:「函式庫(libraries)」。但在這裡我們還是先叫它們 modules (模組)吧。

加分習題

  • 上網搜尋 require 與 include 的差異點。它們有什麼不同?
  • 你能 require 一段沒有特別包含 module 的腳本嗎?
  • 搞懂 Ruby 會去系統的哪裡找你 require 的 modules。

使用道具 举报

回复
论坛徽章:
1056
紫蜘蛛
日期:2015-09-22 15:53:22紫蜘蛛
日期:2015-10-15 13:48:52紫蜘蛛
日期:2015-10-15 14:45:48紫蜘蛛
日期:2015-10-15 14:47:47紫蜘蛛
日期:2015-10-15 14:48:45九尾狐狸
日期:2015-09-22 15:53:22九尾狐狸
日期:2015-10-15 13:50:37九尾狐狸
日期:2015-10-15 14:45:48九尾狐狸
日期:2015-10-15 14:47:47九尾狐狸
日期:2015-10-15 14:48:45
22#
 楼主| 发表于 2013-11-15 15:08 | 只看该作者
習題 13: 参数、解包、參數

在這節習題中,我們將涵蓋另外一種將變數傳遞給腳本的方法(所謂腳本,就是你寫的 .rb 檔案)。你已經知道,如果要執行 ex13.rb,只要在命令列中執行 ruby ex13.rb 就可以了。這句命令中的 ex13.rb 部分就是所謂的「參數(argument)」,我們現在要做的就是寫一個可以接受參數的腳本。

將下面的程式寫下來,後來我將詳細解釋

  1. first, second, third = ARGV

  2. puts "The script is called: #{$0}"
  3. puts "Your first variable is: #{first}"
  4. puts "Your second variable is: #{second}"
  5. puts "Your third variable is: #{third}"
复制代码

ARGV 就是「參數變數(argument variable)」,是一個非常標準的程式術語。在其他的程式語言你也可以看到它全大寫的原因是因為它是一個「常數(constant)」,意思是當它被賦值之後你就不應該去改變它了。這個變數會接收當你運行 Ruby 腳本時所傳入的參數。通過後面的習題你將對它有更多的了解。 你将对它有更多的了解。

第 1 行將 ARGV 「解包(unpack)」,與其將所有參數放到同一個變數下面,我們將每個參數賦予一個變數名稱 first 、 second 以及 third。腳本本身的名稱被存在一個特殊變數 $0 裡,這是我們不需要解包的部份。也許看來有些詭異,但「解包」可能是最好的描述方式了。它的涵義很簡單:「將 ARGV 中的東西解包,然後將所有的參數依次賦予左邊的變數名稱」。

接下來就是正常的印出了。

你應該看到的結果

用下面的方法執行你的程式:

  1. ruby ex13.rb first 2nd 3rd
复制代码

如果你每次使用不同的參數執行,你將看到下面的結果:

  1. $ ruby ex13.rb first 2nd 3rd
  2. The script is called: ex13.rb
  3. Your first variable is: first
  4. Your second variable is: 2nd
  5. Your third variable is: 3rd

  6. $ ruby ex13.rb cheese apples bread
  7. The script is called: ex13.rb
  8. Your first variable is: cheese
  9. Your second variable is: apples
  10. Your third variable is: bread

  11. $ ruby ex13.rb Zed A. Shaw
  12. The script is called: ex13.rb
  13. Your first variable is: Zed
  14. Your second variable is: A.
  15. Your third variable is: Shaw
复制代码

你其實可以將「first」、「2nd」、「3rd」替換成任意三樣東西。你可以將它們換成任意你想要的東西。

  1. ruby ex13.rb stuff I like
  2. ruby ex13.rb anything 6 7
复制代码

加分習題

  • 傳三個以下的參數給你的腳本。當有缺少參數時哪些數值會被使用到?
  • 再寫兩個腳本,其中一個接收更少的參數,另一個接收更多的參數。在參數解包時給它們取一些有意義的變數名稱。
  • 結合 gets.chomp 和 ARGV 一起使用,讓你的腳本從用戶手上得到更多輸入。

使用道具 举报

回复
论坛徽章:
1056
紫蜘蛛
日期:2015-09-22 15:53:22紫蜘蛛
日期:2015-10-15 13:48:52紫蜘蛛
日期:2015-10-15 14:45:48紫蜘蛛
日期:2015-10-15 14:47:47紫蜘蛛
日期:2015-10-15 14:48:45九尾狐狸
日期:2015-09-22 15:53:22九尾狐狸
日期:2015-10-15 13:50:37九尾狐狸
日期:2015-10-15 14:45:48九尾狐狸
日期:2015-10-15 14:47:47九尾狐狸
日期:2015-10-15 14:48:45
23#
 楼主| 发表于 2013-11-15 15:09 | 只看该作者

習題 14: 提示和傳遞

讓我們使用 ARGV 和 gets.chomp 一起來向使用者提一些特別的問題。下一節習題你將會學習到如何讀寫檔案,這節習題是下節的基礎。在這道習題裡我們將用一個簡單的 > 作為提示符號。這和一些遊戲中的方法類似,例如 Zork 或者 Adventure 這兩款遊戲。

  1. user = ARGV.first
  2. prompt = '> '

  3. puts "Hi #{user}, I'm the #{$0} script."
  4. puts "I'd like to ask you a few questions."
  5. puts "Do you like me #{user}?"
  6. print prompt
  7. likes = STDIN.gets.chomp()

  8. puts "Where do you live #{user}?"
  9. print prompt
  10. lives = STDIN.gets.chomp()

  11. puts "What kind of computer do you have?"
  12. print prompt
  13. computer = STDIN.gets.chomp()

  14. puts <<MESSAGE
  15. Alright, so you said #{likes} about liking me.
  16. You live in #{lives}.  Not sure where that is.
  17. And you have a #{computer} computer.  Nice.
  18. MESSAGE
复制代码

注意到我們將用戶提示符號設置為 prompt,這樣我們就不用每次都要重打一遍了。如果你要將提示符號和修改成別的字串,你只要改一個地方就可以了。

非常順手吧。

Important: 同時必須注意的是,我們也用了 STDIN.gets 取代了 gets。這是因為如果有東西在 ARGV 裡,標準的gets 會認為將第一個參數當成檔案而嘗試從裡面讀東西。在要從使用者的輸入(如stdin)讀取資料的情況下我們必須明確地使用 STDIN.gets。

你應該看到的結果

當你執行這個腳本時,記住你需要把你的名字傳給這個腳本,讓 ARGV 可以接收到。

  1. $ ruby ex14.rb Zed
  2. Hi Zed, I'm the ex14.rb script.
  3. I'd like to ask you a few questions.
  4. Do you like me Zed?
  5. > yes
  6. Where do you live Zed?
  7. > America
  8. What kind of computer do you have?
  9. > Tandy

  10. Alright, so you said 'yes' about liking me.
  11. You live in 'America'.  Not sure where that is.
  12. And you have a 'Tandy' computer.  Nice.
复制代码

加分習題

  • 查一下 Zork 和 Adventure 是兩個怎樣的遊戲。看能不能抓到,然後玩玩看。
  • 將 prompt 變數改為完全不同的內容再執行一遍。
  • 給你的腳本再新增一個參數,讓你的程式用到這個參數。
  • 確認你弄懂了我如何結合 <<SOMETHING 形式的多行字串與 #{} 字串注入做的印出。

使用道具 举报

回复
论坛徽章:
1056
紫蜘蛛
日期:2015-09-22 15:53:22紫蜘蛛
日期:2015-10-15 13:48:52紫蜘蛛
日期:2015-10-15 14:45:48紫蜘蛛
日期:2015-10-15 14:47:47紫蜘蛛
日期:2015-10-15 14:48:45九尾狐狸
日期:2015-09-22 15:53:22九尾狐狸
日期:2015-10-15 13:50:37九尾狐狸
日期:2015-10-15 14:45:48九尾狐狸
日期:2015-10-15 14:47:47九尾狐狸
日期:2015-10-15 14:48:45
24#
 楼主| 发表于 2013-11-16 11:23 | 只看该作者
習題 15: 讀取檔案

你已經學過了 STDIN.gets 和 ARGV,這些是你開始學習讀取檔案的必備基礎。你可能需要多多實驗才能明白它的運作原理,所以你要細心練習,並且仔細檢查結果。處理檔案需要非常仔細,如果不仔細的話,你可能會把有用的檔案弄壞或者清空。導致前功盡棄。

這節練習涉及到寫兩個檔案。一個正常的 ex15.rb 文件,另外一個是 ex15_sample.txt,第二個文件並不是腳本,而是供你的腳本讀取的文字檔案。以下是後者的內容:

  1. This is stuff I typed into a file.
  2. It is really cool stuff.
  3. Lots and lots of fun to have in here.
复制代码

我們要做的是把該檔案用我們的腳本「打開(open)」,然後印出來。然而把檔名 ex15_sample.txt 「寫死(Hard Coding」在程式碼不是一個好主意,這些資訊應該是使用者輸入的才對。如果我們碰到其他檔案要處理,寫死的檔名就會給你帶來麻煩了。解決方案是使用 ARGV 和 STDIN.gets 來從使用者端獲取資訊,從而知道哪些檔案該被處理。

  1. filename = ARGV.first

  2. prompt = "> "
  3. txt = File.open(filename)

  4. puts "Here's your file: #{filename}"
  5. puts txt.read()

  6. puts "Type the filename again:"
  7. print prompt
  8. file_again = STDIN.gets.chomp()

  9. txt_again = File.open(file_again)

  10. puts txt_again.read()
复制代码

這個腳本中有一些新奇的玩意,我們來快速地過一遍:

程式碼的 1-3 行使用 ARGV 來獲取檔名,這個你已經熟悉了。接下來第 4 行我們使用一個新的命令 File.open。現在請在命令列執行 ri File.open 來讀讀它的說明。注意到這多像你的腳本,它接收一個參數,並且傳回一個值,你可以將這個值賦予一個變數。這就是你打開檔案的過程。

第 6 行我們印出了一小行,但在第 7 行我們看到了新奇的東西。我們在 txt 上呼叫了一個函式。你從 open 獲得的東西是一個 file (檔案),檔案本身也支援一些命令。它接受命令的方式是使用句點 . (dot or period),緊跟著你的命令,然後參數。就像 File.open 做的事一樣。差別是:當你說 txt.read() 時,你的意思其實是:「嘿 txt!執行你的 read 命令,無需任何參數!」

腳本剩下的部份基本差不多,不過我就把剩下的分析作為加分習題留給你自己了。

你應該看到的結果

我的腳本叫 “ex15_sample.txt”,以下是執行結果:

  1. $ ruby ex15.rb ex15_sample.txt
  2. Here's your file 'ex15_sample.txt':
  3. This is stuff I typed into a file.
  4. It is really cool stuff.
  5. Lots and lots of fun to have in here.

  6. I'll also ask you to type it again:
  7. > ex15_sample.txt
  8. This is stuff I typed into a file.
  9. It is really cool stuff.
  10. Lots and lots of fun to have in here.

  11. $
复制代码

加分習題


這節的難度跨越有點大,所以你要儘量做好這節加分習題,然後再繼續後面的章節。

  • 在每一行的上面用注釋說明這一行的用途。
  • 如果你不確定答案,就問別人,或者是上網搜尋。大部分時候,只要搜尋「ruby 你要搜尋的東西」,就能得到你要的答案。比如搜尋一下「ruby file.open」。
  • 我使用了「命令」這個詞,不過實際上他們的名字是「函式(function)」和「方法(method)」。上網搜尋一下這兩者的意義和區別。看不懂也沒關係,迷失在其他程式設計師的知識海洋裡是很正常的一件事。
  • 刪掉 9-15 行使用到 STDIN.gets 的部份,再執行一次腳本。
  • 只用 STDIN.gets 撰寫這個腳本,想想哪種得到檔名的方法更好,以及為什麼。
  • 執行 ri File 然後往下捲動直到看見 read() 命令(函式/方法)。看到很多其他的命令了吧。你可以玩其他試試。
  • 再次啟動 IRB,然後在裡面使用 File.open 打開一個文件,這種 open 和 read 的方法也值得一學。
  • 讓你的腳本針對 txt 和 txt_again 變數執行一下 close(),處理完檔案後你需要將其關閉,這是很重要的一點。

使用道具 举报

回复
论坛徽章:
1056
紫蜘蛛
日期:2015-09-22 15:53:22紫蜘蛛
日期:2015-10-15 13:48:52紫蜘蛛
日期:2015-10-15 14:45:48紫蜘蛛
日期:2015-10-15 14:47:47紫蜘蛛
日期:2015-10-15 14:48:45九尾狐狸
日期:2015-09-22 15:53:22九尾狐狸
日期:2015-10-15 13:50:37九尾狐狸
日期:2015-10-15 14:45:48九尾狐狸
日期:2015-10-15 14:47:47九尾狐狸
日期:2015-10-15 14:48:45
25#
 楼主| 发表于 2013-11-16 11:24 | 只看该作者
習題 16: 讀寫檔案

如果你做了上一個練習的加分習題,你應該已經了解了個個種文件相關的命令(方法/函式)。你應該記住的命令如下:

close – 關閉檔案。跟你編輯器的 文件->儲存.. 是一樣的意思。
read – 讀取檔案內容。你可以把結果賦給一個變數。
readline – 讀取檔案文字中的一行。
truncate – 清空文件,請小心使用該命令。
write(stuff) – 將 stuff 寫入檔案。
這是你現在應該知道的重要命令。有些命令需要接收參數,但這對我們並不重要。你只要記住 write 的用法就可以了。 write 需要接收一個字串作為參數,從而將該字串寫入檔案。

讓我們來使用這些命令做一個簡單的文字編輯器吧:

  1. filename = ARGV.first
  2. script = $0

  3. puts "We're going to erase #{filename}."
  4. puts "If you don't want that, hit CTRL-C (^C)."
  5. puts "If you do want that, hit RETURN."

  6. print "? "
  7. STDIN.gets

  8. puts "Opening the file..."
  9. target = File.open(filename, 'w')

  10. puts "Truncating the file.  Goodbye!"
  11. target.truncate(target.size)

  12. puts "Now I'm going to ask you for three lines."

  13. print "line 1: "; line1 = STDIN.gets.chomp()
  14. print "line 2: "; line2 = STDIN.gets.chomp()
  15. print "line 3: "; line3 = STDIN.gets.chomp()

  16. puts "I'm going to write these to the file."

  17. target.write(line1)
  18. target.write("\n")
  19. target.write(line2)
  20. target.write("\n")
  21. target.write(line3)
  22. target.write("\n")

  23. puts "And finally, we close it."
  24. target.close()
复制代码

這是一個大檔案,大概是你鍵入過的最大的檔案。所以慢慢來,仔細檢查,讓它能夠跑起來。有一個小技巧就是你可以讓你的腳本一部分一部分地跑起來。先寫 1-8 行,讓它能跑起來,再多做 5 行,再接著幾行,以此類推,直到整個腳本都可以跑起來為止。

你應該看到的結果

你將看到兩樣東西,一樣是你新腳本的輸出:

  1. $ ruby ex16.rb test.txt
  2. We're going to erase 'test.txt'.
  3. If you don't want that, hit CTRL-C (^C).
  4. If you do want that, hit RETURN.
  5. ?
  6. Opening the file...
  7. Truncating the file.  Goodbye!
  8. Now I'm going to ask you for three lines.
  9. line 1: To all the people out there.
  10. line 2: I say I don't like my hair.
  11. line 3: I need to shave it off.
  12. I'm going to write these to the file.
  13. And finally, we close it.
  14. $
复制代码

這是一個大檔案,大概是你鍵入過的最大的檔案。所以慢慢來,仔細檢查,讓它能夠跑起來。有一個小技巧就是你可以讓你的腳本一部分一部分地跑起來。先寫 1-8 行,讓它能跑起來,再多做 5 行,再接著幾行,以此類推,直到整個腳本都可以跑起來為止。

加分習題

  • 如果你覺得自己沒有弄懂的話,用我們的老方法,在每一行之前加上註釋,為自己理清思路。就算不能理清思路,你也可以知道自己究竟具體哪裡沒弄清楚。
  • 寫一個和上一個習題類似的腳本,使用 read 和 ARGV 讀取你剛才新建立的文件。
  • 檔案中重複的地方太多了。試著用一個 target.write() 將 line1 , line2 , line3 印出來,你可以使用字串、格式化字串以及跳脫字串。
  • 找出為什麼我們打開檔案時要使用 w 模式,而你真的需要 target.truncate() 嗎?去看 Ruby 的 File.open 函式找答案吧。

使用道具 举报

回复
论坛徽章:
1056
紫蜘蛛
日期:2015-09-22 15:53:22紫蜘蛛
日期:2015-10-15 13:48:52紫蜘蛛
日期:2015-10-15 14:45:48紫蜘蛛
日期:2015-10-15 14:47:47紫蜘蛛
日期:2015-10-15 14:48:45九尾狐狸
日期:2015-09-22 15:53:22九尾狐狸
日期:2015-10-15 13:50:37九尾狐狸
日期:2015-10-15 14:45:48九尾狐狸
日期:2015-10-15 14:47:47九尾狐狸
日期:2015-10-15 14:48:45
26#
 楼主| 发表于 2013-11-16 11:26 | 只看该作者
習題 17: 更多的檔案操作

現在讓我們再學習幾種檔案操作。我們將編寫一個 Ruby 腳本,將一個檔案中的內容拷貝到另一個檔案中。這個腳本很短,不過它會讓你對於檔案操作有更多的了解。

  1. from_file, to_file = ARGV
  2. script = $0

  3. puts "Copying from #{from_file} to #{to_file}"

  4. # we could do these two on one line too, how?
  5. input = File.open(from_file)
  6. indata = input.read()

  7. puts "The input file is #{indata.length} bytes long"

  8. puts "Does the output file exist? #{File.exists? to_file}"
  9. puts "Ready, hit RETURN to continue, CTRL-C to abort."
  10. STDIN.gets

  11. output = File.open(to_file, 'w')
  12. output.write(indata)

  13. puts "Alright, all done."

  14. output.close()
  15. input.close()
复制代码

你應該注意到了我們使用了一個很好用的函式 File.exists?。運作原理是將檔名字串當做一個參數傳入,如果檔案存在的話,它會傳回 true,如果不存在的話就傳回 false。之後在這本書中,我們將會使用這個函式做更多的事情。

你應該看到的結果

如同你前面所寫的腳本,運行該腳本需要兩個參數,一個是待拷貝的檔案位置,一個是要拷貝至的檔案位置。如果我們使用以前的 test.txt 我們將看到如下的結果:

  1. $ ruby ex17.rb test.txt copied.txt
  2. Copying from test.txt to copied.txt
  3. The input file is 81 bytes long
  4. Does the output file exist? False
  5. Ready, hit RETURN to continue, CTRL-C to abort.

  6. Alright, all done.

  7. $ cat copied.txt
  8. To all the people out there.
  9. I say I don't like my hair.
  10. I need to shave it off.
  11. $
复制代码

該命令對於任何檔案應該都是有效的。試試操作一些別的檔案看看結果。不過當心別把你的重要檔案給弄壞了。

Warning: 你看到我用 cat 這個指令了吧?它只能在 Linux 和 OX 下使用,Windows 用戶可以使用 type 做到相同效果。

加分習題

  • 再多讀讀和 require 相關的資料,然後將 IRB 跑起來,試試 require 一些東西看能不能摸出門到。當然,即使搞不清楚也沒關係。
  • 這個腳本實在有點煩人。沒必要再拷貝之前都問一遍吧,沒必要在螢幕上輸出那麼多東西。試著刪掉腳本的一些功能,使它用起來更友善。
  • 看看你能把這個腳本改到多短,我可以把它寫成一行。
  • 我使用了一個叫 cat 的東西,這個古老的命令用處是將兩個檔案「連接(concatenate)」在一起,不過實際上它最大的用處是印出檔案內容到螢幕是。你可以通過 man cat 命令了解到更多資訊。
  • 使用 Windows 的人,你們可以給自己找一個 cat 的替代品。關於 man 的東西就別想太多了。Windows 下沒這個指令。
  • 找出為什麼需要在程式碼中寫 output.close() 的原因。

使用道具 举报

回复
论坛徽章:
1056
紫蜘蛛
日期:2015-09-22 15:53:22紫蜘蛛
日期:2015-10-15 13:48:52紫蜘蛛
日期:2015-10-15 14:45:48紫蜘蛛
日期:2015-10-15 14:47:47紫蜘蛛
日期:2015-10-15 14:48:45九尾狐狸
日期:2015-09-22 15:53:22九尾狐狸
日期:2015-10-15 13:50:37九尾狐狸
日期:2015-10-15 14:45:48九尾狐狸
日期:2015-10-15 14:47:47九尾狐狸
日期:2015-10-15 14:48:45
27#
 楼主| 发表于 2013-11-16 11:29 | 只看该作者
習題 18: 命名、變數、程式碼、函式

好大的一個標題。接下來我要教你「函式 (function)」了!咚咚鏘!說到函式,不一樣的人會對它有不一樣的理解和使用方法,不過我只會教你現在能用到的最簡單的使用方式。

函式可以做三件事情:

  • 它們可以給程式碼片段取名,就跟「變數」給字串和數字命名一樣。
  • 它們可以接受參數,就跟你的腳本接受 ARGV 一樣。
  • 通過使用 #1 和 #2 ,他們可以讓你創造出「迷你腳本」或者「微命令」。


你可以在 Ruby 中使用 def 新建函式,我將讓你創造四個不同的函式,它們運作起來和你的腳本一樣。然後我會示範給你各個函式之間的關係。

  1. # this one is like your scripts with argv
  2. def puts_two(*args)
  3.   arg1, arg2 = args
  4.   puts "arg1: #{arg1}, arg2: #{arg2}"
  5. end

  6. # ok, that *args is actually pointless, we can just do this
  7. def puts_two_again(arg1, arg2)
  8.   puts "arg1: #{arg1}, arg2: #{arg2}"
  9. end

  10. # this just takes one argument
  11. def puts_one(arg1)
  12.   puts "arg1: #{arg1}"
  13. end

  14. # this one takes no arguments
  15. def puts_none()
  16.   puts "I got nothin'."
  17. end

  18. puts_two("Zed","Shaw")
  19. puts_two_again("Zed","Shaw")
  20. puts_one("First!")
  21. puts_none()
复制代码

讓我們把你一個函式 puts_two 肢解一下,這個函式和你寫腳本的方式差不多,因此看上去你應該會覺得比較眼熟:

  • 首先我們告訴 Ruby 創造一個函式,使用 def 去「定義(define)」一個函式。
  • 緊跟著 def 的是函式的名稱。本例中它的名稱是「puts_two」,但名字可以 隨便取,就叫「peanuts」也沒關係。但函式的名稱最好能夠表達出它的功能來。
  • 然後我們告訴函式我們需要 args (asterisk args),這和腳本的 ARGV 非常相似,參數必須放在小括號 () 中才能正常運作。
  • 在定義(definition)之後,後面的行都必須以 2 個空格縮排。其中縮排的第一行的作用是將參數解包,就像我們在腳本中做的事一樣。
  • 為了示範它的運作原理,我們把解包後的每個參數都印出來。puts_two 的問題是它不是建立一個函式最簡單的方法。在 Ruby 中我們可以直接跳過解包參數的過程直接使用 () 裡面的名稱作為變數名。就像 puts_two_again 實現的功能。


接下來的例子是 print_one ,它像你示範了函式如何接收單個參數。

最後一個例子是 print_none ,它向你示範了函式可以不接收任何參數。

Warning: 如果你不太能看懂上面的內容也別氣餒。這是非常重要的。後面我們還有更多的習題向你示範如何創造和使用函式。現在你只要把函式理解成「迷你腳本」就可以了

你應該看到的結果

執行上面的腳本會看到如下結果:

  1. $ ruby ex18.rb
  2. arg1: 'Zed', arg2: 'Shaw'
  3. arg1: 'Zed', arg2: 'Shaw'
  4. arg1: 'First!'
  5. I got nothin'.
  6. $
复制代码

你應該看出來函式是怎樣運作的了。注意到函式的用法和你以前見過的 File.exist? 、 File.open以及別的「命令」有點類似了吧?其實我只是為了讓你容易禮節才叫他們「命令」。它們的本質其實就是函式。也就是說,你也可以在你自己的腳本中創造你自己的「命令」。

加分習題

為自己寫一個函式檢查清單以供後續參考。你可以寫在一個索引卡片上隨時閱讀,直到你記住所有的要點為止。注意事項如下:

  • 函式定義是以 def 開始的嗎?
  • 函式名稱是以字串和底線 _ 組成的嗎?
  • 函式名稱是不是緊跟著括號 ( ?
  • 括號裡是否包含參數?多個參數是否以逗號隔開?
  • 參數名稱是否有重複?(不能使用重複的參數名)
  • 緊跟著參數的最後是否括號 ) ?
  • 緊跟著函式定義的程式碼是否用了 2 個空格的縮排 ( indent )?
  • 函式結束的位置是不是「end」


當你執行(或者說「使用(use)」或者「呼叫(call)」一個函數時,記得檢查下列幾項事情:

  • 呼叫函式時是否使用了函式的名稱?
  • 函式名稱是否緊跟著()?(非必要,理想性的話應該要加)
  • 參數是否以逗號隔開?
  • 函式是否以)結尾?


按照這兩份檢查清單裡的內容檢查你的習題,直到你不需要檢查清單為止。

最後,將下面這句話閱讀幾遍:
「執行(run)函式」、「呼叫(call)函式」和「使用(use)函式」是同一個意思。

使用道具 举报

回复
论坛徽章:
1056
紫蜘蛛
日期:2015-09-22 15:53:22紫蜘蛛
日期:2015-10-15 13:48:52紫蜘蛛
日期:2015-10-15 14:45:48紫蜘蛛
日期:2015-10-15 14:47:47紫蜘蛛
日期:2015-10-15 14:48:45九尾狐狸
日期:2015-09-22 15:53:22九尾狐狸
日期:2015-10-15 13:50:37九尾狐狸
日期:2015-10-15 14:45:48九尾狐狸
日期:2015-10-15 14:47:47九尾狐狸
日期:2015-10-15 14:48:45
28#
 楼主| 发表于 2013-11-16 11:30 | 只看该作者
習題 19: 函式和變數

函式這個概念也許承載了太多的資訊量。不過別擔心,只要堅持做這些練習題,對照上個練習中的檢查清單檢查這次練習的關聯,你最終會明白這些內容的。

有一個你可能沒有注意到的細節,我們現在強調一下,函式裡面的變數和腳本裡面的變數之間是沒有連接的。下面的這個練習可以讓你對這一點有更多的思考:

  1. def cheese_and_crackers(cheese_count, boxes_of_crackers)
  2.   puts "You have #{cheese_count} cheeses!"
  3.   puts "You have #{boxes_of_crackers} boxes of crackers!"
  4.   puts "Man that's enough for a party!"
  5.   puts "Get a blanket."
  6.   puts # a blank line
  7. end

  8. puts "We can just give the function numbers directly:"
  9. cheese_and_crackers(20, 30)

  10. puts "OR, we can use variables from our script:"
  11. amount_of_cheese = 10
  12. amount_of_crackers = 50
  13. cheese_and_crackers(amount_of_cheese, amount_of_crackers)

  14. puts "We can even do math inside too:"
  15. cheese_and_crackers(10 + 20, 5 + 6)

  16. puts "And we can combine the two, variables and math:"
  17. cheese_and_crackers(amount_of_cheese + 100, amount_of_crackers + 1000)
复制代码

通過這個練習,你看到我們給我們的函式 cheese_and_crackers 很多的參數,然後在函式裡把他們印出來。我們可以塞數字、塞變數進去函式,我們甚至可以將變數和數學運算結合在一起。

從一方面來說,函式的參數和我們生成變數時用的 = 賦值符號類似。事實上,如果一個物件你可以用 = 將其命名,你通常也可以將其作為參數傳給一個函式。

你應該看到的結果

你應該研究一下腳本的輸出,和你想像的結果對比一下看有什麼不同。

  1. $ ruby ex19.rb
  2. We can just give the function numbers directly:
  3. You have 20 cheeses!
  4. You have 30 boxes of crackers!
  5. Man that's enough for a party!
  6. Get a blanket.

  7. OR, we can use variables from our script:
  8. You have 10 cheeses!
  9. You have 50 boxes of crackers!
  10. Man that's enough for a party!
  11. Get a blanket.

  12. We can even do math inside too:
  13. You have 30 cheeses!
  14. You have 11 boxes of crackers!
  15. Man that's enough for a party!
  16. Get a blanket.

  17. And we can combine the two, variables and math:
  18. You have 110 cheeses!
  19. You have 1050 boxes of crackers!
  20. Man that's enough for a party!
  21. Get a blanket.
  22. $
复制代码

加分習題

  • 倒著將腳本讀完,在每一行上面添加一行註解,說明這行程式的作用。
  • 從最後一行開始,倒著閱讀每一行,讀出所有重要的符號來。
  • 自己邊寫出至少一個函式出來,然後用十種方法運行這個函式。

使用道具 举报

回复
论坛徽章:
1056
紫蜘蛛
日期:2015-09-22 15:53:22紫蜘蛛
日期:2015-10-15 13:48:52紫蜘蛛
日期:2015-10-15 14:45:48紫蜘蛛
日期:2015-10-15 14:47:47紫蜘蛛
日期:2015-10-15 14:48:45九尾狐狸
日期:2015-09-22 15:53:22九尾狐狸
日期:2015-10-15 13:50:37九尾狐狸
日期:2015-10-15 14:45:48九尾狐狸
日期:2015-10-15 14:47:47九尾狐狸
日期:2015-10-15 14:48:45
29#
 楼主| 发表于 2013-11-18 13:45 | 只看该作者
習題 20: 函式和檔案

回憶一下函式的要點,然後一邊做這節練習,一邊注意一下函式和檔案是如何一起協作發揮作用的。

  1. input_file = ARGV[0]

  2. def print_all(f)
  3.   puts f.read()
  4. end

  5. def rewind(f)
  6.   f.seek(0, IO::SEEK_SET)
  7. end

  8. def print_a_line(line_count, f)
  9.   puts "#{line_count} #{f.readline()}"
  10. end

  11. current_file = File.open(input_file)

  12. puts "First let's print the whole file:"
  13. puts # a blank line

  14. print_all(current_file)

  15. puts "Now let's rewind, kind of like a tape."

  16. rewind(current_file)

  17. puts "Let's print three lines:"

  18. current_line = 1
  19. print_a_line(current_line, current_file)

  20. current_line = current_line + 1
  21. print_a_line(current_line, current_file)

  22. current_line = current_line + 1
  23. print_a_line(current_line, current_file)
复制代码

特別注意一下,每次運行 print_a_line 時,我們是怎樣傳遞當前的行號資訊的。

你應該看到的結果

  1. $ ruby ex20.rb test.txt
  2. First let's print the whole file:

  3. To all the people out there.
  4. I say I don't like my hair.
  5. I need to shave it off.

  6. Now let's rewind, kind of like a tape.
  7. Let's print three lines:
  8. 1 To all the people out there.
  9. 2 I say I don't like my hair.
  10. 3 I need to shave it off.

  11. $
复制代码

加分習題

  • 通讀腳本,在每行之前加上註解,以理解腳本裡發生的事情。
  • 每次 print_a_line 運行時,你都傳遞了一個叫 current_line的變數。在每次呼叫函數時,印出 current_line 的值,跟踪一下它在 print_a_line 中是怎樣變成 line_count 的。
  • 找出腳本中每一個用到函式的地方。檢查 def 一行,確認參數沒有用錯。
  • 上網研究一下 file 中的 seek 函數是做什麼用的。試著運行 ri file 看看能不能從 rdoc 中學到更多。
  • 研究一下 += 這個簡寫操作符號的作用,寫一個腳本,把這個操作符號用在裡邊試一下。

使用道具 举报

回复
论坛徽章:
1056
紫蜘蛛
日期:2015-09-22 15:53:22紫蜘蛛
日期:2015-10-15 13:48:52紫蜘蛛
日期:2015-10-15 14:45:48紫蜘蛛
日期:2015-10-15 14:47:47紫蜘蛛
日期:2015-10-15 14:48:45九尾狐狸
日期:2015-09-22 15:53:22九尾狐狸
日期:2015-10-15 13:50:37九尾狐狸
日期:2015-10-15 14:45:48九尾狐狸
日期:2015-10-15 14:47:47九尾狐狸
日期:2015-10-15 14:48:45
30#
 楼主| 发表于 2013-11-18 13:47 | 只看该作者
習題 21: 函式可以傳回東西

你已經學過使用 = 給變數命名,以及將變數定義為某個數字換字串。接下來我們將讓你見證更多奇蹟。我們要示範給你的是如何使用 = 來將變數設置為「一個函式的值」。有一件事你需要特別注意,但待會再說,先輸入下面的腳本吧:

  1. def add(a, b)
  2.   puts "ADDING #{a} + #{b}"
  3.   a + b
  4. end

  5. def subtract(a, b)
  6.   puts "SUBTRACTING #{a} - #{b}"
  7.   a - b
  8. end

  9. def multiply(a, b)
  10.   puts "MULTIPLYING #{a} * #{b}"
  11.   a * b
  12. end

  13. def divide(a, b)
  14.   puts "DIVIDING #{a} / #{b}"
  15.   a / b
  16. end

  17. puts "Let's do some math with just functions!"

  18. age = add(30, 5)
  19. height = subtract(78,4)
  20. weight = multiply(90, 2)
  21. iq = divide(100, 2)

  22. puts "Age: #{age}, Height: #{height}, Weight: #{weight}, IQ: #{iq}"

  23. # A puzzle for the extra credit, type it in anyway.
  24. puts "Here is a puzzle."

  25. what = add(age, subtract(height, multiply(weight, divide(iq, 2))))

  26. puts "That becomes: #{what} Can you do it by hand?"
复制代码

現在我們創造了我們自己的加減乘除數學函式:add、subtract、multiply 以及 divide。最重要的是函式的最後一行,例如 add 的最後一行是 return a + b,它實現的功能是這樣的:

  • 我們呼叫函式時使用了兩個參數:a 和 b。
  • 我們印出這個函式的功能,這裡就是計算加法(ADDING)。
  • 接下來我們告訴 Ruby 讓他做某個回傳的動作:我們將 a+b 的值傳回 (return)。或者你可以這麼說:「我將 a 和 b 加起來,再把結果傳回。」
  • Ruby 將兩個數字相加,然後當函式結束時,它就可以將 a + b 的結果賦予給一個變數。


和本書裡的很多其他東西一樣,你要慢慢消化這些內容,一步一步執行下去,追蹤一下究竟發生了什麼。為了幫助你理解,本節的加分習題將讓你解決一個謎題,並且讓你學到點比較酷的東西。

你應該看到的結果

  1. $ ruby ex21.rb
  2. Let's do some math with just functions!
  3. ADDING 30 + 5
  4. SUBTRACTING 78 - 4
  5. MULTIPLYING 90 * 2
  6. DIVIDING 100 / 2
  7. Age: 35, Height: 74, Weight: 180, IQ: 50
  8. Here is a puzzle.
  9. DIVIDING 50 / 2
  10. MULTIPLYING 180 * 25
  11. SUBTRACTING 74 - 4500
  12. ADDING 35 + -4426
  13. That becomes:  -4391 Can you do it by hand?
  14. $
复制代码

加分習題

  • 如果你不是很確定 return 回來的值,試著自己寫幾個函式出來,讓它們傳回一些值。你可以將任何可以放在 = 右邊的東西作為一個函式的傳回值。
  • 這個腳本的結尾是一個謎題。我將一個函式的傳回值當作了另外一個函式的參數。我將它們鏈接到了一起,接跟寫數學等式一樣。這樣可能有些難讀,不過執行一下你就知道結果了。接下來,你需要試試看能不能用正常的方法實現和這個方程式一樣的功能。
  • 一旦你解決了這個謎題,試著修改一下函式里的某些部分,然後看會有什麼樣的結果。你可以有目的地修改它,讓它輸出另外一個值。
  • 最後,倒過來做一次。寫一個簡單的等式,使用一樣的函式來計算它。

這個習題可能會讓你有些頭大,不過還是慢慢來,把它當做一個遊戲,解決這樣的謎題正是寫程式的樂趣之一。後面你還會看到類似的小謎題。

使用道具 举报

回复

您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

TOP技术积分榜 社区积分榜 徽章 团队 统计 知识索引树 积分竞拍 文本模式 帮助
  ITPUB首页 | ITPUB论坛 | 数据库技术 | 企业信息化 | 开发技术 | 微软技术 | 软件工程与项目管理 | IBM技术园地 | 行业纵向讨论 | IT招聘 | IT文档
  ChinaUnix | ChinaUnix博客 | ChinaUnix论坛
CopyRight 1999-2011 itpub.net All Right Reserved. 北京盛拓优讯信息技术有限公司版权所有 联系我们 未成年人举报专区 
京ICP备16024965号-8  北京市公安局海淀分局网监中心备案编号:11010802021510 广播电视节目制作经营许可证:编号(京)字第1149号
  
快速回复 返回顶部 返回列表