1.2 Ruby之语法
总体来说,个人感觉Ruby语法与Python语法较为类似。
1.2.1 Ruby的输入与输出
RuBy的输入会自动带入换行符,因此需要使用gets.chomp提取纯净的字符串。
Ruby的输出也有三种方式,分别是p、puts、print。它们之间输出方式有差异:
- p 方法会以可读性更好的方式输出对象,包括对象的类型和结构。
- puts 方法会输出对象,并在末尾添加换行符。
- print 方法会输出对象,但不会添加换行符。
运行下列程序:
a=gets.chomp
p a
print a
puts a
有结果:
PS C:\Users\Administrator\Desktop> ruBy RuByTest.rb
测试
"测试"
测试测试
在输出字符串时,单引号与双引号皆可,双引号多了一个插值语法:
a=gets.chomp
puts "A#{a}B"
返回
PS C:\Users\Administrator\Desktop> ruBy RuByTest.rb
测试
A测试B
不仅如此,与单引号相比,双引号支持转义操作,如/n。
1.2.2 Ruby的注释
#单行注释
=begin
a="多行注释"
=end
__END__
之后全注释
1.2.3 Ruby的条件语句
1.2.3.1 case-when条件
较为简单,见实例:
op=gets
a=gets
b=gets
case op.chomp
when "+"
jg=a.to_i+b.to_i
when "-"
jg=a.to_i-b.to_i
end
puts jg
1.2.3.2 if分支语句
较为简单,见实例:
a=gets.chomp.to_i
if a>80
puts "very good"
elsif a>60
puts "enough"
else
puts "death"
end
其中表“或”与“且”的符号仍是||与&&,不同条件之间可不加括号分隔。
在简单两种情况的if-else运算时,完全可以使用三项运算减少代码冗余:
条件 ? 处理1:处理2
当条件为真时执行处理1,当条件为假时执行处理2。
1.2.4 Ruby的循环语句
1.2.4.1 while-do循环
利用while-do循环实现猜数游戏:
#rand while break
puts "game begins"
a=rand(1..20)
i=0
while i!=1 do
puts "game continue"
b=gets.chomp.to_i
if a>b
puts 'be bigger'
elsif a<b
puts 'be smaller'
else
i=1
puts "congratulation!,the answer is #{a}!!!"
end
end
1.2.4.2 for循环
有代码:
#循环数组
l1=["q1","q2","q3","q4"]
for i in l1 do
puts i
end
#循环1到5
for num in 1..5 do
puts num
end
#循环1到4
for num in 1...5 do
puts num
end
有输出:
PS C:\Users\Administrator\Desktop> ruby RubyTest.rb
q1
q2
q3
q4
1
2
3
4
5
1
2
3
4
可以看到,闭区间与开区间的区别就在于..与...之间。
1.2.4.3 downto循环
(9.downto(0)).each do |i|
puts i
if i == 5
break
end
end
1.2.5 Ruby的函数
运行下列函数,体会Ruby内置的可忽略参数与默认参数机制:
def fun1
puts "函数1"
end
def fun2(*a)
print "函数2"
puts a
end
def fun3(a="asd")
print "函数3"
puts a
end
fun1
fun2("qwe")
fun2
fun3("zxc")
fun3()
结果:
PS C:\Users\Administrator\Desktop> ruBy RuByTest.rb
函数1
函数2qwe
函数2函数3zxc
函数3asd
Ruby中的函数也可以应用全局变量,如下例:
$x = 10
def update_global_variable
$x = 20
end
puts $x # 输出 10
update_global_variable
puts $x # 输出 20
1.2.6 Ruby的对象
1.2.6.1 对象的基础用法
具体语法如下所示,另外需要说明,按我们现在的操作,成员变量无法像C#一样直接调用,需要用如下方法:
class Player
#构造函数
def initialize(name="Koma")
#@后相当于成员
@id=name
end
def claim
p @id
end
def claim2
@id
end
end
Symbolism=Player.new("Symbol")
Symbolism.claim
p "输出成员变量"
p Symbolism.claim2
直接调用属性的方法下一节会讲到。
1.2.6.2 对象的内部成员的探究
1.2.6.2.1 instance_methods函数
列出对象内部的方法。
class Player
#构造函数
def initialize(name="Koma")
#@后相当于成员
@id=name
end
def claim
p @id
end
def claim2
@id
end
end
Symbolism=Player.new("Symbol")
puts Player.instance_methods(false)
puts "--------------------------------"
puts Player.instance_methods(true)
返回值为:
PS C:\Users\Administrator\Desktop> ruBy RuByTest.rb
claim
claim2
--------------------------------
claim
claim2
singleton_class
dup
itself
methods
singleton_methods
protected_methods
private_methods
public_methods
instance_variables
instance_variable_get
instance_variable_set
instance_variable_defined?
remove_instance_variable
instance_of?
kind_of?
is_a?
display
public_send
class
tap
frozen?
extend
yield_self
then
clone
<=>
===
!~
method
public_method
singleton_method
nil?
eql?
respond_to?
define_singleton_method
hash
freeze
inspect
object_id
send
to_s
to_enum
enum_for
equal?
!
__send__
==
!=
__id__
instance_eval
instance_exec
Player.instance_methods(false) 和 Player.instance_methods(true) 的区别在于是否包含继承的方法。
- Player.instance_methods(false) 会返回 Player 类自身定义的实例方法,不包括从父类继承的方法。
- Player.instance_methods(true) 会返回 Player 类及其父类继承的所有实例方法。
1.2.6.2.2 respond_to?函数与属性寄存器
见下代码:
class Player
attr_accessor :id
#构造函数
def initialize(name="Koma")
#@后相当于成员
@id=name
@attribute=100
end
def claim
p @attribute
end
end
Symbolism=Player.new("syb")
if Symbolism.respond_to?("id")
p Symbolism.id
else
p "该属性目前不可读"
end
if Symbolism.respond_to?("attribute")
p Symbolism.id.to_s
else
p "该属性目前不可读"
end
if Symbolism.respond_to?("claim")
Symbolism.claim
else
p "该函数目前不可读"
end
if Symbolism.respond_to?("claim2")
Symbolism.claim2
else
p "该函数目前不可读"
end
(1)attr_accessor,一个属性寄存器,在此后写过的属性大概相当于public过了,可以直接调用。
(2)respond_to?,调查对象的方法、属性是否可用,返回的是一个布尔值。
1.2.6.3 Ruby的静态函数
如下,可以用两种方式调用:
class Player
attr_accessor :id
#构造函数
def initialize(name="Koma")
#@后相当于成员
@id=name
@attribute=100
end
def claim
p @attribute
end
def self.static
p "这是一个静态函数"
end
end
Player.static
Player::static
输出:
PS C:\Users\Administrator\Desktop> ruby RubyTest.rb
"这是一个静态函数"
"这是一个静态函数"
值得注意的是,在静态函数中,不可以调用非静态的成员,否则会报错。
1.2.6.4 Ruby的类之继承
程序如下:
class Player
attr_accessor :id
#构造函数
def initialize(name="Koma")
#@后相当于成员
@id=name
@attribute=100
end
def claim
p @attribute
end
end
class Young_player < Player
def child_claim
p id
end
end
Symbolism=Young_player.new("syb")
Symbolism.claim
Symbolism.child_claim
输出:
PS C:\Users\Administrator\Desktop> ruby RubyTest.rb
100
"syb"
最重要的是,类的名称首字母一定大写,这个重要规则之前都没有提到,在此补充。
1.2.7 Ruby的数组
1.2.7.1 数组的定义
a=["q1","q2","q3","q4"]
p a
puts a
print a
注意这三种输出也是不同的:
PS C:\Users\Administrator\Desktop> ruby RubyTest.rb
["q1", "q2", "q3", "q4"]
q1
q2
q3
q4
["q1", "q2", "q3", "q4"]
如果想要输出数组中某一值,只需要:
puts a[3]
1.2.7.2 数组的循环
a=["q1","q2","q3","q4"]
a.each do |i|
puts "输出:#{i}"
end
a.each_with_index do |i,q|
puts "第#{q}次输出,启动#{i}"
end
输出的结果为:
PS C:\Users\Administrator\Desktop> ruby RubyTest.rb
输出:q1
输出:q2
输出:q3
输出:q4
第0次输出,启动q1
第1次输出,启动q2
第2次输出,启动q3
第3次输出,启动q4
可以看出,这两种输出方式没有明显不同,第二种循环方式把脚标也带上了罢了。
1.2.7.3 数组的链接
a=["q1","q2","q3","q4"]
if a.respond_to?("each")
puts a.join(";")
end
这里补充一个知识点,respond_to?仍然可以应用到ruby的数组上,毕竟ruby号称万物皆对象,究极的OOP。
PS C:\Users\Administrator\Desktop> ruby RubyTest.rb
q1;q2;q3;q4
1.2.8 字符串运算
1.2.8.1 字符串+运算
不赘述,通用。
a=["q1","q2","q3","q4"]
puts a[0]+a[1]+a[2]+a[3]
输出:
PS C:\Users\Administrator\Desktop> ruby RubyTest.rb
q1q2q3q4
1.2.8.2 字符串<<运算
看代码:
a="Symbolism"
b=" ,blessing"
a<<b
puts a
puts b
输出:
PS C:\Users\Administrator\Desktop> ruby RubyTest.rb
Symbolism ,blessing
,blessing
也就是说,a<<b的操作相当于a=a+b。
1.2.8.3 字符串*运算
代码:
a="Symbolism"
puts a*3
输出:
PS C:\Users\Administrator\Desktop> ruby RubyTest.rb
SymbolismSymbolismSymbolism
1.2.8.4 字符串截取运算
str = "0123456789" # 10-character string
substring = str[5..7]
puts substring
1.2.9 Ruby的哈希表
1.2.9.1 一般哈希表
代码:
mvp_rank={
"curry"=>28.1,
"harden"=>30.3,
"lebron"=>26,
}
puts mvp_rank
puts mvp_rank["harden"]
输出:
PS C:\Users\Administrator\Desktop> ruby RubyTest.rb
{"curry"=>28.1, "harden"=>30.3, "lebron"=>26}
30.3
1.2.9.2 JSON形式哈希表
代码:
player={
name:"harden",
age:28,
point:30.3
}
puts player
puts player[:name]+";"+player[:age].to_s+";"+player[:point].to_s+";"
输出:
PS C:\Users\Administrator\Desktop> ruby RubyTest.rb
{:name=>"harden", :age=>28, :point=>30.3}
harden;28;30.3;
1.2.10 Ruby的模板
1.2.10.1 模板的简单应用
见代码:
module BaseFunc
Version="0.0.1"
def v
return Version
end
def add(a,b)
return a+b
end
def self.showVersion
return Version
end
#定义静态方法
module_function :v
end
puts BaseFunc::Version
puts BaseFunc::showVersion
puts BaseFunc.showVersion
puts BaseFunc.v
有输出:
PS C:\Users\Administrator\Desktop> ruby RubyTest.rb
0.0.1
0.0.1
0.0.1
0.0.1
(1)模板有了字段成员。
(2)模板函数在声明时即可被声明为静态。
(3)可以用module_function补充定义静态模板函数。
(4)模板函数是否静态皆可调用模板成员。
1.2.10.2 类继承模板
见代码:
module BaseFunc
Version="0.0.1"
def v
return Version
end
def add(a,b)
return a+b
end
def self.showVersion
return Version
end
#定义静态方法
module_function :v
end
class Baseclass include BaseFunc
end
puts Baseclass::Version
myCls=Baseclass.new
puts myCls.add(10,20)
输出为:
PS C:\Users\Administrator\Desktop> ruby RubyTest.rb
0.0.1
30
(1)通过”class 类 include 模块“语法实现从模块到类的继承。
(2)模块中的静态函数不可被类继承。
(3)模块成员可以被继承,并可以直接通过::打印出来。
1.2.10 Ruby的例外处理
见下列代码:
begin
#有可能发生错误的处理
puts "处理开始"
#raise "my raise error!"
#10/0
rescue=>e
#错误发生时
puts "错误发生!"
puts e
else
#正常处理时
puts "正常处理"
ensure
#最后处理,无论是否发生错误
puts "最后的扫尾处理"
end
当工作流运行正常时:
PS C:\Users\Administrator\Desktop> ruby RubyTest.rb
处理开始
正常处理
最后的扫尾处理
当工作流发生异常时:
PS C:\Users\Administrator\Desktop> ruby RubyTest.rb
处理开始
错误发生!
divided by 0
最后的扫尾处理
具体流程备注里写的很明白了。
1.2.11 Ruby的其余函数
1.2.11.1 随机数函数
a=rand(1..20)
1.2.11.2 字符串向整形的转化
b=gets.chomp.to_i
同理,还有数字转字符串:to_s、整形转浮点:to_f。
1.2.11.3 矩阵运算函数
定义矩阵并按行列索引:
require 'matrix'
# 创建一个矩阵
matrix = Matrix[[1, 2, 3], [4, 5, 6], [7, 8, 9]]
# 按行列索引输出元素
element = matrix[1, 2] # 第2行第3列的元素,索引从0开始
puts element
求逆矩阵函数:
require 'matrix'
# 创建一个矩阵
matrix = Matrix[[1, 2], [3, 4]]
# 计算矩阵的逆矩阵
inverse_matrix = matrix.inverse
puts inverse_matrix
1.2.11.4 程序运行时间
这里给出第一种,用于测试简单代码块儿的时间。
require 'benchmark'
# 定义一个需要测试运行时间的代码块
def my_method
sum = 0
(1..1000000).each { |i| sum += i }
end
# 使用Benchmark模块测试代码块的运行时间
time = Benchmark.realtime do
my_method
end
puts "程序运行时间:#{time} 秒"
然后第二种测算时间的方式比较灵活。
# 记录开始时间戳
start_time = Time.now.to_i
# 这里是你想要测试的Ruby程序代码
# 例如,一个简单的循环
(1..1000).each do |i|
i = i + 1
yy = i *1000 + 1
puts yy
end
# 记录结束时间戳
end_time = Time.now.to_i
# 计算运行时间
runtime = end_time - start_time
# 输出运行时间
puts "程序运行时间:#{runtime} 秒"
1.2.11.5 程序消耗内存
require 'memory_profiler'
report = MemoryProfiler.report do
arr = []
1000000.times { arr << "a" * 1000 }
end
puts "Total allocated memory: #{report.total_allocated_memsize / 1048576} MB"
1.2.12 Ruby调用其他程序集的方法
1.2.12.1 Ruby调用dll
要在Ruby中调用C++编写的DLL,你可以使用Ruby的Win32API库。这个库允许Ruby代码调用Windows的动态链接库(DLL)中的函数。以下是详细步骤和示例:
步骤 1: 创建C++ DLL
首先,你需要有一个C++ DLL。这里是一个简单的示例,它包含一个函数add,用于计算两个整数的和。
// add.cpp
extern "C" {
__declspec(dllexport) int add(int a, int b) {
return a + b;
}
}
使用Visual Studio或其他编译器将上述代码编译成DLL。确保使用__declspec(dllexport)来导出函数。
步骤 2: 编译DLL
在Visual Studio中,创建一个新的项目,选择“动态链接库(DLL)”作为项目类型。将上述代码添加到项目中,并编译。编译后,你将得到一个.dll文件。
步骤 3: 在Ruby中调用DLL
在Ruby中,你可以使用Win32API库来加载并调用这个DLL中的函数。以下是如何做到这一点的示例代码:
require 'Win32API'
# 创建一个Win32API对象来调用DLL中的add函数
# 参数分别是:DLL文件路径,函数名,参数类型数组,返回值类型
add_function = Win32API.new('path/to/your/add.dll', 'add', ['I', 'I'], 'I')
# 调用函数
result = add_function.call(5, 3)
# 输出结果
puts "The result is #{result}"
注意事项
1. 确保DLL文件的路径正确无误。
2. 确保DLL中的函数名与你在Ruby代码中使用的一致。
3. 如果在调用时遇到任何问题(如找不到符号),请检查你的DLL是否正确编译并导出了所需的函数。
4. 如果DLL正在被其他程序使用,可能会遇到无法写入的问题。确保没有其他程序正在使用该DLL,或者在尝试重新编译前关闭使用该DLL的程序。
通过以上步骤,你可以在Ruby程序中调用C++编写的DLL中的函数。
在Ruby的Win32API库中使用的参数具体含义如下:
1. 'path/to/your/add.dll' - 这是DLL文件的路径。你需要提供DLL文件的完整路径或相对路径,确保Ruby能找到并加载这个DLL。
2. 'add' - 这是你想要调用的函数名。这个名字必须与DLL中导出的函数名完全匹配。
3. ['I', 'I'] - 这是一个数组,表示函数参数的类型。每个字符代表一个参数的类型。在这个例子中,'I'代表整数(Integer)。因此,['I', 'I']表示这个函数接受两个整数作为参数。
4. 'I' - 这表示函数的返回值类型。在这个例子中,'I'同样代表整数。
返回值类型
Win32API支持的返回值类型有限,主要包括:
'I' 或 'L':表示整数(Integer)或长整数(Long),在32位系统中通常是32位,在64位系统中可能是64位。
'S':表示字符串(String)。如果函数返回一个字符串,可以使用这个类型。
'P':表示指针(Pointer),可以用来接收字符串或数组的地址。
关于字符串、数组和列表的返回值
字符串:可以通过返回类型'S'或'P'(指针)来处理。如果使用'P',你可能需要在Ruby中进一步处理这个指针来转换成字符串。
数组和列表:Win32API本身不直接支持数组或列表作为返回类型。如果需要从DLL获取数组或列表,通常会返回一个指针(使用'P'),然后在Ruby中根据需要解析这个指针。
如果你的函数需要返回复杂的数据结构(如数组或结构体
),通常的做法是返回一个指针,并在Ruby中使用额外的库(如ffi)来解析这些复杂的数据类型。
具体参数类型简写如下:
# Verbose data types that can be used instead of single letters
DATA_TYPES = {
'ATOM' => 'I',
'BOOL' => 'B',
'BOOLEAN' => 'B',
'BYTE' => 'I',
'CALLBACK' => 'K',
'CHAR' => 'I',
'COLORREF' => 'L',
'DWORD' => 'L',
'DWORDLONG' => 'L',
'DWORD_PTR' => 'P',
'DWORD32' => 'I',
'DWORD64' => 'L',
'HACCEL' => 'L',
'HANDLE' => 'L',
'HBITMAP' => 'L',
'HBRUSH' => 'L',
'HCOLORSPACE' => 'L',
'HCONV' => 'L',
'HDC' => 'L',
'HFILE' => 'I',
'HKEY' => 'L',
'HFONT' => 'L',
'HINSTANCE' => 'L',
'HKEY' => 'L',
'HLOCAL' => 'L',
'HMENU' => 'L',
'HMODULE' => 'L',
'HRESULT' => 'L',
'HWND' => 'L',
'INT' => 'I',
'INT_PTR' => 'P',
'INT32' => 'I',
'INT64' => 'L',
'LANGID' => 'I',
'LCID' => 'L',
'LCTYPE' => 'L',
'LONG' => 'L',
'LONGLONG' => 'L',
'LONG_PTR' => 'P',
'LONG32' => 'L',
'LONG64' => 'L',
'LPARAM' => 'P',
'LPBOOL' => 'P',
'LPBYTE' => 'P',
'LPCOLORREF' => 'P',
'LPCSTR' => 'P',
'LPCTSTR' => 'P',
'LPCVOID' => 'L',
'LPCWSTR' => 'P',
'LPDWORD' => 'P',
'LPHANDLE' => 'P',
'LPINT' => 'P',
'LPLONG' => 'P',
'LPSTR' => 'P',
'LPTSTR' => 'P',
'LPVOID' => 'L',
'LPWORD' => 'P',
'LPWSTR' => 'P',
'LRESULT' => 'P',
'PBOOL' => 'P',
'PBOOLEAN' => 'P',
'PBYTE' => 'P',
'PHKEY' => 'P',
'SC_HANDLE' => 'L',
'SC_LOCK' => 'L',
'SERVICE_STATUS_HANDLE' => 'L',
'SHORT' => 'I',
'SIZE_T' => 'P',
'TCHAR' => 'L',
'UINT' => 'I',
'UINT_PTR' => 'P',
'UINT32' => 'I',
'UINT64' => 'L',
'ULONG' => 'L',
'ULONGLONG' => 'L',
'ULONG_PTR' => 'P',
'ULONG32' => 'L',
'ULONG64' => 'L',
'USHORT' => 'I',
'USN' => 'L',
'WINAPI' => 'L',
'WORD' => 'I'
}
1.2.13 Ruby对文件操作
1.2.13.1 文件的复制
# 原始文件的路径
source_file_path = 'C:/Users/Administrator/Desktop/ach/SymPlugin/mod_coord.txt'
# 目标文件的路径,即复制后的文件名
destination_file_path = 'C:/Users/Administrator/Desktop/ach/SymPlugin/mod_coord1.txt'
# 使用FileUtils.copy方法复制文件
FileUtils.copy(source_file_path, destination_file_path)
1.2.13.2 文件的写入
首先加一个追加写入:
# 指定要追加写入的文件路径
file_path = destination_file_path
# 使用'a'模式打开文件,如果文件不存在,将会创建文件
File.open(file_path, 'a') do |file|
# 向文件中追加内容
file.puts a.bounds.corner(0).to_a[0]
file.puts a.bounds.corner(0).to_a[1]
file.puts a.bounds.corner(0).to_a[2]
end
然后再来一个一般写入(会覆盖):
File.open('C:\Users\Administrator\Desktop\ach\SymPlugin\face_coords.txt', "w") do |file|
grid.each do |i|
file.puts i[0].to_a[0].to_s
end
end