方法名需要小写开头(大写开头会看作常量), 方法要找调用之前定义
局部变量: 小写字母/ 下划线_ 开头,
def say_hello(name='World') puts "Hello, #{name}!" # 使用#{}来访问任何变量或常量的值 end say_hello # => "Hello, World!" say_hello("John") # => "Hello, John!"可变参数def sample(*test)
返回值: 默认返回最后一个语句的值; 或者 return a, b
特殊的量如下
self: 当前方法的接收器对象。true: 代表 true 的值。false: 代表 false 的值。nil: 代表 undefined 的值。__FILE__: 当前源文件的名称。__LINE__: 当前行在源文件中的编号。String 是一个提供的类, 有庞大的方法集
创建: 使用 ’ or ‘’ 来做(多用’’, 有时候字符串内部有 ’ )
转义字符: \b 退格, \r 回车符, \s 空格, \t tab, \a 蜂鸣警告
常用方法
方法调用方式返回值描述str.lengthInteger字符串的长度,空字符串返回 0str.include?(other_str)True 或 False字符串包含,传入的参数为另一个字符串,如果该字符串中包含该子串,则返回True,否则返回Falsestr.insert(index, other_str)new_str字符串插入,参数index是待插入的下标,other_str是另一个字符串,返回的是插入后新的字符串,下标从 0 开始计算str.split(pattern=$;, [limit])Array字符串分割,将字符串按照pattern进行分割,默认分割符为空格,返回值是一个包含若干字符串的数组str.gsub(pattern, replacement)new_str字符串替换,将字符串按照pattern匹配的字符更换为replacement,返回替换后的字符串str.replace(other_str)other_str字符串整体替换,将字符串整体替换成新的字符串str.delete([other_str]+)new_str字符串删除,传入参数[other_str]+可包含多个字符,该方法匹配到str中的所有字符并删除,返回新的字符串str.stripnew_str清除空格,清除掉str中字符串前后的所有空格,换行符,回车符。不包含字符间的空格,返回新的字符串str.reversereverse_str字符串翻转,将字符串顺序翻转,返回翻转后的字符串str.to_iInteger字符串转换为数字, 如果字符串以数字开头,则转换为开头数字的整型值,如果字符串不以数字开头,则返回 0str.chompnew_str去掉字符串末尾的\n或\rstr.chopnew_str去掉字符串末尾的最后一个字符,不管是\n,\r还是普通字符str.downcasenew_str将字符串转换为全小写str.upcasenew_str将字符串转换为全大写数组Array可以存储 String, Integer, Fixnum, Hash, Symbol等对象, 也可以是其他Array(成为多维数组), 添加元素时候, 会自动增长.
创建:
new方法: arr = Array.new, new(5) 表示长为5的空Nil数组, arr = Array.new(5, ‘coder’)Array.[]( 1, 2, 3)Array[1, 2, 3][1, 2, 3] # 最常用访问数组元素, 用index索引, 从0开始, -1为最后一个. eg: arr[2,4](表示范围), arr[1…4], arr.at(0)
还可以用 arr.take(3) 访问前3个, drop(3) 访问索引3之后的所有元素(1…5) 这就表示Range了, (1…5).to_a 把Range 转换成 Array常用方法
方法调用方式返回值描述arr.empty?true 或 false判段数组是否为空arr.push(element)new_array在数组的最后加入元素element,返回加入元素后的新数组arr << 6new_array同上arr.insert (index, elements)new_array在指定位置index塞进元素elements,可以塞多个元素 arr.insert (3, 'a', 'b', 'c')arr.delete(element)element删除数组中所有为element的元素arr.compactnew_array删除数组中所有空元素nil,返回删除空元素后的新数组arr.uniqnew_array清除数组中的重复元素,返回去除重复后的新数组arr.reversenew_array翻转arr,返回翻转后的新数组arr.clear[ ]删除数组中的所有元素,返回空数组arr.countInteger没有参数时,返回数组的大小,带有参数时,返回数组中与参数相同元素的个数arr.includes?(element)true 或 false判断数组arr中是否包含元素elementarr.sortnew_array将数组按照首字母进行排序,也可定制排序规则arr.sampleelement从数组中随机取样,带参数取样个数,则可取多个样本arr.flattennew_array将多维数组转换成一维数组arr.join(',')String将数组使用连接符,连接成一个字符串使用 key -> value 的键值对的集合, 索引为任何对象类型的任意键来完成, 不是整数索引, 其它和数组类似. 注意不会维持顺序, 只负责两个对象的配对. 若通过不存在的键访问哈希, 返回nil
创建:
new: hash = Hash.new(0)
如下方法
language = {} language['Ruby'] = 'Wonderful' language['Python'] = 'Excellent' # 或者如下 language = {"Ruby" => "Wonderful", "Python" => "Excellent"} language = { Ruby: "Wonderful", Python: "Excellent" } # 更推荐, # 后者使用 :, key 为 :Ruby 是一个符号Symbol, 更高效访问: 用language.keys 查看所有的键, language[“Ruby”]直接访问
常用方法
方法调用方式返回值描述hsh == other_hashtrue 或 false判断两个哈希是否相等。键相等,值相等,键值对应关系均相等才返回truehsh.clear{}清空当前哈希hsh.delete(key)value从哈希中删除匹配key的键值对,并返回对应的值valuehsh.has_key?(key)true 或 false判断哈希中是否包含键keyhsh.has_value?(value)true 或 false判断哈希中是否包含值valuehsh.to_sString将哈希的内容转换为字符串输出hsh.invertnew_hsh将哈希的键值对颠倒,键变值,值变键,构成新的哈希返回hsh.key(value)key返回给定值对应的键。如果找不到该值,则返回0hsh.lengthInteger返回哈希的大小hsh.merge(other_hash)new_hsh将hsh和other_hash合并成一个新的哈希。重复键的项值采用other_hash。hsh.store(key, value)value在哈希中加入新的键值对hsh.to_aArray将哈希转换为数组,格式为: [[ key1, value1 ], [ key2, value2 ]]一段代码, 传入参数时候可以看作一个方法调用, 块是一个强大的模块. 可以用块实现回掉, 并实现迭代器.
块是括号{ } (只有一行代码), do 和 end 之间(多行代码)的代码块. 放在需要调用块的代码行的末尾.
使用yield声明, 可以多次调用这个块. yield看作方法调用, 调用这个块外的一段代码
def call_back # 这就是一个迭代器 puts "Start of method" yield yield puts "End of method" end call_back {puts "In the block"}此外还可以给yield传递参数 yield("hello", 99), call_block {|str, num| ... }
重复执行多次相同事情, 用迭代器.
Iterator是一个简单的能接受代码块的方法(例如each方法), 若方法中包含yield调用, 肯定是迭代器.
与块的传递关系(相互传递): 块被当做一个特殊参数传递给迭代器方法; 迭代器方法内部使用yield调用代码块时候可以把参数值传入块.
ruby的容器对象(例如Array, Hash, Range)有两个简单的迭代器:
each: 最简单, 对容器每个元素调用块collect: 把对象元素传递给块, 块处理后返回一个包含处理结果的新数组Arrayeach(获取数组每个元素), each_with_index(获取数组元素和下标)
arr = [1, 2, 3, 4, 5] arr.each { |num| puts num } # num 是一个局部变量,each迭代器每一次迭代将数组中的一个元素赋值给 num languages = ['Ruby', 'Javascript', 'Java'] languages.each_with_index do |lang, index| puts "#{index}, I love #{lang}!" endcollect(获取数组元素, 处理后返回新数组)
a = [1,2,3,4,5] b = a.collect{ |x| x * 2 }有四种常见循环结构 while, do…while, until, for
while 语句
while conditional [do / :] code end # do 或 : 可以不写, 如果在一行内写while需要do : 来隔开条件和codedo-while 语句(相比while, 至少执行一次)
begin code end while conditional # 或者 code while conditonalfor语句
for variable [, variable ...] in expression [do] code end # 对expression(例如一个数组)中每个元素分别执行一次codeuntil语句, 和while区别: conditional为false进入循环
i = 1 num = 5 until i > num do # 等价于 while i <= num puts("我们爬了#{i}层楼" ) i = i + 1 endif else 语句
if condition1 [then] # 多条件用and , or 连接 code1 [elsif condition2 [then] # 使用elsif , 不是elif, else id code2]... [else code3] end # 一定有end结尾!unless 语句, (if为假), 不推荐使用.
case语句, 类比 switch case 语句
price = 85 case price when 0..10 puts "便宜" when 11..50 puts "普通" else puts "贵" end其他
break: 终止最内层循环, 配合if;next: 跳到循环下次迭代redo: 重新开始最内部循环的该次迭代, 重新执行一遍本次迭代class Person, 类名首字母要大写. 类就是一段代码, 和函数, 对象没有本质区别.
3.times do # 用3.times 来重复执行3次 class A # 其实只定义了一个类A, 但是使用了3次 puts "Hello" end end可以只任何时候打开一个已经存在的类, 并添加新的方法, 包括String, Array这样的标准类也可以添加自己的方法.(不同于其他语言, 可能导致在不同时间在同一类中引入一个同样的方法, 应该混乱, 成为猴子补丁)
类也是一种对象, Class有自己的类为Class. 限制更少, 可以像对象那样在运行时, 定义动态的类, 允许编辑类.
类的方法:
实例方法, 更常用
class Test def hello # def self.hello or def Test.hello, 三种方法均可 puts "hello" end end Test.new.hello # 先new定义实例再调用类方法
Test = Class.new # 利用一个影子类, 为Test对象创造一个单例方法 def Test.hello puts "Hello" end Test.hello子类继承父类 class child < father, 只支持单继承(多重继承使用Mix-In), 若子类和父类定义同名方法, 子类方法会覆盖父类方法
class Father def initialize(name) # 构造函数 @name = name # 类的实例变量 @name end def says puts "I am father" end end class Son < Father def initialize(name) # 覆盖了父类的 @name = "son_#{name}" end def says puts "I am son. name: #{@name}" end end son = Son.new("Jack") # 使用Son.new 去创建新的对象 son.says #=> I am son. name: son_Jack类变量: @@开头, 必须初始化后才能调用, 作用于类的所有范围, 所有实例对象共享, 包括子类和实例, 通过Protected声明.
实例变量: @开头, 可以跨实例/对象 中的方法使用, 在不同对象之间可以改变. 每个实例对象都有自己独有的实例变量. (区别类实例变量)
模块(Module)相当于类的扩充, 是方法和常量的集合. 两个好处:
提供了一个命名空间, 防止命名冲突能实现Mix-in 功能模块和类的不同:
模块不能被实例化模块不能被继承, 无子模块模块的方法:
模块方法: 和类方法类似, 暴露出来可被外部使用
实例方法: 不能被外部引用, 需要在类中引用(include / extend)模块, 作为类的方法使用
module TestModule def module_function_say_hello # 模块方法 puts "Hello! I'm Module Function!" end def instance_function_say_hello # 实例方法 puts "hello! I'm instance function!" end # 将方法定义为模块方法, 外部随意使用 module_function :module_function_say_hello end # 调用模块方法 puts TestModule.module_function_say_hello # 使用模块名调用模块的实例方法,产生 NoMethodError 错误 TestModule.instance_function_say_hello class TestClass # 在类 TestClass 中引入模块 TestModule include TestModule end test_object = TestClass.new # 调用模块的实例方法(类的成员方法) puts test_object.instance_function_say_hello # 使用对象调用模块方法,产生 非法访问 错误 test_object.module_function_say_hello class TestClass2 extend TestModule # 使用extend, 变成类的类方法引入 end TestClass2.instance_function_say_hello #=> hello! I'm instance function! TestClass2.module_function_say_hello #=> NoMethodError: private method `module_function_say_hello' called for TestClass2:Class注: 模块方法作为模块的公有方法被外部访问, 但被引入到类后, 变成了Private, 外部不可用;
注: 实例方法, 由于模块不能被实例化, 通过类的引入(include)来把模块中所有实例方法变成这个类的实例方法. extend 来变成类的类方法访问
require语句: 类似include, import, 若第三方想使用已定义的模块, 用require加载模块文件
实现继承, 一种受限制的多重继承, 可以对某些类进行"点缀".
多重继承的3个问题
结构复杂化, 有多个父类, 不是线性关系优先顺序模糊功能冲突: 不同父类有相同方法的冲突在类中使用include 模块, 方法查找顺序:
优先使用类自己的同名方法类中有多个模块, 优先使用最后一个模块 多级嵌套(include), 查找还是线性相同模块被include了两次, 第二次之后会被忽略 module Mod1 def meth puts "这是Mod1" end end module Mod2 def meth puts "这是Mod2" end end module Mod3 include Mod2 include Mod1 include Mod2 end class Class1 include Mod1 include Mod3 end object_class = Class1.new object_class.meth #=> 这是Mod1 p Class1.ancestors #=> [Class1, Mod3, Mod1, Mod2, Object, Kernel, BasicObject]gets: 获取标准屏幕的用户输入, 最后会有\n 字符, 可用gets.chomp去除
也可以传参调用gets(sep, limit) , sep 是分隔符, limit 是输入上限字符, 若超过, 则分为多段
ARGV 从命令行接受参数, ARGV[0] = Educoder (输入ruby test.rb Educoder)
输出:
常见的 puts , p 方法
STDOUT
$stdout << "hello " << "world\n"printf 方法, 同C语言, %d, %f, %x, %o, 常见的格式序列如下:
格式域 作用 b 作为二进制输出 c 作为字符表示输出(ASCII码表) d 作为整数输出 f 作为浮点数输出 o 作为八进制数输出 s 作为字符串输出 u 作为无符号小数输出 x 作为十六进制数输出,a-f为小写字母 X 作为十六进制数输出,A-F为大写字母 \n 换行输出
# `#' 号在输出八进制数时使其前面带上八进制数标志 `0` # `+' 号改变负数的输出格式 printf("%o", 123) #=> "173" printf("%#o", 123) #=> "0173" printf("%+o", -123) #=> "-173"putc(obj), 输出一个字符
若obj为数字, 查ascii, 转换为字符输出若obj为字符串, 只输出第一个字符创建 File.new(filename, mode="r" [, opt]) filename 为需要操作的文件名(可带路径), mode 有 “r r+ w w+ a a+ b t”, mode默认为"r 只读"
File.open("filename", "mode") do |file| # ... 处理文件 end # 之后自动关闭文件 f = File.new("testfile") # 也可用new文件读写
简单IO读写也适用于文件对象, file.gets 读取一行
文件独有的: file.read([length]) 读取指定个数字符, 若length被忽略会读取到EOF
还有 readline(读取一行包括\n, 并放到下一行等待继续读), readlines(读取整个文件, 按照行号返回一个数组)
# testfile 文件保持与上述一致 f = File.new("testfile", "a+") f.write("This is new line!") # 文件写入 # 字符串写入到末尾, 注意\n 可能要添加!File 处理文件, Dir 类 处理目录 Dir.new(dir_name) 创建目录对象
基本操作
Dir.chdir("/usr/bin") 切换目录
puts Dir.pwd 查看当前目录
puts Dir.entries("./").join(" ") entries 获取指定目录的文件和目录列表, 返回 数组, eg: 输出".. file.rb . testfile"
Dir.foreach("/usr/bin") do |entry| puts entry end # 也可以遍历Dir.mkdir( “mynewdir”, 755 ) 来给新创建的目录增添权限掩码为 755 的权限。
Tips:掩码 755 设置所有者(owner)、所属组(group)、每个人(world [anyone])的权限为 rwxr-xr-x,其中
读取: r = read = 4 写入: w = write = 2 执行: x = execute = 1
Dir.delete(“testdir”) 删除目录, unlink, rmdir 同理, 注意目录非空会报错