Link Search Menu Expand Document

程式設計基礎練習

1. 前言準備

這堂課將練習編程的基礎邏輯,包括:

  1. 輸入、處理、輸出
  2. 基本算術
  3. 控制流程
  4. 函式方法
  5. 循環
  6. 陣列 Array
  7. 雜湊 Hash
  8. 物件 Object
  9. 檔案 File

難度大約是電腦本科生大一程度,也是程序員一定會的基本題。

請在 Github 上 Fork 這個專案 https://github.com/ihower/programming-exercise,然後 git clone 回去:

git clone git@github.com:your_name/programming-exercise.git cd programming-exercise.git

其中每個 .rb 檔案就是一個小題目需要完成,用 ruby 檔名 就會執行,例如:

ruby 01-hello.rb

就會執行第一個題目。請完成一題就 commit 一次,最後送 Pull Request 交作業。

題目看起來很多,但是其實每一題的答案只是幾行代碼而已,不用想太複雜。

2. 輸入、處理、輸出

題目 1

輸入名字,輸出 “Hello, 名字”

成功畫面:

image

題目 2

交換變數 a 和變數 b 的值

提示說明

標準螢幕輸出(STDOUT)

  • puts 會輸出變數在螢幕上,並且有換行
  • print 也會輸出變數在螢幕,不會換行

標準螢幕輸入(STDIN)

gets 可以從鍵盤拿到輸入的值。

什麽是變數?

讓電腦(記憶體)記住一個值(value),就像一個箱子📦 = 是賦值運算符(assignment operator),是指派右邊的值到左邊的變數

在 Ruby 中,一律用小寫開頭命名變數,並且習慣用 _ 底線來分隔單字,例如 your_name

字符串 String

  • "用雙引號包起來"
  • '或是用單引號'
  • "如果裡面有'單引號',那就要用雙引號包起來"
  • '如果裡面有"雙引號",那就要用單引號包起來'
  • "如果你堅持雙引號裡面要有雙引號,那你得寫成 \" 做逸出"

字符串串接

+ 的,例如 "a" + "b"

字串內嵌

#{} 的語法可以將變數內嵌在字符串中

例如 "This is #{a}"

如何註解?

在 Ruby 中用 # 註解。# 後面的字都不會執行

irb 互動式 Ruby 環境

執行 irb 可以進入 Ruby 的互動式環境,可以快速地練習進行語法實驗

image

3. 基本算術

題目 3

輸入直角三角形的底和高,輸出三角形的面積(底乘高除二)

題目 4

輸入有多少片比薩餅和多少人,輸出每人可以分到幾片,以及剩下幾片

提示說明

什麽是數據類型?

每個值都屬於一個數據類型,例如有:

  • 字符串 String
  • 整數 Integer (不可以有小數)
  • 浮點數 Float (可以存放小數)
  • 陣列 Array
  • 雜湊 Hash

整數和浮點數

  • 沒有小數點的就是整數,例如 123
  • 有小數點的就是浮點數,例如 123.012.3

有哪些基本算術?

  • +
  • -
  • *
  • /
  • % 餘數,例如 20 % 32

註意,整數除整數還是整數。只有浮點數運算的結果才會是浮點數。

例如 10/4 會是 2。要 10.0 / 4 才是 2.5

型別轉換 (Type Conversion)

字符串和數字是不能直接相加的,會出現 TypeError 的錯誤,需要先做型別轉換:

  • 透過 to_i 可以將字符串轉成整數,例如 "123".to_i 變成 123
  • 透過 to_f 可以將整數轉乘浮點數,例如 123.to_f 變成 123.0
  • 透過 to_s 可以轉成字符串,例如 123.to_s 變成 "123"

注意,在 gets 中,拿到的是「字符串」,即使你輸入的是數字。所以如我要做數字計算,記得先做型別轉換。

在 Rails controller 中,params 的值也都是字符串。

浮點數常用方法

四捨五入 round

1.4.round      #=> 1
1.5.round      #=> 2
1.6.round      #=> 2
(-1.5).round   #=> -2

無條件進制 ceil

1.2.ceil      #=> 2
2.0.ceil      #=> 2
(-1.2).ceil   #=> -1
(-2.0).ceil   #=> -2

無條件捨去 floor

1.2.floor      #=> 1
2.0.floor      #=> 2
(-1.2).floor   #=> -2
(-2.0).floor   #=> -2

更多請參考 Ruby Float API

4. 控制流程

題目 5

