文章目录
  1. 1. 课程目标
  2. 2. 课程复盘
    1. 2.1. 1. 输入、处理、输出
    2. 2.2. 2. 基本算术
    3. 2.3. 3. 控制流程
    4. 2.4. 4. 方法 Method
    5. 2.5. 5. 循环 Loop
    6. 2.6. 6. 数组 Array
  3. 3. 7. 散列 Hash
  • 给定一 Hash,输出有最大 value 的 key
    1. 1. 8. 对象 Object
      1. 1.1. 什么是对象?
      2. 1.2. 如何自订义对象?
      3. 1.3. 内建的类
      4. 1.4. 什么是常数 (Constant)?
    2. 2. 9. 档案处理
      1. 2.1. 什么是档案?
      2. 2.2. 如何开档读取和写入
    3. 3. 10. 推荐资源
  • 原文链接:编程基础练习簿

    课程目标

    这堂课将练习编程的基础逻辑,包括:

    • 输入、处理、输出
    • 基本算术
    • 控制流程
    • 函式方法
    • 循环 Loop
    • 数组 Array
    • 散列 Hash
    • 对象 Object
    • 档案 File

    课程复盘

    1. 输入、处理、输出

    • 在终端中用 用 ruby 档名 就会执行 .rb 档案

    • 标准萤幕输出(STDOUT)

      • puts 会输出变量在萤幕上,并且有换行
      • print 也会输出变量在萤幕,不会换行
    • 标准萤幕输入(STDIN)

      • gets 可以从键盘拿到输入的值。
    • 字符串 String

      • "用双引号包起来"
      • '或是用单引号'
      • "如果里面有'单引号',那就要用双引号包起来"
      • '如果里面有"双引号",那就要用单引号包起来'
      • "如果你坚持双引号里面要有双引号,那你得写成 \" 做逸出"
    • 字符串串接

      • + 的,例如 "a" + "b"
    • 字串内嵌

      • #{} 的语法可以将变量内嵌在字符串中
      • 例如 "This is #{a}"

    题目 2
    交换变数 a 和变数 b 的值

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    # 题目: 交换 a, b 变数的值
    a = 1
    b = 2
    puts "a 是 #{a}"
    puts "b 是 #{b}"
    a, b = b, a
    puts "a 应该是 2,现在是 #{a}"
    puts "b 应该是 1,现在是 #{b}"

    2. 基本算术

    • % 馀数,例如 20 % 32
      注意,整数除整数还是整数。只有浮点数运算的结果才会是浮点数。

    型别转换 (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
    2
    3
    4
    1.4.round #=> 1
    1.5.round #=> 2
    1.6.round #=> 2
    (-1.5).round #=> -2

    无条件进制 ceil

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

    无条件舍去 floor

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

    3. 控制流程

    关系运算子

    以下这些运算子,都会回传 Boolean 值,也就是 truefalse

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

    例如 "123" == "123" 会回传 true123 > 555 会回传 false

    控制流程(Control Flow)

    透过 Boolean 条件,就可以决定程序怎么执行

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    if a > b
    # ...
    elsif a == b
    # ...
    else
    # ...
    end

    逻辑运算子

    • &&
    • ||
    • ! 相反
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    # 题目: 使用者输入 x,y,z,请输出三个数中最大的数
    print "请输入一个数字x,然后按 Enter: "
    x = gets().to_i
    print "请输入一个数字y,然后按 Enter: "
    y = gets().to_i
    print "请输入一个数字z,然后按 Enter: "
    z = gets().to_i
    result = [x, y, z].max
    puts "最大的数是 #{result}"

    事例代码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    # 题目: 使用者输入 x,y,z,请输出三个数中最大的数
    print "请输入一个数字x,然后按 Enter: "
    x = gets.to_f
    print "请输入一个数字y,然后按 Enter: "
    y = gets.to_f
    print "请输入一个数字z,然后按 Enter: "
    z = gets.to_f
    if x >= y && x >= z
    result = x
    elsif y >= x && y >= z
    result = y
    else z >= x && z >= y
    result = z
    end
    puts "最大的数是 #{result}"

    4. 方法 Method

    在方法中用 return 返回一个值,让我们可以把它存进一个变量,在后面的程序来使用。

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

    如果没有写 return,该方法的最后一行的值会自动被返回。例如上述程式的 return 可以省略:

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

    5. 循环 Loop

    1
    2
    3
    4
    5
    6
    7
    # 题目: 列出 1100 之间,所有 7 的倍数
    i = 1
    while ( i <= 100 )
    i+=1
    puts i if i % 7 == 0
    end
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    # 题目: 输入一个数字 N,输出 N * N 乘法表
    print "请输入数字 N,然后按 Enter: "
    n = gets().to_i
    i = 0
    j = 0
    while ( i <= n )
    j = 0
    while ( j <= n )
    puts "#{i} x #{j} = #{i*j}"
    j += 1
    end
    i += 1
    end
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    # 输入一个数字 N,请检查是不是质数
    提示:从 2 开始到 N/2 不断去除这个数字,如果可以整除就表示不是质数
    def is_prime(n)
    i = 2
    while ( i <= n-1 )
    return false if n <= 1 || n % i == 0
    i += 1
    end
    true
    end
    print "请输入数字 N,然后按 Enter: "
    n = gets().to_i
    if is_prime(n)
    puts "这是质数"
    else
    puts "这不是质数"
    end

    6. 数组 Array

    什么是数组?

    • 一个有顺序的容器,用数字当作索引。
    • 索引从 0 开始
    • 原则上里面的元素,数据类型最好都是一样的,例如都存整数,或是都存字符串。

    以下就是一个数组,用中括号前后包住,用逗号分隔:

    array = [5,1,2,3,5]
    数组里面还可以放数组,这样会变成二维数组:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    array = [
    [1,2,3],
    [4,5,6],
    [7,8,9]
    ]
    array[0][0] # 这是 1
    array[0][1] # 这是 2
    array[2][2] # 这是 9

    如何走访数组?

    for 方法:

    1
    2
    3
    for i in array
    puts i
    end

    在 Ruby 中更习惯用 each 方法:

    1
    2
    3
    array.each do |i|
    puts i
    end

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

    1
    2
    3
    4
    5
    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

    字符串与数组

    编程语言内部实作字符串的方式,就是将一个一个字符用数组连结起来。这就是为什么字符串也可以用数字索引操作:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    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"

    示例代码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    # 给定一阵列内含数字,输出最大值
    def find_max(array)
    array.max
    end
    arr = [8, 12, 36, 53, 9, 75, 3, 71, 59, 88]
    max = find_max(arr)
    puts "Max is #{max}" # 应该是 88
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    # 使用者不断输入数字存进 Array,最后输出总和、平均、最大值、最小值
    # 注意:如果没有输入任何东西直接按 Enter,gets 仍会读到一个换行符号,这是一个萤幕不可见的字符,在字符串中用 "\n"表示。
    arr = []
    while (true)
    print "请输入数字,结束请直接按 Enter: "
    user_input = gets
    if user_input == "\n"
    break
    else
    arr << user_input.to_i
    end
    end
    sum = 0
    arr.each { |i| sum += i }
    average = sum / arr.size
    puts arr.to_s
    puts "总和是 #{sum}"
    puts "平均是 #{average}"
    puts "最大值是 #{arr.max}"
    puts "最小值是 #{arr.min}"
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    # 建构一个阵列有一百个的元素,内容是 0, 1, 4, 9, 16, 25...... 每个元素是该索引的平方
    # 例如 N = 1,则数组是 [0]
    # 例如 N = 3,则数组是 [0,1,4]
    arr = []
    print "请输入数字 N,然后按 Enter: "
    n = gets().to_i
    (0..n).each_with_index do |object, index|
    arr << index ** 2
    end
    puts arr.to_s
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    # 建构一个阵列有一百个的元素,内容是 0, 1, 4, 9, 16, 25...... 每个元素是该索引的平方
    arr = []
    print "请输入数字 N,然后按 Enter: "
    n = gets.to_i
    i = 0
    while i < n
    arr << i*i
    i += 1
    end
    puts arr.to_s
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    # 给定一数组内含数字,输出另一个数组只包含偶数,然后排序并去除重复的数字
    # Hint: 可用 arr.sort 排序,和 arr.uniq 去除重复
    def filter_even(arr)
    new_arr = []
    arr.each do |i|
    if i % 2 == 0
    new_arr << i
    end
    end
    new_arr.sort.uniq
    end
    arr = [7, 68, 42, 46, 9, 91, 77, 46, 86, 1]
    puts "#{filter_even(arr)}" # 应该是 [42, 46, 68, 86]
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    # 给定一数组内含数字,请实作选择排序法进行排序。
    # 提示: 新建立一个空数组,然后走访本来的数组检查每一个元素,如果符合条件就塞进(push)进新的数组。
    # https://zh.wikipedia.org/wiki/选择排序
    def insertion_sort(arr)
    (0...arr.length).each do |i|
    min, index = arr[i], i
    (i...arr.length).each { |j| min, index = arr[j], j if arr[j] < min }
    arr[i], arr[index] = arr[index], arr[i]
    end
    arr
    end
    arr = [7, 68, 42, 46, 9, 91, 77, 46, 86, 1]
    answer = insertion_sort(arr)
    puts answer.to_s # 应该是 [1, 7, 9, 42, 46, 46, 68, 77, 86, 91]
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    # 给定一阵列内含数字,请输出 0~9 中不见的数字。例如 arr = [2,2,1,5,8,4],输出 [0,3,6,7,9]
    def find_missing(arr)
    missing_num = []
    (0..9).each do |i|
    missing_num << i unless arr.include?(i)
    end
    missing_num
    end
    answer = find_missing( [2,2,1,5,8,4] )
    puts answer.to_s # 应该是 [0,3,6,7,9]

    7. 散列 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?

    1
    2
    3
    4
    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

    示例代码:

    给定一 Hash,输出有最大 value 的 key

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    def find_max(hash)
    hash.each do |key, value|
    return key if value == hash.values.max
    end
    end
    h = {
    "a" => 71,
    "b" => 38,
    "c" => 21,
    "d" => 80,
    "e" => 10
    }
    answer = find_max(h)
    puts "有最大 value 的是 #{answer}" # 应该是 d
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    # 给定一 Hash,输出 value 是偶数的 keys
    def find_even_keys(hash)
    result = []
    hash.each do |key, value|
    result << key if value % 2 == 0
    end
    result
    end
    h = {
    "a" => 71,
    "b" => 38,
    "c" => 21,
    "d" => 80,
    "e" => 10
    }
    answer = find_even_keys(h)
    puts "有偶数 value 的 keys 有 #{answer}" # 应该是数组 [b,d,e]
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    # 计算一个阵列中各个元素的出现频率
    def count(arr)
    h = {}
    arr.each do |i|
    a = arr.count(i)
    h[i] = a
    end
    return h # 回传一个 hash
    end
    arr = ["a", "d", "d", "c", "b", "c", "c", "c", "d", "d", "e", "e", "e", "d", "a", "c", "e", "a", "d", "e"]
    answer = count(arr)
    puts answer # 答案应该是 {"a"=>3, "d"=>6, "c"=>5, "b"=>1, "e"=>5}
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    # 给定一个散列 Hash,请根据指定的键值进行过滤和排序
    arr = [
    { "name" => "Peter", "age" => 30 },
    { "name" => "John", "age" => 15 },
    { "name" => "David", "age" => 45 },
    { "name" => "Steven", "age" => 22 },
    { "name" => "Vincent", "age" => 6 },
    ]
    def filter(arr)
    result_arr = []
    arr.each do |h|
    if h["age"] >= 18
    result_arr.push(h)
    end
    end
    result_arr.sort_by { |i| i["age"] }
    end
    puts "所有成年人,并由小到大: #{filter(arr)}"
    # 答案应该是
    #[
    # { "name" => "Steven", "age" => 22 },
    # { "name" => "Peter", "age" => 30 },
    # { "name" => "David", "age" => 45 }
    #]

    8. 对象 Object

    什么是对象?

    对象(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 类:

    1
    2
    3
    4
    5
    6
    7
    8
    class Car
    attr_accessor :color # 这会定义属性 @color
    def run
    puts "This #{@color} car is running"
    end
    end

    有了类,就可以用 new 来产生出对象:

    1
    2
    3
    4
    5
    6
    7
    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)都是一种常数,所以这也是为什么命名类的时候一律都是大写开头。

    9. 档案处理

    什么是档案?

    所有正在执行中的程序和数据,都是放在内存(memory)之中。内存存取速度快,但是容量小而且一关机数据就不见了。
    档案则是存在硬盘上,容量大但存取慢,但是关机重来还是继续保存着。

    所谓的读取档案就是将数据从硬盘放进内存里面,让程序可以操作。反之写入档案则是将内存的数据写入硬盘进行保存。

    如何开档读取和写入

    使用 Ruby 的 File API

    读取档案内容:

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

    写入档案

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

    这样就会写入 bar.txt,内容是 :

    1
    2
    aaa
    bbb

    示例代码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    # 请打开 wordcount.txt,计算每个单字出现的次数
    doc = File.read("wordcount.txt")
    words = doc.downcase.scan(/\w+/)
    result = {}
    words.each do |i|
    result[i] = words.count(i)
    end
    puts result
    1
    2
    3
    4
    5
    6
    7
    8
    9
    # 请打开 wordcount.txt,计算每个单字出现的次数
    doc = File.read("wordcount.txt")
    words = doc.downcase.scan(/\w+/)
    result = words.each_with_object(Hash.new(0)) { |word, hash| hash[word] += 1 }
    puts result
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    # 简易 Todo 代办事项应用
    require 'byebug'
    text = File.read("todos.txt")
    todos = []
    text.each_line do |line|
    todos << line.chomp
    end
    todos.each_with_index do |todo, index|
    puts "#{index}: #{todo}"
    end
    while (true)
    print "请输入指令 1. add 2. remove 3. save,然后按 Enter: "
    command = gets.chomp
    if command == "add"
    print "请输入代办事项: "
    todos << gets
    elsif command == "remove"
    print "请输入要删除的编号: "
    choose = gets().to_i
    todos.delete_at(choose)
    elsif command == "save"
    puts "存盘离开"
    File.open("todos.txt", "w+") do |f|
    for a in todos
    f << a + "\n"
    end
    end
    todos.each_with_index do |todo, index|
    puts "#{index}: #{todo}"
    end
    break;
    else
    puts "看不懂,请再输入一次"
    end
    end

    10. 推荐资源

    如果这教程对你来说偏简单,推荐你接下来买一本 Ruby 基础教程, 人民邮电,如果你要找工作面试,请尽量念完这一本。

    如果想继续练习基础编程,或是面试的公司有考算法,请练习以下的题库网站(请练习 Easy 程度程度即可,更高难度需要学完数据结构课程)

    如果这教程对你来说还是偏难太过抽象,建议你可以找啊哈磊的小学生坐在马桶上都能看懂的编程入门书,会用更浅显的对话和例子,用一整本书的长度来讲这教程的内容。

    文章目录
    1. 1. 课程目标
    2. 2. 课程复盘
      1. 2.1. 1. 输入、处理、输出
      2. 2.2. 2. 基本算术
      3. 2.3. 3. 控制流程
      4. 2.4. 4. 方法 Method
      5. 2.5. 5. 循环 Loop
      6. 2.6. 6. 数组 Array
    3. 3. 7. 散列 Hash
  • 给定一 Hash,输出有最大 value 的 key
    1. 1. 8. 对象 Object
      1. 1.1. 什么是对象?
      2. 1.2. 如何自订义对象?
      3. 1.3. 内建的类
      4. 1.4. 什么是常数 (Constant)?
    2. 2. 9. 档案处理
      1. 2.1. 什么是档案?
      2. 2.2. 如何开档读取和写入
    3. 3. 10. 推荐资源