有哪些值得推荐的 Ruby 学习流程? - 知乎

很多人是: 先学 Rails 做项目, 然后再来学 Ruby…

[@RednaxelaFX](//www.zhihu.com/people/a06cfb38e37dac1658e6457df4d7f032) 的学习流程就不一样, 他是先看 Ruby 的 parse.y 来速成语法, 然后再用来爬小黄图或者汉化日文游戏, 同时看看各种 VM 的实现例如 JRuby 啦, Rubinius 啦, MRuby 啦, IronRuby 啦…

正统点的学习法是找一本书看, 例如 Programming Ruby 1.9 & 2.0 , 但是可能会慢一点但适合没什么编程基础的. 实践派可能会喜欢看 Learn Ruby with the Neo Ruby Koans

对有前途的小姑娘小伙我会推荐看上古邪书 Why’s (Poignant) Guide to Ruby

有点编程经验的, 直接看一张网页 Ruby.on-page.net 或者比着 code roseta 做就可以了

Ruby Quiz 每周一题全看一遍你会受益良多, 在学习了基础语法后可以开始看

旁门左道的例如我, 在各问答网站和社区上找 Code Golf 问题用 Ruby 回答, 或者关注知乎的 Ruby 话题。

tzx|最后:


By David Flanagan, Yukihiro Matsumoto
Publisher: O'Reilly Media
Final Release Date: January 2008
Pages: 448

Chapter 1 Introduction

A Tour of Ruby

Try Ruby

About This Book

This chapter concludes with a heavily commented extended example demonstrating a nontrivial Ruby program.

The chapters that follow cover Ruby from the bottom up: @
  • Chapter 2 covers the lexical and syntactic structure of Ruby, including basic issues like character set, case sensitivity, and reserved words.
  • Chapter 3 explains the kinds of data—numbers, strings, ranges, arrays, and so on — that Ruby programs can manipulate, and it covers the basic features of all Ruby objects.
  • Chapter 4 covers primary expressions in Ruby—literals, variable references, method invocations, and assignments—and it explains the operators used to combine primary expressions into compound expressions.
  • Chapter 5 explains conditionals, loops (including blocks and iterator methods), exceptions, and the other Ruby expressions that would be called statements or control structures in other languages.
  • Chapter 6 formally documents Ruby’s method definition and invocation syntax, and it also covers the invocable objects known as procs and lambdas. This chapter includes an explanation of closures and an exploration of functional programming techniques in Ruby.
  • Chapter 7 explains how to define classes and modules in Ruby. Classes are fundamental to object-oriented programming, and this chapter also covers topics such as inheritance, method visibility, mixin modules, and the method name resolution algorithm.
  • Chapter 8 covers Ruby’s APIs that allow a program to inspect and manipulate itself, and then demonstrates metaprogramming techniques that use those APIs to make programming easier. The chapter includes an example of domain-specific language.
  • Chapter 9 demonstrates the most important classes and methods of the core Ruby platform with simple code fragments. This is not a reference but a detailed overview of the core classes. Topics include text processing, numeric computation, collections (such as arrays and hashes), input/output, networking, and threads. After reading this chapter, you’ll understand the breadth of the Ruby platform, and you’ll be able to use the ri tool or an online reference to explore the platform in depth.
  • Chapter 10 covers the top-level Ruby programming environment, including global variables and global functions, command-line arguments supported by the Ruby interpreter, and Ruby’s security mechanism.

A Sudoku Solver in Ruby

code @
class Puzzle
    ASCII = ".123456789"                                    # external rep
    BIN = "\000\001\002\003\004\005\006\007\010\011"        # internal rep
    def initialize(lines)
        if (lines.responds_to? :join)
            s = lines.join
        else
            s = lines.dup
        end
        s.gsub!(/\s/, "")                                   # strip all whitespaces

        raise Invalid, "Grid is the wrong size" unless s.size == 81
        if i = s.index(/[^123456789\.]/)
            raise Invalid, "Illegal character #{s[i,1]} in puzzle"
        end
        s.tr!(ASCII, BIN)                                   # external rep -> internal rep
        @grid = s.unpack('c*')                              # unpack bytes to array of nums
        raise Invalid, "Initial puzzle has duplicates" if has_duplicates?
    end

    def dup
        copy = super
        @grid = @grid.dup
        copy                                                # return the copied
    end

    def [](row,col)
        @grid[row*9+col]
    end

    def []=(row, col, newvalue)
        unless (0..9).include? newvalue
            raise ...
        end
        @grid[row*9+col] = newvalue
    end

    BoxOfIndex = [
        0,0,0,1,1,1,2,2,2,
        0,0,0,1,1,1,2,2,2,
        0,0,0,1,1,1,2,2,2,
        3,3,3,4,4,4,5,5,5,
        3,3,3,4,4,4,5,5,5,
        3,3,3,4,4,4,5,5,5,
        6,6,6,7,7,7,8,8,8,
        6,6,6,7,7,7,8,8,8,
        6,6,6,7,7,7,8,8,8
    ].freeze

    def each_unknown
        0.upto 8 do |row|
            0.upto 8 do |col|
                index = row*9+col
                next if @grid[index] != 0 # Move on if we know the cell's value
                box = BoxOfIndex[index]
                yield row, col, box
            end
        end
    end

    def has_duplicates?
        # uniq! returns nil (false) if all the elements in an array are unique.
        # So if uniq! returns something then the board has duplicates.
        0.upto(8) {|row| return true if rowdigits(row).uniq! }
        0.upto(8) {|col| return true if coldigits(col).uniq! }
        0.upto(8) {|box| return true if boxdigits(box).uniq! }
        false
    end

    AllDigits = [1, 2, 3, 4, 5, 6, 7, 8, 9].freeze
    def possible(row, col, box)
        # +: array concat, -: set diff
        AllDigits - (rowdigits(row) + coldigits(col) + boxdigits(box))
    end

    private # these below methods are private

    # all digits in the row-th row
    def rowdigits(row)
        # Array subtraction is set difference, with duplicate removal.
        @grid[row*9,9] - [0]
    end

    # 可以看到,“+” 是 array concat,“-” 是 set diffrence (no dup)
    def coldigits(col)
        result = []
        # Start with an empty array
        col.step(80, 9) {|i|
            v = @grid[i]
            # Get value of cell at that index
            result << v if (v != 0) # Add it to the array if non-zero
        }
        result              # 这是不是有 dup 啊……
    end

    # 每个 box 左上角的 index
    BoxToIndex = [0, 3, 6, 27, 30, 33, 54, 57, 60].freeze

    def boxdigits(b)
        # Convert box number to index of upper-left corner of the box.
        i = BoxToIndex[b]
        # Return an array of values, with 0 elements removed.
        [
            @grid[i], @grid[i+1], @grid[i+2],
            @grid[i+9], @grid[i+10], @grid[i+11],
            @grid[i+18], @grid[i+19], @grid[i+20]
        ] - [0]
    end
end # This is the end of the Puzzle class
# An exception of this class indicates invalid input,
class Invalid < StandardError
end

class Impossible < StandardError
end

def Sudoku.scan(puzzle)
    unchanged = false
    until unchanged
        unchanged = true            # Assume no cells will be changed this time
        rmin,cmin,pmin = nil        # Track cell with minimal possible set
        min = 10                    # More than the maximal number of possibilities
        puzzle.each_unknown do |row, col, box|
            p = puzzle.possible(row, col, box)
            case p.size
            when 0
                raise Impossible
            when 1
                puzzle[row,col] = p[0]  # set to the only possibility
                unchanged = false       # note that we've made a change
            else    # >1
                if unchanged && p.size < min
                    min = p.size
                    rmin, cmin, pmin = row, col, p
                end
            end
        end
        return rmin, cmin, pmin         # 找到了 puzzle 最容易突破的点:这里可能性最少
    end

    def Sudoku.solve(puzzle)
        puzzle = puzzle.dup
        r,c,p = scan(puzzle)
        return puzzle if r == nil
        p.each do |guess|
            puzzle[r,c] = guess
            begin
                return solve(puzzle)
            rescue Impossible
                next
            end
        end
        raise Impossible
    end
end

Chapter 2 The Structure and Execution of Ruby Programs

Lexical Structure

seq of tokens:

Syntactic Structure

File Structure

__END__ @
#!/usr/bin/ruby -w              # shebang comment
# -*- coding: utf-8 -*-         # coding comment, for Emacs. (Vim? use "# vi: set fileencoding=utf-8")
require 'socket'                # load libs

    ...                         # program code goes here

__END__                         # mark end of code
    ...                         # program data goes here

Program Encoding

Better utf-8

Program Execution

utf-8 @
  • ruby -Ku, utf-8
  • # coding: utf-8
  • 这个完全是 shell 的事情……

    ruby -E utf-8                   # Encoding name follows -E
    ruby -Eutf-8                    # The space is optional
    ruby --encoding utf-8           # Encoding following --encoding with a space
    ruby --encoding=utf-8           # Or use an equals sign with --encoding

    ruby 还可以 register at_exit 函数(和 C 一样)

Chapter 3 Datatypes and Objects

Numbers

Text

Arrays

Hashes

Ranges

Symbols

True, False, and Nil

o == nil
o.nil?

Objects

Chapter 4 Expressions and Operators

Literals and Keyword Literals

Variable References

Constant References

Method Invocations

Assignments

Operators

Ruby operators, by precedence (high to low), with arity (N), associativity (A), and definability (M)

Chapter 5 Statements and Control Structures

esoteric

    英 [,esə'terɪk; ,iːsə-] 美 [,ɛsə'tɛrɪk]

    adj. 秘传的;限于圈内人的;难懂的

Conditionals

Loops

Iterators and Enumerable Objects

Blocks

# The block takes two words and "returns" their relative order
words.sort! {|x,y| y <=> x }
array.collect do |x|
    next 0 if x == nil          # return x
    next x, x*x                 # return two values,但这两个 value 其实是一个 [],是一个元素
end

# 所以上面的操作过后,list 的元素个数不变。

# or without next
array.collect do |x|
    if x == nil
        0
    else
        [x, x*x]
    end
end

Altering Control Flow

Exceptions and Exception Handling

The Ruby Exception Class Hierarchy

define new exception classes

class MyError < StandardError; end

raise "msg"
raise RuntimeError, "msg"
raise RuntimeError.new("msg")
raise RuntimeError.exception("msg")
raise RuntimeError

Handling Exceptions with rescue

begin
    ...
rescue
    ...
end

begin                                       # Handle exceptions in this block
    x = factorial(-1)                       # Note illegal argument
rescue => ex                                # Store exception in variable ex
    puts "#{ex.class}: #{ex.message}"       # Handle exception by printing message
end                                         # End the begin/rescue block

Handling exceptions by type

rescue Exception
rescue ArgumentError => e
# 可以 rescue 几遍

BEGIN and END

和 awk 一样

BEGIN {
    ...
}

END {
    ...
}

10.times {BEGIN { puts "loop" }}    # Only printed once

if (true)
    ...begin...                     # 执行 (一次)
    ...end...                       # 执行
else
    ...begin...                     # 执行
    ...end...                       # 不执行
end

The Kernel method at_exit provides an alternative to the END statement; it registers a block of code to be executed just before the interpreter exits.

Threads, Fibers, and Continuations

Chapter 6 Methods, Procs, Lambdas, and Closures

Defining Simple Methods

Method Names

Methods and Parentheses

Optional parentheses

Define method 的时候,可以 omit paren (通常大家都这样。)

Required Parentheses

Method Arguments

todo, p189

Procs and Lambdas

block 不是 obj,所以不能修改。一个 object,像一个 block,称之为 proc 或者 lambda。

The subsections that follow explain @
  • How to create Proc objects in both proc and lambda forms
  • How to invoke Proc objects
  • How to determine how many arguments a Proc expects
  • How to determine if two Proc objects are the same
  • How procs and lambdas differ from each other

Closures

closures: closes over

In Ruby, procs and lambdas are closures. The term “closure” comes from the early days of computer science; it refers to an object that is both an invocable function and a variable binding for that function. When you create a proc or a lambda, the resulting Proc object holds not just the executable block but also bindings for all the variables used by the block.

def multiplier(n)
    lambda {|data| data.collect{|x| x*n } }     # 和 JS 里面一样,返回一个函数
end
doubler = multiplier(2)
puts doubler.call([1,2,3]) # Prints 2,4,6

Method Objects

Ruby has powerful metaprogramming (or reflection) capabilities, and methods can actually be represented as instances of the Method class.

m = 0.method(:succ)             # A Method representing the succ method of Fixnum 0

Functional Programming

mean = a.inject {|x,y| x+y } / a.size
sumOfSquares = a.map{|x| (x-mean)**2 }.inject{ |x,y| x+y }
standDevision = Math.sqrt(sumOfSquares/(a.size-1))

Chapter 7 Classes and Modules

Defining a Simple Class

Method Visibility: Public, Protected, Private

Subclassing and Inheritance

Object Creation and Initialization

Modules

Loading and Requiring Modules

Singleton Methods and the Eigenclass

Method Lookup

Constant Lookup

Chapter 8 Reflection and Metaprogramming

Types, Classes, and Modules

Evaluating Strings and Blocks

Variables and Constants

Methods

Hooks

Tracing

ObjectSpace and GC

Custom Control Structures

Missing Methods and Missing Constants

Dynamically Creating Methods

Alias Chaining

Domain-Specific Languages

Chapter 9 The Ruby Platform

Strings

Regular Expressions

Numbers and Math

Dates and Times

Collections

Files and Directories

Input/Output

Networking

Threads and Concurrency

Chapter 10 The Ruby Environment

Invoking the Ruby Interpreter

The Top-Level Environment

Practical Extraction and Reporting Shortcuts

Calling the OS

Security

Colophon