輸入體重和身高,輸出身體質量指數(BMI)和建議文字

BMI 公式為 bmi = ( 體重 / (身高x身高) ),單位是公斤和公尺

  • 如果 BMI < 18.5,顯示過輕
  • 如果 BMI >= 24,顯示過重
  • 如果 BMI 介於 18.5 ~ 24,顯示正常

題目 6

輸入一個數字 x,請判斷是否正數、零或負數,以及是不是偶數

題目 7

輸入數字 x, y, z,請根據以下的判斷輸出結果

  • 當 x < 0 輸出 “A”
  • 當 x > 0
    • 當 y > 0
      • 當 z > 0 輸出 “B”
      • 當 z < 0 輸出 “C”
    • 當 y < 0
      • 當 z > 0 輸出 “D”
      • 當 z < 0 輸出 “E”

題目 8

輸入數字 x, y, z,請輸出三個數中最大的數

提示說明

布林 Boolean 數據類型

布林 Boolean 數據類型只有兩種值: truefalse

關系運算子

以下這些運算子,都會回傳 Boolean 值,也就是 truefalse

  • ==
  • !=
  • <
  • >
  • <=
  • >=

例如 "123" == "123" 會回傳 true123 > 555 會回傳 false

控制流程(Control Flow)

透過 Boolean 條件,就可以決定程序怎麽執行

if 9 > 3
  # 這段會執行
else
  # 這段不會執行
end

更多條件的話:

if a > b
  # ...
elsif a == b
  # ...
else
  # ...
end

邏輯運算子

&&

  • ab 都是 true 時,a && b 回傳 true
  • ab 任一個是 false 時,a && b 回傳 false

||

  • ab 任一個是 true 時,a || b 回傳 true
  • ab 都是 false 時,a || b 回傳 false

! 相反

如果 atrue,那 !a 就是 false。反之亦然。

透過 &&||,就可以結合不同條件:

if ( 9 > 3 ) && ( 6 == 6)
 # ...
elsif ( 2 > 1 ) || ( 5 < 1 )
 # ...
end

Boolean 轉型

條件不需要一定要轉成 Boolean 型態,在 Ruby 之中只要不是 falsenil,都會自動判斷成 true

例如:

if "foobar"
  # 會執行
end

if 123
  # 會執行
end
if 0
  # 會執行
end
if ""
  # 會執行
end
if []
  # 會執行
end
if nil
  # 不會執行
end

5. 方法 Method

題目 9

將題目 3 計算直角三角形面積,改成調用方法

題目 10

將題目 8 找出三個數中最大的數,改成調用方法

提示說明

一些需要重復被執行的代碼,可以透過定義函式包裝起來,方便之後繼續調用

有些程式語言的術語用 Function 函式,在 Ruby 中則習慣稱之為 Method 方法。

如何定義方法和調用

def

def hello
  puts "Hello"
end

hello()  # 這樣會調用 hello 方法

hello    # 在 Ruby 語言中 () 可以省略

如何帶入參數

def hello(name)
  puts "Hello, #{name}"
end

hello("ihower")   # 這樣會調用 hello 方法

hello "ihower"    # 在 Ruby 語言中 () 可以省略

回傳值

def get_hello(name)
  return "Hello, #{name}"
end

str = get_hello("ihower")
puts str

在方法中用 return 返回一個值,讓我們可以把它存進一個變數,在後面的程序來使用。

如果沒有寫 return,該方法的最後一行的值會自動被返回。例如上述程式的 return 可以省略:

def get_hello(name)
  "Hello, #{name}"
end

6. 循環 Loop

題目 11

輸出 1 到 100 之間,所有 7 的倍數。

題目 12

求 1~100 所有偶數的和

題目 13

輸入一個數字 N,輸出 N * N 乘法表,例如輸入 12,輸出

0 x 0 = 0
0 x 1 = 0
......
12 x 11 = 132
12 x 12 = 144

題目 14

輸入一個數字 N,請檢查是不是質數

提示:從 2 開始到 N/2 不斷去除這個數字,如果可以整除就表示不是質數

題目 15

猜數字游戲:程序會先產生一個隨機數,然後用戶開始猜這個數字。

程序會針對猜的數字回答太高、太低或猜中,猜中後程序就會終止。

提示說明

什麽是循環?

就是可以重復執行某段程式碼,直到滿足某個條件。

x = 10
while x <= 10
  puts x
  x = x + 1
end

這個 while 就會一直循環,直到 x 超過 10 不滿足條件,才會離開循環。

