x# 这是一行注释# 一切都是对象(object)nil.class #=> Nil100.class #=> Int32true.class #=> Bool# nil, false 以及空指针是假值(falsey values)!nil #=> true : Bool!false #=> true : Bool!0 #=> false : Bool# 整数类型1.class #=> Int32# 四种有符号整数1_i8.class #=> Int81_i16.class #=> Int161_i32.class #=> Int321_i64.class #=> Int64# 四种无符号整数1_u8.class #=> UInt81_u16.class #=> UInt161_u32.class #=> UInt321_u64.class #=> UInt642147483648.class #=> Int649223372036854775808.class #=> UInt64# 二进制数0b1101 #=> 13 : Int32# 八进制数0o123 #=> 83 : Int32# 十六进制数0xFE012D #=> 16646445 : Int320xfe012d #=> 16646445 : Int32# 浮点数类型1.0.class #=> Float64# Crystal中有两种浮点数1.0_f32.class #=> Float321_f32.class #=> Float321e10.class #=> Float641.5e10.class #=> Float641.5e-7.class #=> Float64# 字符类型'a'.class #=> Char# 八进制字符'\101' #=> 'A' : Char# Unicode字符'\u0041' #=> 'A' : Char# 字符串"s".class #=> String# 字符串不可变(immutable)s = "hello, " #=> "hello, " : Strings.object_id #=> 134667712 : UInt64s += "Crystal" #=> "hello, Crystal" : Strings.object_id #=> 142528472 : UInt64# 支持字符串插值(interpolation)"sum = 1 + 2" #=> "sum = 3" : String# 多行字符串"这是一个 多行字符串"# 书写带有引号的字符串%(hello "world") #=> "hello \"world\""# 符号类型# 符号是不可变的常量,本质上是Int32类型# 符号通常被用来代替字符串,来高效地传递特定的值:symbol.class #=> Symbolsentence = :question? # :"question?" : Symbolsentence == :question? #=> true : Boolsentence == :exclamation! #=> false : Boolsentence == "question?" #=> false : Bool# 数组类型(Array)[1, 2, 3].class #=> Array(Int32)[1, "hello", 'x'].class #=> Array(Int32 | String | Char)# 必须为空数组指定类型[] # Syntax error: for empty arrays use '[] of ElementType'[] of Int32 #=> [] : Array(Int32)Array(Int32).new #=> [] : Array(Int32)# 数组可以通过下标访问array = [1, 2, 3, 4, 5] #=> [1, 2, 3, 4, 5] : Array(Int32)array[0] #=> 1 : Int32array[10] # raises IndexErrorarray[-6] # raises IndexErrorarray[10]? #=> nil : (Int32 | Nil)array[-6]? #=> nil : (Int32 | Nil)# 使用负位置编号,从后往前访问数组array[-1] #=> 5# With a start index and size# 使用起始位置编号+大小array[2, 3] #=> [3, 4, 5]# 使用范围(range)访问数组array[1..3] #=> [2, 3, 4]# 向尾部添加元素array << 6 #=> [1, 2, 3, 4, 5, 6]# 删除尾部元素array.pop #=> 6array #=> [1, 2, 3, 4, 5]# 删除首部元素array.shift #=> 1array #=> [2, 3, 4, 5]# 检查元素是否存在与数组之中array.includes? 3 #=> true# 一种特殊语法,用来创建字符串数组或符号数组%w(one two three) #=> ["one", "two", "three"] : Array(String)(one two three) #=> [:one, :two, :three] : Array(Symbol)# 对于定义了`new`和`#<<`方法的类,可以用以下语法创建新对象set = Set{1, 2, 3} #=> [1, 2, 3]set.class #=> Set(Int32)# 以下代码与上方等同set = Set(typeof(1, 2, 3)).newset << 1set << 2set << 3# 哈希表类型(Hash){1 => 2, 3 => 4}.class #=> Hash(Int32, Int32){1 => 2, 'a' => 3}.class #=> Hash(Int32 | Char, Int32)# 必须为空哈希表指定类型{} # Syntax error{} of Int32 => Int32 # {}Hash(Int32, Int32).new # {}# 可以使用键(key)快速查询哈希表hash = {"color" => "green", "number" => 5}hash["color"] #=> "green"hash["no_such_key"] #=> Missing hash key: "no_such_key" (KeyError)hash["no_such_key"]? #=> nil# 检查某一键哈希表中是否存在hash.has_key? "color" #=> true# 对于定义了`#[]=`方法的类,可以使用以下语法创建对象class MyType def []=(key, value) puts "do stuff" endendMyType{"foo" => "bar"}# 以上与下列代码等同tmp = MyType.newtmp["foo"] = "bar"tmp# 范围类型(Range)1..10 #=> Range(Int32, Int32)Range.new(1, 10).class #=> Range(Int32, Int32)# 包含或不包含端点(3..5).to_a #=> [3, 4, 5](3...5).to_a #=> [3, 4]# 检查某一值是否在范围内(1..8).includes? 2 #=> true# 元组类型(Tuple)# 元组类型尺寸固定,不可变,储存在栈中# 元组可以有不同类型的对象组成{1, "hello", 'x'}.class #=> Tuple(Int32, String, Char)# 使用下标访问元组tuple = {:key1, :key2}tuple[1] #=> :key2tuple[2] #=> syntax error : Index out of bound# 将元组中的元素赋值给变量a, b, c = {:a, 'b', "c"}a #=> :ab #=> 'b'c #=> "c"# 命名元组类型(NamedTuple)tuple = {name: "Crystal", year: 2011} # NamedTuple(name: String, year: Int32)tuple[:name] # => "Crystal" (String)tuple[:year] # => 2011 (Int32)# 命名元组的键可以是字符串常量{"this is a key": 1} # => NamedTuple("this is a key": Int32)# 过程类型(Proc)# 过程代表一个函数指针,以及可选的上下文(闭包)# 过程通常使用字面值创建proc = ->(x : Int32) { x.to_s }proc.class # Proc(Int32, String)# 或者使用`new`方法创建Proc(Int32, String).new { |x| x.to_s }# 使用`call`方法调用过程proc.call 10 #=> "10"# 控制语句(Control statements)if true "if 语句"elsif false "else-if, 可选"else "else, 同样可选"endputs "可以将if后置" if true# 将if作为表达式a = if 2 > 1 3 else 4 enda #=> 3# 条件表达式a = 1 > 2 ? 3 : 4 #=> 4# `case`语句cmd = "move"action = case cmd when "create" "Creating..." when "copy" "Copying..." when "move" "Moving..." when "delete" "Deleting..."endaction #=> "Moving..."# 循环index = 0while index <= 3 puts "Index: index" index += 1end# Index: 0# Index: 1# Index: 2# Index: 3index = 0until index > 3 puts "Index: index" index += 1end# Index: 0# Index: 1# Index: 2# Index: 3# 更好的做法是使用`each`(0..3).each do |index| puts "Index: index"end# Index: 0# Index: 1# Index: 2# Index: 3# 变量的类型取决于控制语句中表达式的类型if a < 3 a = "hello"else a = trueendtypeof a #=> (Bool | String)if a && b # 此处`a`与`b`均为Nilendif a.is_a? String a.class #=> Stringend# 函数(Functions)def double(x) x * 2end# 函数(以及所有代码块)均将最末尾表达式的值作为返回值double(2) #=> 4# 在没有歧义的情况下,括号可以省略double 3 #=> 6double double 3 #=> 12def sum(x, y) x + yend# 使用逗号分隔参数sum 3, 4 #=> 7sum sum(3, 4), 5 #=> 12# yield# 所有函数都有一个默认生成、可选的代码块(block)参数# 在函数中可以使用yield调用此代码块def surround puts '{' yield puts '}'endsurround { puts "hello world" }# {# hello world# }# 可将代码块作为参数传给函数# "&" 表示对代码块参数的引用def guests(&block) block.call "some_argument"end# 使用星号"*"将参数转换成元组def guests(*array) array.each { |guest| puts guest }end# 如果函数返回数组,可以将其解构def foods ["pancake", "sandwich", "quesadilla"]endbreakfast, lunch, dinner = foodsbreakfast #=> "pancake"dinner #=> "quesadilla"# 按照约定,所有返回布尔值的方法都以问号结尾5.even? # false5.odd? # true# 以感叹号结尾的方法,都有一些破坏性(destructive)行为,比如改变调用接收者(receiver)# 对于某些方法,带有感叹号的版本将改变调用接收者,而不带有感叹号的版本返回新值company_name = "Dunder Mifflin"company_name.gsub "Dunder", "Donald" #=> "Donald Mifflin"company_name #=> "Dunder Mifflin"company_name.gsub! "Dunder", "Donald"company_name #=> "Donald Mifflin"# 使用`class`关键字来定义类(class)class Human # 类变量,由类的所有实例所共享 @@species = "H. sapiens" # `name`的类型为`String` @name : String # 构造器方法(initializer) # 其中@name、@age为简写,相当于 # # def initialize(name, age = 0) # @name = name # @age = age # end # # `age`为可选参数,如果未指定,则使用默认值0 def initialize(@name, @age = 0) end # @name的setter方法 def name=(name) @name = name end # @name的getter方法 def name @name end # 上述getter与setter的定义可以用property宏简化 property :name # 也可用getter与setter宏独立创建getter与setter getter :name setter :name # 此处的`self.`使`say`成为类方法 def self.say(msg) puts msg end def species @@species endend# 将类实例化jim = Human.new("Jim Halpert")dwight = Human.new("Dwight K. Schrute")# 调用一些实例方法jim.species #=> "H. sapiens"jim.name #=> "Jim Halpert"jim.name = "Jim Halpert II" #=> "Jim Halpert II"jim.name #=> "Jim Halpert II"dwight.species #=> "H. sapiens"dwight.name #=> "Dwight K. Schrute"# 调用类方法Human.say("Hi") #=> 输出 Hi ,返回 nil# 带有`@`前缀的变量为实例变量class TestClass @var = "I'm an instance var"end# 带有`@@`前缀的变量为类变量class TestClass @@var = "I'm a class var"end# 首字母大写的变量为常量Var = "这是一个常量"Var = "无法再次被赋值" # 常量`Var`已经被初始化# 在crystal中类也是对象(object),因此类也有实例变量(instance variable)# 类变量的定义由类以及类的派生类所共有,但类变量的值是独立的# 基类class Human @@foo = 0 def self.foo @@foo end def self.foo=(value) @@foo = value endend# 派生类class Worker < HumanendHuman.foo #=> 0Worker.foo #=> 0Human.foo = 2 #=> 2Worker.foo #=> 0Worker.foo = 3 #=> 3Human.foo #=> 2Worker.foo #=> 3module ModuleExample def foo "foo" endend# include <Module> 将模块(module)中的方法添加为实例方法# extend <Module> 将模块中的方法添加为类方法class Person include ModuleExampleendclass Book extend ModuleExampleendPerson.foo # => undefined method 'foo' for Person:ClassPerson.new.foo # => 'foo'Book.foo # => 'foo'Book.new.foo # => undefined method 'foo' for Book# 异常处理# 定义新的异常类(exception)class MyException < Exceptionend# 再定义一个异常类class MyAnotherException < Exception; endex = begin raise MyException.newrescue ex1 : IndexError "ex1"rescue ex2 : MyException | MyAnotherException "ex2"rescue ex3 : Exception "ex3"rescue ex4 # 捕捉任何类型的异常 "ex4"endex #=> "ex2"