注意,如果 while 的終止條件沒有滿足,會變成無窮循環,程序就不會終止了。

提前中斷

while 循環中,用 break 關鍵字,會提前中斷循環

7. 陣列 Array

題目 16

給定一陣列內含數字,輸出最大值

題目 17

使用者不斷輸入數字存進陣列 Array,最後輸出總和、平均、最大值、最小值

注意:如果沒有輸入任何東西直接按 Enter,gets 仍會讀到一個換行符號,這是一個螢幕不可見的字符,在字符串中用 "\n"表示。

題目 18

輸入數字 N,建構一個陣列有 N 的元素,內容是 0, 1, 4, 9, 16, 25…… 每個元素是該索引的平方。

例如 N = 1,則陣列是 [0] 例如 N = 3,則陣列是 [0,1,4]

題目 19

給定一陣列內含數字,輸出另一個陣列只包含偶數

提示: 新建立一個空陣列,然後走訪本來的陣列檢查每一個元素,如果符合條件就塞進(push)進新的陣列。

題目 20

承上題,請排序並去除重復的數字

提示:可用 arr.sort 健行排序,以及 arr.uniq 去除重復

題目 21

Ruby 的 Array 陣列是內建了 .sort 方法可排序,我們這裡練習自行實作一個排序算法。

給定一陣列內含數字,請實作選擇排序法進行排序。

假設陣列有N個元素,先從第一個元素走訪到最後一個,找到最小的那一個交換到第一個位置。

接著從第二個元素走訪到最後一個,找到最小的元素,交換到第二個位置。以此類推,最後就會得到一個從小到大的陣列。

題目 22

給定一陣列內含數字,請輸出 0~9 中不見的數字。例如 arr = [2,2,1,5,8,4],輸出 [0,3,6,7,9]

提示說明

什麽是陣列?

  • 一個有順序的容器,用數字當作索引。
  • 索引從 0 開始
  • 原則上裡面的元素,數據類型最好都是一樣的,例如都存整數,或是都存字符串。

以下就是一個陣列,用中括號前後包住,用逗號分隔:

array = [5,1,2,3,5]

陣列裡面還可以放陣列,這樣會變成二維陣列:

array = [
  [1,2,3],
  [4,5,6],
  [7,8,9]
]

array[0][0] # 這是 1
array[0][1] # 這是 2
array[2][2] # 這是 9

如何走訪陣列?

for 方法:

for i in array
  puts i
end

在 Ruby 中更習慣用 each 方法:

array.each do |i|
  puts i
end

如果走訪時,需要索引值,可以改用 each_with_index 方法:

array.each_with_index do |i, j|
  puts i
  puts j   # j 從 0 開始數
end

如何操作陣列?

array.size 會回傳陣列長度

array[1] 會讀取第二個元素

array[1] = 999 會更改第二的元素的值

array[99999] 讀取一個不存在的值,會回傳 nil

array[0]array.first 是陣列的第一個元素

array[-1]array.last 是陣列的最後一個元素

array.push(999)array << 999 會新增一個元素 999 到陣列的最後面

array.unshift(999) 會新增一個元素 999 到陣列的最前面

array.delete_at(i) 會刪除索引值在 i 的元素

更多 Ruby 的 Array 方法請參考 Ruby Array API

字符串與陣列

程式語言內部實作字符串的方式,就是將一個一個字符用陣列連結起來。這就是為什麽字符串也可以用數字索引操作:

str = "ABCDEFG"

str[0]  # 得到 "A"

str[1] = "x"
str   # 變成 "AxCDEFG"

str.split("") # 用 "" 拆開字符串,得到陣列 ["A", "B", "C", "D", "E", "F", "G"]

["x", "y", "z"].join(" ") # 用 " " 串接成字符串,得到 "x y z"

8. 雜湊 Hash

題目 23

給定一雜湊 Hash,輸出有最大值(value)的鍵(key)

題目 24

給定一雜湊 Hash,輸出值(value)是偶數的鍵(keys)

注意,答案是一個陣列

題目 25

計算一個陣列中各個元素的出現頻率

題目 26

給定一個雜湊 Hash,請根據指定的鍵值進行過濾和排序

提示說明

什麽是雜湊 Hash?

用鍵(key)當作索引的容器,例如

h = { "a" => 123, "b" => 456 }

h["a"] 就是 123

h["b"] 就是 456

h["qweqkleklwqen"] 如果讀取一個不存在的key,結果會是 nil

h["new_key"] = 123 如果本來沒有這個 key,就會直接新增一組 key-value

如何走訪雜湊 Hash?

h.each do |key, value|
  puts key
  puts value
end

常用方法

h.keys 會回傳一個陣列包括所有 keys

h.values 會回傳一個陣列包括所有 values

h.merge(h2) 會合並雜湊 h2 到雜湊 h

更多 Ruby 的 Hash 方法請參考 Ruby Hash API

9. 物件 Object

題目 27

請自訂一個 Person 物件,屬性有 first_name, last_name,然後定義一個方法是 greet 會輸出 "Hi, <first_name> <last_name>"

提示說明

什麽是物件?

物件(Object)也是一種數據類型,這種數據除了擁有屬性,也有方法可以調用。

在 Ruby 之中,其實每種數據類型,例如 String、Integer、Float、Array、Hash 等等,也都是物件(Object)。

例如字符串的 size 方法和 split 方法:

"abc".size 會回傳 3

"a,b,c,d".split(",") 會回傳陣列 ["a", "b", "c", "d"]

其中 . 就是調用方法的意思。

如何自訂義物件?

需要透過類 Class 自訂義 Object。Class 就像一種樣板,定義出同一種類型的物件有哪些共同的屬性和方法。

例如以下的代碼定義了 Car 類別:

class Car

  attr_accessor :color  # 這會定義屬性 @color

  def run
    puts "This #{@color} car is running"
  end
end

有了類,就可以用 new 來產生出物件:

car1 = Car.new
car1.color = "red"
car1.run

car2 = Car.new
car2.color = "blue"
car2.run

內建的類別

剛剛說在 Ruby 之中,每種數據類型,例如 String、Integer、Float、Array、Hash 等等,也都是物件(Object)。

這些物件,其實也都是從類別產生出來的,例如

"foobar".class 會回傳 String 是個類別

String.new("foobar") 等同於 "foobar"

[1,2,3].class 會回傳 Array 是個類別

Array.new([1,2,3]) 等同於 [1,2,3]

不過因為這些數據類型太常用了,所以就不需要寫 .new 來產生,直接用符號表示就可以了。

什麽是常數 (Constant)?

在 Ruby 中,大寫開頭的變數又叫做常數,指一個數值固定不變的常量。

例如:

STATUS = ["pending", "confirmed"]

Pi = 3.1415926

所有的類別(Class)都是一種常數,所以這也是為什麽命名類別的時候一律都是大寫開頭。

10. 檔案處理

題目 28

請打開 wordcount.txt,計算每個單字出現的次數

題目 29

請打開 todos.txt,其中每一行就是一個待辦事項。 請編寫一個程序,可以新增和刪除代辦事項,最後可以存盤離開。 重新執行這只程序,可以繼續上次的代辦事項。

注意:gets 讀到的字符串,最後都會有一個換行符號,這是一個螢幕不可見的字符,在字符串中用 "\n"表示。用 chomp 方法可以去除字符串前後的空白和換行符號 "\"

提示說明

什麽是檔案?

所有正在執行中的程序和數據,都是放在記憶體(memory)之中。記憶體存取速度快,但是容量小而且一關機數據就不見了。 檔案則是存在硬盤上,容量大但存取慢,但是關機重來還是繼續保存著。

所謂的讀取檔案就是將數據從硬盤放進記憶體裡面,讓程序可以操作。反之寫入檔案則是將記憶體的數據寫入硬盤進行保存。

如何開檔讀取和寫入

使用 Ruby 的 File API

讀取檔案內容:

file = File.open("foo.txt")
doc = file.read("foo.txt")

寫入檔案:

File.open("bar.txt", "w+") do |f|
  f << "aaa"
  f << "\n"
  f << "bbb"
end

這樣就會寫入 bar.txt,內容是 :

aaa
bbb

11. 推薦資源

如果這教程對你來說偏簡單,推薦你接下來買一本 Ruby 基礎教程, 人民郵電,如果你要找工作面試,請盡量念完這一本。

如果想繼續練習基礎編程,或是面試的公司有考算法,可以練習以下的題庫網站(請練習 Easy 程度程度即可,更高難度需要學完資料結構課程)

如果這教程對你來說還是偏難太過抽象,建議你可以找啊哈磊的小學生坐在馬桶上都能看懂的編程入門書,會用更淺顯的對話和例子,用一整本書的長度來講這教程的內容。

其他參考資料


Copyright © 2010-2021 Wen-Tien Chang All Rights Reserved.