ZetCode-Ruby-教程-一-
ZetCode Ruby 教程(一)
原文:ZetCode
Ruby HTTPClient
教程
在本教程中,我们展示了如何使用 Ruby HTTPClient
模块。 我们获取数据,发布数据,使用 cookie 并连接到安全的网页。 ZetCode 也有一个简洁的 Ruby 教程。
超文本传输协议(HTTP)是用于分布式协作超媒体信息系统的应用协议。 HTTP 是万维网数据通信的基础。
Ruby HTTPClient
提供了用于通过 HTTP 访问 Web 资源的方法。 它提供了 Ruby 中的libwww-perl
(LWP)的功能。 (有关 Perl LWP 的信息,请参见 ZetCode 的文章。)该宝石是由中村博史创建的。
$ sudo gem install httpclient
该模块通过sudo gem install httpclient
命令安装。
$ service nginx status
* nginx is running
我们在本地主机上运行 nginx Web 服务器。 我们的一些示例将连接到本地运行的 nginx 服务器上的 PHP 脚本。
版本
第一个程序输出库和 Ruby 语言的版本。
version.rb
#!/usr/bin/ruby
require 'httpclient'
puts HTTPClient::LIB_NAME
puts HTTPClient::RUBY_VERSION_STRING
puts HTTPClient::VERSION
这三个常数提供了库和 Ruby 版本号。
$ ./version.rb
(2.8.0, ruby 1.9.3 (2013-11-22))
ruby 1.9.3 (2013-11-22)
2.8.0
这是示例的示例输出。
·get_content·函数
get_content
是一种用于获取由给定 URL 标识的文档的高级方法。
get_content.rb
#!/usr/bin/ruby
require 'httpclient'
client = HTTPClient.new
cont = client.get_content 'http://www.something.com'
puts cont
该脚本获取www.something.com
网页的内容。
cont = client.get_content 'http://www.something.com'
get_content
方法将结果作为一个字符串返回。
$ ./get_content.rb
<html><head><title>Something.</title></head>
<body>Something.</body>
</html>
这是get_content.rb
脚本的输出。
以下程序获取一个小型网页,并剥离其 HTML 标签。
strip_tags.rb
#!/usr/bin/ruby
require 'httpclient'
client = HTTPClient.new
client.get_content('http://www.something.com') do |chunk|
puts chunk.gsub(%r{</?[^>]+?>}, '')
end
该脚本会剥离www.something.com
网页的 HTML 标签。
client.get_content('http://www.something.com') do |chunk|
puts chunk.gsub(%r{</?[^>]+?>}, '')
end
一个简单的正则表达式用于剥离 HTML 标记。 在这种情况下,get_content
方法以字符串块的形式返回内容。
$ ./strip_tags.rb
Something.
Something.
该脚本将打印网页的标题和内容。
请求
HTTP 请求是从客户端发送到浏览器的消息,以检索某些信息或采取某些措施。
HTTPClient
的request
方法创建一个新请求。 请注意,HTTPClient
类具有诸如get
,post
或put
之类的方法,这些方法为我们节省了一些输入。
create_request.rb
#!/usr/bin/ruby
require 'httpclient'
client = HTTPClient.new
method = 'GET'
url = URI.parse 'http://www.something.com'
res = client.request method, url
puts res.body
该示例创建一个 GET 请求并将其发送到http://www.something.com
。
method = 'GET'
url = URI.parse 'http://www.something.com'
我们创建一个请求方法和 URL。
res = client.request method, url
使用request
方法发出请求。
puts res.body
消息响应的body
属性包含消息的正文。
$ ./create_request.rb
<html><head><title>Something.</title></head>
<body>Something.</body>
</html>
这是示例的输出。
状态
HTTP::Message
表示 HTTP 请求或响应。 其status
方法返回响应的 HTTP 状态代码。
status.rb
#!/usr/bin/ruby
require 'httpclient'
client = HTTPClient.new
res = client.get 'http://www.something.com'
puts res.status
puts HTTP::Status::successful? res.status
res = client.get 'http://www.something.com/news/'
puts res.status
puts HTTP::Status::successful? res.status
res = client.get 'http://www.urbandicionary.com/define.php?term=Dog'
puts res.status
puts HTTP::Status::successful? res.status
我们使用get
方法执行三个 HTTP 请求,并检查返回的状态。
puts HTTP::Status::successful? res.status
HTTP::Status::successful?
方法指示状态代码是否成功。
$ ./status.rb
200
true
404
false
302
false
200 是对成功的 HTTP 请求的标准响应,404 指示找不到请求的资源,302 指示该资源已临时重定向。
head
方法
head
方法检索文档标题。 标头由字段组成,包括日期,服务器,内容类型或上次修改时间。
head.rb
#!/usr/bin/ruby
require 'httpclient'
client = HTTPClient.new
res = client.head 'http://www.something.com'
puts "Server: " + res.header['Server'][0]
puts "Last modified: " + res.header['Last-Modified'][0]
puts "Content type: " + res.header['Content-Type'][0]
puts "Content length: " + res.header['Content-Length'][0]
该示例打印服务器,www.something.com
网页的上次修改时间,内容类型和内容长度。
$ ./head.rb
Server: Apache/2.4.12 (FreeBSD) OpenSSL/1.0.1l-freebsd mod_fastcgi/mod_fastcgi-SNAP-0910052141
Last modified: Mon, 25 Oct 1999 15:36:02 GMT
Content type: text/html
Content length: 77
这是head.rb
程序的输出。
get
方法
get
方法向服务器发出 GET 请求。 GET 方法请求指定资源的表示形式。
greet.php
<?php
echo "Hello " . htmlspecialchars($_GET['name']);
?>
在/usr/share/nginx/html/
目录中,有此greet.php
文件。 该脚本返回name
变量的值,该值是从客户端检索到的。 htmlspecialchars()
函数将特殊字符转换为 HTML 实体; 例如&
。
mget.rb
#!/usr/bin/ruby
require 'httpclient'
client = HTTPClient.new
res = client.get 'http://localhost/greet.php?name=Jan'
puts res.body
该脚本将带有值的变量发送到服务器上的 PHP 脚本。 该变量直接在 URL 中指定。
$ ./mget.rb
Hello Jan
This is the output of the example.
$ tail -1 /var/log/nginx/access.log
127.0.0.1 - - [08/May/2016:13:15:31 +0200] "GET /greet.php?name=Jan HTTP/1.1" 200 19 "-"
"HTTPClient/1.0 (2.8.0, ruby 1.9.3 (2013-11-22))"
我们检查了 nginx 访问日志。
get
方法采用第二个参数,我们可以在其中指定查询参数。
mget2.rb
#!/usr/bin/ruby
require 'httpclient'
client = HTTPClient.new
query = {'name' => 'Jan'}
res = client.get 'http://localhost/greet.php', query
puts res.body
该示例与上一个示例基本相同。
$ ./mget2.rb
Hello Jan
This is the output of the example.
重定向
重定向是将一个 URL 转发到另一个 URL 的过程。 HTTP 响应状态代码 301“永久移动”用于永久 URL 重定向。
location = /oldpage.html {
return 301 /files/newpage.html;
}
将这些行添加到位于 Debian 上/etc/nginx/sites-available/default
的 Nginx 配置文件中。
$ sudo service nginx restart
编辑完文件后,我们必须重新启动 nginx 才能应用更改。
newpage.html
<!DOCTYPE html>
<html>
<head>
<title>New page</title>
</head>
<body>
<p>
This is a new page
</p>
</body>
</html>
这是位于 nginx 文档根目录中的newpage.html
文件。
redirect.rb
#!/usr/bin/ruby
require 'httpclient'
client = HTTPClient.new
res = client.get 'http://localhost/oldpage.html', :follow_redirect => true
puts res.body
该脚本访问旧页面并遵循重定向。
res = client.get 'http://localhost/oldpage.html', :follow_redirect => true
:follow_redirect
选项用于遵循重定向。
$ ./redirect.rb
<!DOCTYPE html>
<html>
<head>
<title>New page</title>
</head>
<body>
<p>
This is a new page
</p>
</body>
</html>
This is the output of the example.
$ tail -2 /var/log/nginx/access.log
127.0.0.1 - - [09/May/2016:14:08:50 +0200] "GET /oldpage.html HTTP/1.1" 301 193 "-"
"HTTPClient/1.0 (2.8.0, ruby 1.9.3 (2013-11-22))"
127.0.0.1 - - [09/May/2016:14:08:50 +0200] "GET /files/newpage.html HTTP/1.1" 200 113 "-"
"HTTPClient/1.0 (2.8.0, ruby 1.9.3 (2013-11-22))"
从access.log
文件中可以看到,该请求已重定向到新的文件名。 通信包含两个 GET 消息。
用户代理
在本节中,我们指定用户代理的名称。
agent.php
<?php
echo $_SERVER['HTTP_USER_AGENT'];
?>
在 nginx 文档根目录中,我们有这个简单的 PHP 文件。 它返回用户代理的名称。
agent.rb
#!/usr/bin/ruby
require 'httpclient'
client = HTTPClient.new default_header: {"User-Agent" => "Ruby script"}
res = client.get 'http://localhost/agent.php'
puts res.body
该脚本为agent.php
脚本创建一个简单的 GET 请求。
client = HTTPClient.new default_header: {"User-Agent" => "Ruby script"}
在HTTPClient
的构造器中,我们指定用户代理。
$ ./agent.rb
Ruby script
服务器使用我们随请求发送的代理名称进行了响应。
post
方法
post
方法在给定的 URL 上调度 POST 请求,为填写的表单内容提供键/值对。
target.php
<?php
echo "Hello " . htmlspecialchars($_POST['name']);
?>
在本地 Web 服务器上,我们有此target.php
文件。 它只是将过帐的值打印回客户。
post_value.rb
#!/usr/bin/ruby
require 'httpclient'
client = HTTPClient.new
query = {"name" => "Jan"}
res = client.post 'http://localhost/target.php', query
puts res.body
脚本使用具有Jan
值的name
键发送请求。 POST 请求通过post
方法发出。
$ ./mpost.rb
Hello Jan
这是mpost.rb
脚本的输出。
$ tail -1 /var/log/nginx/access.log
127.0.0.1 - - [08/May/2016:13:38:57 +0200] "POST /target.php HTTP/1.1" 200 19 "-"
"HTTPClient/1.0 (2.8.0, ruby 1.9.3 (2013-11-22))"
使用 POST 方法时,不会在请求 URL 中发送该值。
从字典中检索定义
在以下示例中,我们在 www.dictionary.com 上找到术语的定义。 要解析 HTML,我们使用nokogiri
。 可以使用sudo gem install nokogiri
命令安装。
get_term.rb
#!/usr/bin/ruby
require 'httpclient'
require 'nokogiri'
client = HTTPClient.new
term = 'dog'
res = client.get 'http://www.dictionary.com/browse/'+term
doc = Nokogiri::HTML res.body
doc.css("div.def-content").map do |node|
puts node.text.strip!.gsub(/\s{3,}/, " ")
end
在此脚本中,我们在www.dictionary.com
上找到了术语狗的定义。 Nokogiri::HTML
用于解析 HTML 代码。
res = client.get 'http://www.dictionary.com/browse/'+term
为了执行搜索,我们在 URL 的末尾附加了该词。
doc = Nokogiri::HTML res.body
doc.css("div.def-content").map do |node|
puts node.text.strip!.gsub(/\s{3,}/, " ")
end
我们使用Nokogiri::HTML
类解析内容。 定义位于<div class="def-content">
标签内。 我们通过删除过多的空白来改善格式。
cookie
HTTP cookie 是从网站发送并在用户浏览时存储在用户的 Web 浏览器或程序数据子文件夹中的一小段数据。 当用户访问网页时,浏览器/程序会将 cookie 发送回服务器,以通知用户先前的活动。 Cookies 的有效期限为有效期。
接收到 HTTP 请求时,服务器可以发送带有响应的Set-Cookie
标头。 之后,cookie 值将以 Cookie HTTP 标头的形式与对同一服务器的所有请求一起发送。
cookies.php
<?php
$theme = $_COOKIE['theme'];
if (isset($theme)) {
echo "Your theme is $theme";
} else {
echo "You are using default theme";
setcookie('theme', 'black-and-white', time() + (86400 * 7));
}
?>
该 PHP 文件读取 cookie。 如果 cookie 不存在,则会创建它。 cookie 存储用户的主题。
send_cookie.rb
#!/usr/bin/ruby
require 'httpclient'
url = URI.parse "http://localhost/cookies.php"
cookie = WebAgent::Cookie.new
cookie.name = "theme"
cookie.value = "green-and-black"
cookie.url = url
client = HTTPClient.new
client.cookie_manager.add cookie
res = client.get url
puts res.body
我们创建一个自定义 cookie,并将其发送到cookies.php
页面。
cookie = WebAgent::Cookie.new
cookie.name = "theme"
cookie.value = "green-and-black"
cookie.url = url
使用WebAgent::Cookie
类创建一个 cookie。
client = HTTPClient.new
client.cookie_manager.add cookie
cookie 被添加到 cookie 管理器中。
$ ./send_cookie.rb
Your theme is green-and-black
This is the output of the example.
接下来,我们将读取 cookie 并将其本地存储在文件中。
read_cookie.rb
#!/usr/bin/ruby
require 'httpclient'
client = HTTPClient.new
res = client.get 'http://localhost/cookies.php'
client.set_cookie_store 'cookie.dat'
p res.header["Set-Cookie"]
client.save_cookie_store
该脚本从 PHP 文件读取 cookie,并将其本地存储在cookie.dat
文件中。
最后,我们读取存储的 cookie,并将其发送到同一 PHP 文件。
send_cookie2.rb
#!/usr/bin/ruby
require 'httpclient'
client = HTTPClient.new
cm = HTTPClient::CookieManager.new 'cookie.dat'
cm.load_cookies
client.cookie_manager = cm
res = client.get 'http://localhost/cookies.php'
p res.body
HTTPClient::CookieManager
用于读取 cookie。
$ ./send_cookie.rb
Unknown key: Max-Age = 604800
"You are using default theme"
$ ./read_cookie.rb
Unknown key: Max-Age = 604800
["theme=black-and-white; expires=Sun, 15-May-2016 16:00:08 GMT; Max-Age=604800"]
$ ./send_cookie.rb
"Your theme is black-and-white"
我们运行脚本。 作者认为,警告消息应忽略。
证书
客户端的set_auth
方法设置用于领域的名称和密码。 安全领域是一种用于保护 Web 应用资源的机制。
$ sudo apt-get install apache2-utils
$ sudo htpasswd -c /etc/nginx/.htpasswd user7
New password:
Re-type new password:
Adding password for user user7
我们使用htpasswd
工具创建用于基本 HTTP 认证的用户名和密码。
location /secure {
auth_basic "Restricted Area";
auth_basic_user_file /etc/nginx/.htpasswd;
}
在 nginx /etc/nginx/sites-available/default
配置文件中,我们创建一个安全页面。 领域的名称是"Restricted Area"
。
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<title>Secure page</title>
</head>
<body>
<p>
This is a secure page.
</p>
</body>
</html>
在/usr/share/nginx/html/secure
目录中,我们有这个 HTML 文件。
credentials.rb
#!/usr/bin/ruby
require 'httpclient'
user = 'user7'
passwd = '7user'
client = HTTPClient.new
client.set_auth 'http://localhost/secure/', user, passwd
cont = client.get_content 'http://localhost/secure/'
puts cont
该脚本连接到安全网页; 它提供访问该页面所需的用户名和密码。
$ ./credentials.rb
<!DOCTYPE html>
<html lang="en">
<head>
<title>Secure page</title>
</head>
<body>
<p>
This is a secure page.
</p>
</body>
</html>
使用正确的凭据,credentials.rb
脚本返回受保护的页面。
在本教程中,我们使用了 Ruby HTTPClient
模块。 在 ZetCode 上有类似的 Ruby Faraday 教程和 Ruby Net::HTTP
教程。
Ruby Faraday 教程
在本教程中,我们展示了如何使用 Ruby Faraday 模块。 我们获取数据,发布数据,使用 JSON 并连接到安全的网页。 我们还创建了一个自定义的 Faraday 中间件。 本教程使用 Sinatra 应用作为几个示例。 ZetCode 也有一个简洁的 Ruby 教程。
超文本传输协议(HTTP)是用于分布式协作超媒体信息系统的应用协议。 HTTP 是万维网数据通信的基础。
Ruby Faraday
是一个简单,灵活的 HTTP 客户端库,支持多个后端。 Faraday 也是一种中间件。
$ sudo gem install faraday
该模块通过sudo gem install faraday
命令安装。
Sinatra
Sinatra 是流行的 Ruby Web 应用框架。 它易于安装和设置。 我们的一些示例还将使用 Sinatra 应用。
$ sudo gem install sinatra
$ sudo gem install thin
我们安装 Sinatra 和 Thin Web 服务器。 如果安装了 Thin,Sinatra 会自动选择默认 WEBrick 服务器上的 Thin。
$ pwd
/home/janbodnar/prog/sinatra/first
$ ls
main.rb
在第一个目录中,我们有一个main.rb
文件,它是 Sinatra 应用文件。
main.rb
require 'sinatra'
get '/' do
"First application"
end
应用对/
路由做出反应。 它将一条简单的消息发送回客户端。
$ ruby main.rb
== Sinatra (v1.4.7) has taken the stage on 4567 for development with backup from Thin
Thin web server (v1.6.4 codename Gob Bluth)
Maximum connections set to 1024
Listening on localhost:4567, CTRL+C to stop
使用ruby main.rb
命令启动应用。 瘦服务器启动; 它在 4567 端口上监听。
$ curl localhost:4567/
First application
使用curl
命令行工具,我们连接到服务器并访问/
路由。 一条消息出现在控制台上。
版本
第一个 Faraday 程序将打印库和 Ruby 语言的版本。
version.rb
#!/usr/bin/ruby
require 'faraday'
puts Faraday::VERSION
puts Faraday::default_adapter
这两个常数提供了库的版本号和默认的 Faraday 适配器。
$ ./version.rb
0.9.2
net_http
这是字符串的示例输出。
获取内容
get
方法获取由给定 URL 标识的文档。
get_content.rb
#!/usr/bin/ruby
require 'faraday'
res = Faraday.get 'http://www.something.com'
puts res.body
该脚本获取www.something.com
网页的内容。
$ ./get_content.rb
<html><head><title>Something.</title></head>
<body>Something.</body>
</html>
这是get_content.rb
脚本的输出。
以下程序获取一个小型网页,并剥离其 HTML 标签。
strip_tags.rb
#!/usr/bin/ruby
require 'faraday'
con = Faraday::Connection.new "http://www.something.com"
res = con.get
puts res.body.gsub(%r{</?[^>]+?>}, '')
该脚本会剥离www.something.com
网页的 HTML 标签。
puts res.body.gsub(%r{</?[^>]+?>}, '')
一个简单的正则表达式用于剥离 HTML 标记。
$ ./strip_tags.rb
Something.
Something.
该脚本将打印网页的标题和内容。
状态
Faraday::Response
的status
方法返回响应的 HTTP 状态代码。
status.rb
#!/usr/bin/ruby
require 'faraday'
res = Faraday.get 'http://www.something.com'
puts res.status
puts res.success?
res = Faraday.get 'http://www.something.com/news/'
puts res.status
puts res.success?
res = Faraday.get 'http://www.urbandicionary.com/define.php?term=Dog'
puts res.status
puts res.success?
我们使用get
方法执行三个 HTTP 请求,并检查返回状态。
res = Faraday.get 'http://www.something.com'
puts res.status
使用status
方法检查 HTTP 响应的状态。
puts res.success?
success?
方法指示状态代码是否成功。
$ ./status.rb
200
true
404
false
302
false
200 是对成功的 HTTP 请求的标准响应,404 指示找不到请求的资源,302 指示该资源已临时重定向。
head
方法
head
方法检索文档标题。 标头由字段组成,包括日期,服务器,内容类型或上次修改时间。
head.rb
#!/usr/bin/ruby
require 'faraday'
con = Faraday.new :url => "http://www.something.com"
res = con.head
puts res.headers['server']
puts res.headers['date']
puts res.headers['last-modified']
puts res.headers['content-type']
puts res.headers['content-length']
该示例打印www.something.com
网页的服务器,日期,上次修改时间,内容类型和内容长度。
$ ./head.rb
Apache/2.4.12 (FreeBSD) OpenSSL/1.0.1l-freebsd mod_fastcgi/mod_fastcgi-SNAP-0910052141
Tue, 10 May 2016 10:19:01 GMT
Mon, 25 Oct 1999 15:36:02 GMT
text/html
77
这是head.rb
程序的输出。
get
方法
get
方法向服务器发出 GET 请求。 GET 方法请求指定资源的表示形式。
main.rb
require 'sinatra'
get '/greet' do
"Hello #{params[:name]}"
end
这是 Sinatra 应用文件。 收到/greet
路由后,它将返回一条消息,其中包含客户端发送的名称。
mget.rb
#!/usr/bin/ruby
require 'faraday'
con = Faraday.new
res = con.get 'http://localhost:4567/greet', { :name => 'Peter' }
puts res.body
该脚本将具有值的变量发送到 Sinatra 应用。 该变量直接在 URL 中指定。
$ ./mget.rb
Hello Peter
这是示例的输出。
127.0.0.1 - - [10/May/2016:22:04:38 +0200] "GET /greet?name=Peter HTTP/1.1" 200 11 0.0034
在瘦服务器的此日志中,我们可以看到参数已编码到 URL 中。
get
方法采用第二个参数,我们可以在其中指定查询参数。
mget2.rb
#!/usr/bin/ruby
require 'faraday'
res = Faraday.get do |req|
req.url 'http://localhost/greet'
req.params['name'] = 'Jan'
end
puts res.body
这是发出 GET 消息的另一种方式。
$ ./mget2.rb
Hello Peter
This is the output of the example.
用户代理
在本节中,我们指定用户代理的名称。
main.rb
require 'sinatra'
get '/agent' do
request.user_agent
end
Sinatra 应用返回客户端发送的用户代理。
agent.rb
#!/usr/bin/ruby
require 'faraday'
con = Faraday.new
res = con.get do |req|
req.url 'http://localhost:4567/agent'
req.headers['User-Agent'] = 'Ruby script'
end
puts res.body
该脚本向 Sinatra 应用创建一个简单的 GET 请求。
res = con.get do |req|
req.url 'http://localhost:4567/agent'
req.headers['User-Agent'] = 'Ruby script'
end
用户代理在请求的headers
属性中指定。
$ ./agent.rb
Ruby script
服务器使用我们随请求发送的代理名称进行了响应。
post
方法
post
方法在给定的 URL 上调度 POST 请求,为填写的表单内容提供键/值对。
main.rb
require 'sinatra'
post '/target' do
"Hello #{params[:name]}"
end
Sinatra 应用在/target
路由上返回问候语。 它从params
哈希中获取值。
mpost.rb
#!/usr/bin/ruby
require 'faraday'
con = Faraday.new 'http://localhost'
res = con.post '/target', { :name => 'Jan' }
puts res.body
脚本使用具有Jan
值的name
键发送请求。 POST 请求通过post
方法发出。
$ ./mpost.rb
Hello Jan
这是mpost.rb
脚本的输出。
127.0.0.1 - - [11/May/2016:13:49:44 +0200] "POST /target HTTP/1.1" 200 9 0.0006
使用 POST 方法时,不会在请求 URL 中发送该值。
从字典中检索定义
在以下示例中,我们在 www.dictionary.com 上找到术语的定义。 要解析 HTML,我们使用nokogiri
。 可以使用sudo gem install nokogiri
命令安装。
get_term.rb
#!/usr/bin/ruby
require 'faraday'
require 'nokogiri'
term = 'dog'
con = Faraday.new :url => 'http://www.dictionary.com/browse/'+term
res = con.get
doc = Nokogiri::HTML res.body
doc.css("div.def-content").map do |node|
s = node.text.strip!
s.gsub!(/\s{3,}/, " ") unless (s == nil)
puts s unless (s == nil)
end
在此脚本中,我们在www.dictionary.com
上找到了术语狗的定义。 Nokogiri::HTML
用于解析 HTML 代码。
con = Faraday.new :url => 'http://www.dictionary.com/browse/'+term
为了执行搜索,我们在 URL 的末尾附加了该词。
doc = Nokogiri::HTML res.body
doc.css("div.def-content").map do |node|
s = node.text.strip!
s.gsub!(/\s{3,}/, " ") unless (s == nil)
puts s unless (s == nil)
end
我们使用Nokogiri::HTML
类解析内容。 定义位于<div class="def-content">
标签内。 我们通过删除过多的空白来改善格式。
JSON 格式
JSON (JavaScript 对象表示法)是一种轻量级的数据交换格式。 人类很容易读写,机器也很容易解析和生成。
$ sudo gem install json
如果以前没有安装过,则必须安装json
包。
main.rb
require 'sinatra'
require 'json'
get '/example.json' do
content_type :json
{ :name => 'Jane', :age => 17 }.to_json
end
Sinatra 应用发送 JSON 数据。 它使用to_json
方法完成工作。
parse_json.rb
#!/usr/bin/ruby
require 'faraday'
require 'json'
con = Faraday.new :url => 'http://localhost:4567/example.json'
res = con.get
data = JSON.parse res.body
puts data["name"]
puts data["age"]
该示例读取 Sinatra 应用发送的 JSON 数据。
$ ./parse_json.rb
Jane
17
This is the output of the example.
接下来,我们从 Ruby 脚本将 JSON 数据发送到 Sinatra 应用。
main.rb
require 'sinatra'
require 'json'
post '/readjson' do
data = JSON.parse request.body.read
puts data
"#{data["name"]} is #{data["age"]} years old"
end
该应用读取 JSON 数据并发送回带有已解析值的消息。
post_json.rb
#!/usr/bin/ruby
require 'faraday'
con = Faraday.new
res = con.post do |req|
req.url 'http://localhost:4567/readjson'
req.headers['Content-Type'] = 'application/json'
req.body = '{ "name": "Jane", "age": 17 }'
end
puts res.body
该脚本将 JSON 数据发送到 Sinatra 应用并读取其响应。
req.headers['Content-Type'] = 'application/json'
必须在请求中指定'application/json'
内容类型。
$ ./post_json.rb
Jane is 17 years old
This is the output of the example.
证书
basic_auth
方法设置用于领域的名称和密码。 安全领域是一种用于保护 Web 应用资源的机制。
$ sudo gem install sinatra-basic-auth
对于此示例,我们需要安装sinatra-basic-auth
包。
main.rb
require 'sinatra'
require "sinatra/basic_auth"
authorize do |username, password|
username == "user7" && password == "7user"
end
get '/' do
"hello"
end
protect do
get "/secure" do
"This is restricted area"
end
end
在 Sinatra 应用中,我们指定授权逻辑并设置受保护的路由。
credentials.rb
#!/usr/bin/ruby
require 'faraday'
con = Faraday.new :url => 'http://localhost/secure/'
user = 'user7'
passwd = '7user'
con.basic_auth user, passwd
res = con.get
puts res.body
该脚本连接到安全网页; 它提供访问该页面所需的用户名和密码。
$ ./credentials.rb
This is restricted area
使用正确的凭据,credentials.rb
脚本返回受限制的数据。
Faraday 中间件
中间件是连接两个原本独立的应用的软件。 除了作为 HTTP 客户端之外,Faraday 还充当中间件。 这个概念与 Ruby Rack 非常相似。
Faraday::Connection
包含一个中间件列表。 Faraday 中间件传递有请求和响应信息的env
哈希。 中间件可以在执行请求之前和之后操纵此信息。
重定向
重定向是将一个 URL 转发到另一个 URL 的过程。 HTTP 响应状态代码 302 用于临时 URL 重定向。
重定向是在 Faraday 中间件模块之一中实现的。
$ sudo gem install faraday_middleware
这些模块在faraday_middleware
包中可用。
main.rb
require 'sinatra'
get "/oldpage" do
redirect to("/files/newpage.html"), 302
end
在 Sinatra 应用中,我们使用redirect
命令重定向到其他位置。
newpage.html
<!DOCTYPE html>
<html>
<head>
<title>New page</title>
</head>
<body>
<p>
This is a new page
</p>
</body>
</html>
这是位于public/files
子目录中的newpage.html
文件。
redirect.rb
#!/usr/bin/ruby
require 'faraday'
require 'faraday_middleware'
con = Faraday.new 'http://localhost:4567/oldpage' do |con|
con.use FaradayMiddleware::FollowRedirects, limit: 5
con.adapter Faraday.default_adapter
end
res = con.get
puts res.body
该脚本访问旧页面并遵循重定向。
$ ./redirect.rb
<!DOCTYPE html>
<html>
<head>
<title>New page</title>
</head>
<body>
<p>
This is a new page
</p>
</body>
</html>
This is the output of the example.
127.0.0.1 - - [10/May/2016:22:14:16 +0200] "GET /oldpage HTTP/1.1" 302 - 0.0199
127.0.0.1 - - [10/May/2016:22:14:16 +0200] "GET /files/newpage.html HTTP/1.1" 200 113 0.0073
从日志中,我们可以看到该请求已重定向到新的文件名。 通信包含两个 GET 消息。
MyLogger
在以下示例中,我们创建了自己的小型中间件。 它实现了请求和响应日志记录。
main.rb
require 'sinatra'
get '/greet' do
"Hello #{params[:name]}"
end
这是一个 Sinatra 应用,它向客户端发送问候语。
logger.rb
#!/usr/bin/ruby
require 'faraday'
require 'logger'
class MyLogger
def initialize app
@app = app
@logger = Logger.new(STDOUT)
end
def call env
on_request("request", env)
@app.call(env).on_complete do
on_response("response", env)
end
end
private
def on_request phase, env
@logger.info("#{phase} : #{env.method} - #{env.url}") if env.method and env.url
end
private
def on_response phase, env
@logger.info("#{phase} : #{env.body}") if env.body
end
end
con = Faraday.new(:url => "http://localhost:4567") do |build|
build.request :url_encoded
build.use MyLogger
build.adapter Faraday.default_adapter
end
res = con.get "/greet", {'name' => 'Jan'}
在这里,我们创建一个中间件,该中间件实现了对控制台的日志记录。
def call env
on_request("request", env)
@app.call(env).on_complete do
on_response("response", env)
end
end
中间件必须实现call
方法。 它执行用于请求和响应的方法。
private
def on_request phase, env
@logger.info("#{phase} : #{env.method} - #{env.url}") if env.method and env.url
end
生成请求后,将调用on_request
方法。 该方法记录阶段,请求方法和 URL。
con = Faraday.new(:url => "http://localhost:4567") do |build|
build.request :url_encoded
build.use MyLogger
build.adapter Faraday.default_adapter
end
MyLogger
中间件通过use
方法添加到栈中。 当连接对象执行请求时,它将创建一个共享的env
哈希,将外部中间件包装在每个内部中间件周围,并执行call
方法。
res = con.get "/greet", {'name' => 'Jan'}
一条消息发送到 Sinatra 应用。 该请求和响应被记录到终端。
$ ./logger.rb
I, [2016-05-11T14:48:55.700198 #4945] INFO -- : request : get - http://localhost:4567/greet?name=Jan
I, [2016-05-11T14:48:55.706989 #4945] INFO -- : response : Hello Jan
This is the output of the example.
在本教程中,我们使用了 Ruby Faraday 模块。 在 ZetCode 上有类似的 Ruby HTTPClient 教程和 Ruby Net :: HTTP 教程。
Ruby Net::HTTP
教程
在本教程中,我们展示了如何使用标准的 Ruby Net::HTTP
模块。 我们获取数据,发布数据,使用 JSON 并连接到安全的网页。 本教程使用 Sinatra 应用作为几个示例。 Zetcode 也有一个简洁的 Ruby 教程。
超文本传输协议(HTTP)是用于分布式协作超媒体信息系统的应用协议。 HTTP 是万维网数据通信的基础。
Ruby Net::HTTP
提供了一个丰富的库,可用于构建 HTTP 客户端。
Sinatra
Sinatra 是流行的 Ruby Web 应用框架。 它易于安装和设置。 我们的一些示例还将使用 Sinatra 应用。
$ sudo gem install sinatra
$ sudo gem install thin
我们安装 Sinatra 和 Thin Web 服务器。 如果安装了 Thin,Sinatra 会自动选择默认 WEBrick 服务器上的 Thin。
$ pwd
/home/janbodnar/prog/sinatra/first
$ ls
main.rb
在第一个目录中,我们有一个main.rb
文件,它是 Sinatra 应用文件。
main.rb
require 'sinatra'
get '/' do
"First application"
end
应用对/
路由做出反应。 它将一条简单的消息发送回客户端。
$ ruby main.rb
== Sinatra (v1.4.7) has taken the stage on 4567 for development with backup from Thin
Thin web server (v1.6.4 codename Gob Bluth)
Maximum connections set to 1024
Listening on localhost:4567, CTRL+C to stop
使用ruby main.rb
命令启动应用。 瘦服务器启动; 它在 4567 端口上监听。
$ curl localhost:4567/
First application
使用curl
命令行工具,我们连接到服务器并访问/
路由。 一条消息出现在控制台上。
版本
第一个程序确定库的版本。
version.rb
#!/usr/bin/ruby
require 'net/http'
puts Net::HTTP::version_1_1?
puts Net::HTTP::version_1_2?
该脚本确定net/http
是处于 1.1 版还是 1.2 版模式。
$ ./version.rb
false
true
在我们的情况下,模式为 1.2。
获取内容
get_print
是一种高级方法,可从目标获取正文并将其输出到标准输出。
get_content.rb
#!/usr/bin/ruby
require 'net/http'
uri = URI 'http://www.something.com/'
Net::HTTP.get_print uri
该脚本获取www.something.com
网页的内容。 net/http
设计为与uri
模块紧密配合。
require 'net/http'
这也需要uri
,因此我们不需要单独要求它。
$ ./get_content.rb
<html><head><title>Something.</title></head>
<body>Something.</body>
</html>
这是get_content.rb
脚本的输出。
以下程序获取一个小型网页,并剥离其 HTML 标签。
strip_tags.rb
#!/usr/bin/ruby
require 'net/http'
uri = URI "http://www.something.com/"
doc = Net::HTTP.get uri
puts doc.gsub %r{</?[^>]+?>}, ''
该脚本会剥离www.something.com
网页的 HTML 标签。
puts doc.gsub %r{</?[^>]+?>}, ''
一个简单的正则表达式用于剥离 HTML 标记。
$ ./strip_tags.rb
Something.
Something.
该脚本将打印网页的标题和内容。
状态
响应的code
和message
方法给出其状态。
status.rb
#!/usr/bin/ruby
require 'net/http'
uri = URI 'http://www.something.com'
res = Net::HTTP.get_response uri
puts res.message
puts res.code
uri = URI 'http://www.something.com/news/'
res = Net::HTTP.get_response uri
puts res.message
puts res.code
uri = URI 'http://www.urbandicionary.com/define.php?term=Dog'
res = Net::HTTP.get_response uri
puts res.message
puts res.code
我们使用get_response
方法执行三个 HTTP 请求,并检查返回的状态。
uri = URI 'http://www.something.com/news/'
res = Net::HTTP.get_response uri
puts res.message
puts res.code
使用message
和code
方法检查 HTTP 响应的状态。
$ ./status.rb
OK
200
Not Found
404
Found
302
200 是对成功的 HTTP 请求的标准响应,404 指示找不到请求的资源,302 指示该资源已临时重定向。
head
方法
head
方法检索文档标题。 标头由字段组成,包括日期,服务器,内容类型或上次修改时间。
head.rb
#!/usr/bin/ruby
require 'net/http'
uri = URI "http://www.something.com"
http = Net::HTTP.new uri.host, uri.port
res = http.head '/'
puts res['server']
puts res['date']
puts res['last-modified']
puts res['content-type']
puts res['content-length']
该示例打印www.something.com
网页的服务器,日期,上次修改时间,内容类型和内容长度。
$ ./head.rb
Apache/2.4.12 (FreeBSD) OpenSSL/1.0.1l-freebsd mod_fastcgi/mod_fastcgi-SNAP-0910052141
Wed, 11 May 2016 19:30:56 GMT
Mon, 25 Oct 1999 15:36:02 GMT
text/html
77
这是head.rb
程序的输出。
get
方法
get
方法向服务器发出 GET 请求。 GET 方法请求指定资源的表示形式。
main.rb
require 'sinatra'
get '/greet' do
"Hello #{params[:name]}"
end
这是 Sinatra 应用文件。 收到/greet
路由后,它将返回一条消息,其中包含客户端发送的名称。
mget.rb
#!/usr/bin/ruby
require 'net/http'
uri = URI "http://localhost:4567/greet"
params = { :name => 'Peter' }
uri.query = URI.encode_www_form params
puts Net::HTTP.get uri
该脚本将具有值的变量发送到 Sinatra 应用。 该变量直接在 URL 中指定。
params = { :name => 'Peter' }
这是我们发送到服务器的参数。
uri.query = URI.encode_www_form params
我们使用encode_www_form
方法将参数编码到 URL 中。
puts Net::HTTP.get uri
get
方法将 GET 请求发送到服务器。 它返回打印到控制台的响应。
$ ./mget.rb
Hello Peter
这是示例的输出。
127.0.0.1 - - [11/May/2016:21:51:12 +0200] "GET /greet?name=Peter HTTP/1.1" 200 11 0.0280
在瘦服务器的此日志中,我们可以看到参数已编码到 URL 中。
我们可以直接将参数放入 URL 字符串中。
mget2.rb
#!/usr/bin/ruby
require 'net/http'
uri = URI "http://localhost:4567/greet?name=Peter"
puts Net::HTTP.get uri
这是发出 GET 消息的另一种方式。 它基本上与前面的示例相同。
$ ./mget2.rb
Hello Peter
This is the output of the example.
用户代理
在本节中,我们指定用户代理的名称。
main.rb
require 'sinatra'
get '/agent' do
request.user_agent
end
Sinatra 应用返回客户端发送的用户代理。
agent.rb
#!/usr/bin/ruby
require 'net/http'
uri = URI "http://localhost:4567"
http = Net::HTTP.new uri.host, uri.port
res = http.get '/agent', {'User-Agent' => 'Ruby script'}
puts res.body
该脚本向 Sinatra 应用创建一个简单的 GET 请求。
res = http.get '/agent', {'User-Agent' => 'Ruby script'}
用户代理在get
方法的第二个参数中指定。
$ ./agent.rb
Ruby script
服务器使用我们随请求发送的代理名称进行了响应。
post
方法
post
方法在给定的 URL 上调度 POST 请求,为填写的表单内容提供键/值对。
main.rb
require 'sinatra'
post '/target' do
"Hello #{params[:name]}"
end
Sinatra 应用在/target
路由上返回问候语。 它从params
哈希中获取值。
mpost.rb
#!/usr/bin/ruby
require 'net/http'
uri = URI "http://localhost:4567/target"
params = { :name => 'Peter' }
res = Net::HTTP.post_form uri, params
puts res.body
脚本使用具有Peter
值的name
键发送请求。 POST 请求通过Net::HTTP.post_form
方法发出。
$ ./mpost.rb
Hello Peter
这是mpost.rb
脚本的输出。
127.0.0.1 - - [12/May/2016:11:36:16 +0200] "POST /target HTTP/1.1" 200 11 0.0006
使用 POST 方法时,不会在请求 URL 中发送该值。
从字典中检索定义
在以下示例中,我们在 www.dictionary.com 上找到术语的定义。 要解析 HTML,我们使用nokogiri
包。 可以使用sudo gem install nokogiri
命令安装。
get_term.rb
#!/usr/bin/ruby
require 'net/http'
require 'nokogiri'
term = 'cat'
uri = URI 'http://www.dictionary.com/browse/'+term
res = Net::HTTP.get uri
doc = Nokogiri::HTML res
doc.css("div.def-content").map do |node|
s = node.text.strip!
s.gsub!(/\s{3,}/, " ") unless (s == nil)
puts s unless (s == nil)
end
在此脚本中,我们在www.dictionary.com
上找到了术语cat
的定义。 Nokogiri::HTML
用于解析 HTML 代码。
uri = URI 'http://www.dictionary.com/browse/'+term
为了执行搜索,我们在 URL 的末尾附加了该词。
doc = Nokogiri::HTML res
doc.css("div.def-content").map do |node|
s = node.text.strip!
s.gsub!(/\s{3,}/, " ") unless (s == nil)
puts s unless (s == nil)
end
我们使用Nokogiri::HTML
类解析内容。 定义位于<div class="def-content">
标签内。 我们通过删除过多的空白来改善格式。
JSON 格式
JSON (JavaScript 对象表示法)是一种轻量级的数据交换格式。 人类很容易读写,机器也很容易解析和生成。
$ sudo gem install json
如果以前没有安装过,则必须安装json
包。
main.rb
require 'sinatra'
require 'json'
get '/example.json' do
content_type :json
{ :name => 'Jane', :age => 17 }.to_json
end
Sinatra 应用发送 JSON 数据。 它使用to_json
方法完成工作。
parse_json.rb
#!/usr/bin/ruby
require 'net/http'
require 'json'
uri = URI 'http://localhost:4567/example.json'
res = Net::HTTP.get uri
data = JSON.parse res
puts data["name"]
puts data["age"]
该示例读取 Sinatra 应用发送的 JSON 数据。
$ ./parse_json.rb
Jane
17
This is the output of the example.
接下来,我们从 Ruby 脚本将 JSON 数据发送到 Sinatra 应用。
main.rb
require 'sinatra'
require 'json'
post '/readjson' do
data = JSON.parse request.body.read
"#{data["name"]} is #{data["age"]} years old"
end
该应用读取 JSON 数据并发送回带有已解析值的消息。
post_json.rb
#!/usr/bin/ruby
require 'net/http'
require 'json'
uri = URI 'http://localhost:4567/readjson'
req = Net::HTTP::Post.new uri.path, initheader = {'Content-Type' =>'application/json'}
req.body = {:name => 'Jane', :age => 17}.to_json
res = Net::HTTP.start(uri.hostname, uri.port) do |http|
http.request req
end
puts res.body
该脚本将 JSON 数据发送到 Sinatra 应用并读取其响应。
req = Net::HTTP::Post.new uri.path, initheader = {'Content-Type' =>'application/json'}
'application/json'
内容类型必须在请求的标头中指定。
$ ./post_json.rb
Jane is 17 years old
This is the output of the example.
重定向
重定向是将一个 URL 转发到另一个 URL 的过程。 HTTP 响应状态代码 302 用于临时 URL 重定向。
main.rb
require 'sinatra'
get "/oldpage" do
redirect to("/files/newpage.html"), 302
end
在 Sinatra 应用中,我们使用redirect
命令重定向到其他位置。
newpage.html
<!DOCTYPE html>
<html>
<head>
<title>New page</title>
</head>
<body>
<p>
This is a new page
</p>
</body>
</html>
这是位于public/files
子目录中的newpage.html
文件。
redirect.rb
#!/usr/bin/ruby
require 'net/http'
uri = URI 'http://localhost:4567/oldpage'
res = Net::HTTP.get_response uri
if res.code == "302"
res = Net::HTTP.get_response URI res.header['location']
end
puts res.body
该脚本访问旧页面并遵循重定向。 请注意,这适用于单个重定向。
res = Net::HTTP.get_response URI res.header['location']
标头的位置字段包含文件重定向到的地址。
$ ./redirect.rb
<!DOCTYPE html>
<html>
<head>
<title>New page</title>
</head>
<body>
<p>
This is a new page
</p>
</body>
</html>
This is the output of the example.
127.0.0.1 - - [12/May/2016:12:51:24 +0200] "GET /oldpage HTTP/1.1" 302 - 0.0006
127.0.0.1 - - [12/May/2016:12:51:24 +0200] "GET /files/newpage.html HTTP/1.1" 200 113 0.0006
从日志中,我们可以看到该请求已重定向到新的文件名。 通信包含两个 GET 消息。
证书
basic_auth
方法设置用于领域的名称和密码。 安全领域是一种用于保护 Web 应用资源的机制。
$ sudo gem install sinatra-basic-auth
对于此示例,我们需要安装sinatra-basic-auth
包。
main.rb
require 'sinatra'
require "sinatra/basic_auth"
authorize do |username, password|
username == "user7" && password == "7user"
end
get '/' do
"hello"
end
protect do
get "/secure" do
"This is restricted area"
end
end
在 Sinatra 应用中,我们指定授权逻辑并设置受保护的路由。
credentials.rb
#!/usr/bin/ruby
require 'net/http'
uri = URI 'http://localhost:4567/secure'
req = Net::HTTP::Get.new uri.path
req.basic_auth 'user7', '7user'
res = Net::HTTP.start uri.hostname, uri.port do |http|
http.request req
end
puts res.body
该脚本连接到安全网页; 它提供访问该页面所需的用户名和密码。
$ ./credentials.rb
This is restricted area
使用正确的凭据,credentials.rb
脚本返回受限制的数据。
在本教程中,我们使用了 Ruby net/http
模块。 在 ZetCode 上也有类似的 Ruby HTTPClient
教程和 Ruby Faraday 教程。
Ruby 教程
这是 Ruby 教程。 在本教程中,您将学习 Ruby 语言。 本教程适合初学者。
目录
Ruby
Ruby 是一种动态的,反射性的,通用的面向对象的编程语言。 原始作者是日本程序员松本行弘。 Ruby 于 1995 年首次出现。Ruby 支持各种编程范例。 这包括面向对象,反射,命令式和反射式编程。
相关教程
ZetCode 还有其他编程语言教程: Python 教程, PHP 教程, Tcl 教程, Visual Basic 教程和 C# 教程 。
Ruby
在 Ruby 教程的这一部分中,我们将介绍 Ruby 编程语言。
目标
本教程的目标是使您开始使用 Ruby 编程语言。 本教程涵盖了 Ruby 语言的核心,包括变量,表达式,集合,控件结构和其他核心功能。 它还描述了一些更高级的概念,例如面向对象的编程和正则表达式。 这不是该语言的完整介绍。 该教程是在 Ubuntu Linux 上创建的。
Ruby
Ruby 是一种动态的,反射性的,通用的面向对象的编程语言。 原始作者是日本程序员松本行弘(HTG4)。 Ruby 于 1995 年首次出现。
Ruby 支持各种编程范例。 这包括面向对象,反射,命令式和反射式编程。 Ruby 语言主要受 Perl,Smalltalk,Eiffel 和 Lisp 的影响。 与 Java,C# 或 C 等语言不同,Ruby 没有正式的规范。 相反,Ruby 语言的原始 C 实现可作为事实上的参考。 Ruby 语言还有其他实现,例如 JRuby,IronRuby 或 MacRuby。
官方网站是 ruby-lang.org 。
Ruby 的流行
今天有数百种编程语言在使用。 Ruby 属于最受欢迎的。 估计使 Ruby 成为十大编程语言。 Ruby on Rails 是一个非常流行的 Web 应用框架,它是用 Ruby 创建的第一个杀手级应用。
Ruby 交互式解释器
我们可以在脚本或交互式解释器中运行 Ruby 语句。 在本教程中,我们将使用交互式 Ruby 会话来演示一些较小的代码片段。 较大的代码示例将放在 Ruby 脚本中。
$ irb
irb(main):001:0> puts RUBY_VERSION
1.8.7
=> nil
这是 Ruby 交互式会话的示例。 我们将特殊的RUBY_VERSION
常量的值打印到控制台。 它设置为当前使用的 Ruby 的版本。
Ruby 脚本
我们将有第一个简单的 Ruby 脚本示例。
#!/usr/bin/ruby
# first.rb
puts "This is Ruby"
在此脚本中,我们将消息打印到控制台。
#!/usr/bin/ruby
UNIX 中的每个脚本都以 shebang 开头。 shebang 是脚本中的前两个字符:#!
。 shebang 之后是解释器的路径,它将执行我们的脚本。 /usr/bin/
是 Ruby 解释器最常见的位置。 它也可以位于/usr/local/bin/
或其他位置。
# first.rb
Ruby 中的注释前面带有#
字符。
puts "This is Ruby"
puts
方法将字符串打印到控制台。
$ which ruby
/usr/bin/ruby
可以使用which
命令找到 Ruby 解释器的路径。
$ chmod +x first.rb
$ ./first.rb
This is Ruby
我们使用chmod
命令使脚本可执行并执行。
数据来源
以下资源用于创建本教程:
在 Ruby 教程的这一部分中,我们介绍了 Ruby 语言。
Ruby 语法结构
像人类语言一样,计算机语言也具有词汇结构。 Ruby 程序的源代码由令牌组成。 令牌是原子代码元素。 在 Ruby 语言中,我们具有各种词汇结构,例如注释,变量,字面值,空白,运算符,定界符和关键字。
Ruby 注释
注释被人类用来阐明源代码。 Ruby 中有两种类型的注释。 单行和多行注释。 单行注释以#
字符开头。 多行注释放在=begin
和=end
标记之间。
#!/usr/bin/ruby
=begin
comments.rb
author Jan Bodnar
ZetCode 2011
=end
# prints message to the terminal
puts "Comments example"
显示两种类型注释的示例。 注释会被 Ruby 解释器忽略。
=begin
comments.rb
author Jan Bodnar
ZetCode 2014
=end
这是多行注释的示例。 这两个标记必须从行的开头开始。
Ruby 空白字符
Ruby 中的空白用于分隔标记并终止源文件中的语句。 它还用于提高源代码的可读性。
if true then
puts "A message"
end
在某些地方需要空格。 例如,在if
关键字和true
关键字之间。 或在puts
方法和实际字符串之间。 在其他地方,这是禁止的。 它不能出现在变量标识符或语言关键字中。
a=1
b = 2
c = 3
标记之间放置的空间量与 Ruby 解释器无关。 但是,在整个项目中拥有一种风格很重要。
#!/usr/bin/ruby
x = 5 + 3
puts x
x = 5
+ 3
puts x
x = 5 +
3
puts x
可以使用换行符(空白格式)来终止语句。
x = 5 + 3
在第一种情况下,我们有一个语句。 加法的总和分配给x
变量。 该变量为 8。
x = 5
+ 3
现在,有两个语句。 第一条语句以换行符终止。 x
变量为 5。还有另一个语句+3
,它无效。
x = 5 +
3
最后,我们有一个语句。 换行符前面带有+
二进制运算符,因此解释器需要另一个值。 它看起来在第二行。 在这种情况下,它将两行都作为一条语句。 x
变量为 8。
$ ./whitespace.rb
8
5
8
输出。
Ruby 变量
变量是一个标识符,它保存一个值。 在编程中,我们说我们为变量分配了一个值。 从技术上讲,变量是对存储值的计算机内存的引用。 在 Ruby 中,变量可以容纳字符串,数字或各种对象。 可以随时间为变量分配不同的值。
Ruby 中的变量名称是由字母数字字符和下划线(_
)字符创建的。 变量不能以数字开头。 解释器可以更容易地区分字面值数字和变量。 变量名不能以大写字母开头。 如果标识符以大写字母开头,则在 Ruby 中将其视为常量。
Value
value2
company_name
这些是有效的变量名。
12Val
exx$
first-name
这些是无效变量名称的示例。
变量名前面可以带有两个特殊字符$
和@
。 它们用于创建特定的变量范围。
变量是区分大小写。 这意味着price
和pRice
是两个不同的标识符。
#!/usr/bin/ruby
number = 10
numBER = 11
puts number, numBER
在脚本中,我们将两个数字值分配给两个标识符。 number
和numBER
是两个不同的变量。
$ ./case.rb
10
11
这是脚本的输出。
Ruby 常量
常量是值持有者,随着时间的推移仅持有一个值。 具有第一个大写字母的标识符是 Ruby 中的常量。 在编程中,习惯上以大写形式写入常量的所有字符。
与其他语言不同,Ruby 不会将常量强制为随时间推移仅具有一个值。 仅当我们为现有常量分配新值时,解释器才会发出警告。
#!/usr/bin/ruby
Name = "Robert"
AGE = 23
Name = "Juliet"
在上面的示例中,我们创建了两个常量。 常量之一将在以后重新定义。
Name = "Robert"
AGE = 23
创建两个常量。 当标识符的名称以大写字母开头时,在 Ruby 中将有一个常量。 按照约定,常量通常用大写字母表示。
Name = "Juliet"
我们重新定义一个常数。 哪个发出警告。
$ ./constants.rb
./constants.rb:6: warning: already initialized constant Name
运行示例。
Ruby 字面量
字面值是类型的特定值的字面值表示。 字面值类型包括布尔值,整数,浮点数,字符串,字符和日期。 从技术上讲,字面值将在编译时分配一个值,而变量将在运行时分配。
age = 29
nationality = "Hungarian"
在这里,我们为变量分配了两个字面值。 数字 29 和字符串"Hungarian"
是字面值。
#!/usr/bin/ruby
require 'date'
sng = true
name = "James"
job = nil
weight = 68.5
born = Date.parse("November 12, 1986")
puts "His name is #{name}"
if sng == true
puts "He is single"
else
puts "He is in a relationship"
end
puts "His job is #{job}"
puts "He weighs #{weight} kilograms"
puts "He was born in #{born}"
在上面的示例中,我们有多个字面值。 布尔字面值可以具有值true
或false
。 詹姆斯是一个字符串字面值。 nil
是一个值的缺失。 68.5 是浮点字面值。 最后,1987 年 11 月 12 日是日期字面值。
$ ./literals.rb
His name is James
He is single
His job is
He weighs 68.5 kilograms
He was born in 1986-11-12
这是literals.rb
脚本的输出。
红宝石块
Ruby 语句通常被组织成代码块。 可以使用{ }
字符或do
和end
关键字来分隔代码块。
#!/usr/bin/ruby
puts [2, -1, -4, 0].delete_if { |x| x < 0 }
[1, 2, 3].each do |e|
puts e
end
在示例中,我们显示了两个代码块。
Ruby 代码的流控制通常使用if
关键字进行。 关键字后跟一段代码。 在这种情况下,代码块由then
和end
关键字定界,其中第一个关键字是可选的。
#!/usr/bin/ruby
if true then
puts "Ruby language"
puts "Ruby script"
end
在上面的示例中,我们有一个简单的代码块。 它有两个语句。 该块由then
和end
关键字定界。 then
关键字可以省略。
Ruby 标记
标记$, @
是特殊字符,表示变量的作用域。 $
用于全局变量,@
用于实例变量,@@
用于类变量。
$car_name = "Peugeot"
@sea_name = "Black sea"
@@species = "Cat"
记号始终放在变量标识符的开头。
Ruby 运算符
运算符是用于对某个值执行操作的符号。 (answers.com)
! + - ~ * ** / %
<< >> & | ^
== === != <=> >= >
< <= = %= /= -=
+= *= **= .. ... not
and or ?: && ||
这是 Ruby 语言中可用的运算符的列表。 我们将在本教程的后面部分讨论运算符。
Ruby 分隔符
定界符是一个或多个字符的序列,用于指定纯文本或其他数据流中单独的独立区域之间的边界。 (维基百科)
( ) [ ] { }
, ; ' " | |
#!/usr/bin/ruby
name = "Jane"
occupation = 'Student'
numbers = [ 2, 3, 5, 3, 6, 2 ]
puts name; puts occupation
puts numbers[2]
numbers.each { |i| puts i }
puts ( 2 + 3 ) * 5
在上面的示例中,我们展示了各种 Ruby 分隔符的用法。
name = "Jane"
occupation = 'Student'
单引号和双引号用于在 Ruby 中定界字符串。
numbers = [ 2, 3, 5, 3, 6, 2 ]
方括号用于设置数组的边界。 逗号用于分隔数组中的项目。
puts name; puts occupation
在 Ruby 中,用分号分隔 Ruby 源代码中的两个语句。
puts numbers[2]
分隔符可以在不同的上下文中使用。 在这里,方括号用于访问数组中的项目。
numbers.each { |i| puts i }
圆括号用于定义代码块。 管道用于定义一个元素,该元素在每个循环周期中都填充有当前数组项。
puts ( 2 + 3 ) * 5
括号可用于更改表达式的求值。
Ruby 关键字
关键字是 Ruby 编程语言中的保留字。 关键字用于在计算机程序中执行特定任务。 例如,将值打印到控制台,执行重复性任务或执行逻辑操作。 程序员不能将关键字用作普通变量。
alias and BEGIN begin break case
class def defined? do else elsif
END end ensure false for if
in module next nil not or
redo rescue retry return self super
then true undef unless until when
while yield
这是 Ruby 关键字的列表。
这就是 Ruby 的词汇结构。
Ruby 基础
在 Ruby 教程的这一部分中,我们将介绍 Ruby 语言的基本编程概念。 我们介绍非常基本的程序。 我们将使用变量,常量和基本数据类型。 我们将在控制台上读写; 我们将提到变量插值。
我们从一个非常简单的代码示例开始。
#!/usr/bin/ruby
puts "This is Ruby"
这是简单的 Ruby 脚本。 它将向控制台输出"This is Ruby"
消息。
#!/usr/bin/ruby
这是将执行脚本的 Ruby 解释器的路径。
puts "This is Ruby"
puts
是 Ruby 关键字,将其参数输出到终端。 在我们的例子中,参数是一个字符串消息,由双引号引起来。
$ ./first.rb
This is Ruby
执行脚本将得到以上输出。
我们可以从终端读取值。 (终端和控制台是同义词)
#!/usr/bin/ruby
print "What is your name? "
name = gets
puts "Hello #{name}"
第二个程序将从控制台读取一个值并打印出来。
print "What is your name? "
print
关键字将消息打印到控制台。 print
和puts
关键字之间的区别在于,print
关键字不会开始新的一行。 puts
关键字自动开始新的一行。
name = gets
在这里,我们从用户那里读取输入并将其存储在name
变量中。 gets
是一种方法,在我们的情况下是从终端读取一条线。 这是默认情况下我们可以使用的方法之一。
puts "Hello #{name}"
在此代码行中,我们执行变量插值。 变量插值正在用字符串字面值中的值替换变量。 变量插值的另一个名称是:变量替换和变量扩展。
$ ./name.rb
What is your name? Jan
Hello Jan
这是第二个程序的输出。
Ruby 代码可以从命令行运行。 这是受 Perl 单一代码启发的,在该代码中运行一些小的代码片段来完成一些小的任务。
$ ruby -e "puts RUBY_VERSION"
1.9.3
-e
选项告诉 Ruby 执行该行上指定的 Ruby 代码,而不搜索 Ruby 文件名。 我们的示例将 Ruby 解释器的版本输出到终端。
Ruby 解释器具有-c
选项,用于检查代码的语法。 如果使用此选项,则不执行代码。 如果没有语法错误,Ruby 将在标准输出中显示“语法正确”。
#!/usr/bin/ruby
class Being end
m = Test.new
p m
在上面的示例中,存在语法错误。 如果将class
和end
关键字放在一行上,则还必须使用分号;
字符。
$ ruby -c syntax_check.rb
syntax_check.rb:3: syntax error, unexpected keyword_end, expecting '<' or ';' or '\n'
syntax_check.rb:6: syntax error, unexpected $end, expecting keyword_end
发现语法错误。 如果我们在Being
类后面加上分号,该错误消息将消失。
Ruby 命令行参数
Ruby 程序可以接收命令行参数。 当我们运行程序时,它们会遵循程序的名称。
#!/usr/bin/ruby
puts ARGV
在文件名之后指定的命令行参数可用于名为ARGV
的全局数组中的 Ruby 程序。
puts ARGV
在这里,我们将所有命令行参数打印到终端。
$ ./args.rb 1 2 3
1
2
3
我们提供了三个数字作为命令行参数,并将它们打印到控制台上。
在下面的示例中,我们将打印所有参数以及脚本名称。
#!/usr/bin/ruby
puts $0
puts $*
$0
全局变量包含正在执行的脚本的名称。 Ruby 中的全局变量以$
字符开头。 $*
是另一个全局变量。 它是ARGV
变量的同义词。 它包含为当前脚本提供的命令行参数。
$ ./args2.rb Ruby Python Perl
./args2.rb
Ruby
Python
Perl
args2.rb
脚本接收三个字符串。 脚本的名称和三个参数将显示在终端上。
Ruby 变量和常量
变量是存储数据的地方。 变量具有名称和数据类型。 数据类型是不同类型的值。 整数,字符串和浮点数是数据类型的示例。 Ruby 是一种动态语言。 这意味着我们不必(也不能)将变量声明为某种数据类型。 而是由 Ruby 解释器在分配时确定数据类型。
此外,变量可以随时间包含不同的值以及不同类型的值。 这不同于强类型的语言,例如 Java,C 或 Pascal。 与变量不同,常量(应)保留其值。 一旦初始化,便无法修改。 但是,在 Ruby 中,可以更改常量的值。 在这种情况下,会发出警告。
#!/usr/bin/ruby
city = "New York"
name = "Paul"; age = 35
nationality = "American"
puts city
puts name
puts age
puts nationality
city = "London"
puts city
在上面的示例中,我们使用四个变量。
city = "New York"
我们为城市变量分配一个字符串值。 该变量是动态创建的。
name = "Paul"; age = 35
我们再创建两个变量。 我们可以将两个语句放在一行中。 但是,为了便于阅读,每个语句应放在单独的行上。
puts city
puts name
puts age
puts nationality
我们将变量的值打印到终端。
city = "London"
我们为城市变量分配一个新值。
$ ./variables.rb
New York
Paul
35
American
London
示例的输出。
常数正如我们已经说过的,常数在一段时间内存储一个值。 与其他语言不同,该规则不是在 Ruby 中强制执行的。
#!/usr/bin/ruby
WIDTH = 100
HEIGHT = 150
var = 40
puts var
var = 50
puts var
puts WIDTH
WIDTH = 110
puts WIDTH
在此示例中,我们声明两个常量和一个变量。
WIDTH = 100
HEIGHT = 150
Ruby 中的常量以大写字母开头。 通常的做法是用大写字母写一个常量的所有字符。
var = 40
puts var
var = 50
我们声明并初始化一个变量。 稍后,我们为变量分配一个新值。 是合法的
WIDTH = 110
我们为常数分配一个新值。 常量一旦创建就不应修改。 否则,创建常量没有任何意义。 Ruby 解释器将发出警告。
$ ./constants.rb
40
50
100
./constants.rb:13: warning: already initialized constant WIDTH
110
脚本的输出。
Ruby 变量插值
变量插值正在用字符串字面值中的值替换变量。 变量插值的其他名称是变量替换和变量扩展。
#!/usr/bin/ruby
age = 34
name = "William"
puts "#{name} is #{age} years old"
在 Ruby 中,字符串是不可变的。 我们无法修改现有字符串。 变量插值发生在字符串创建期间。
age = 34
name = "William"
在这里,我们声明两个变量。
puts "#{name} is #{age} years old"
字符串消息以双引号作为边界。 当我们在#{
和}
字符之间放置一个变量名时,将对该变量进行插值:即用其值替换。
$ ./interpolation.rb
William is 34 years old
这是输出。
本章介绍了 Ruby 语言的一些基础知识。
Ruby 变量
在 Ruby 教程的这一部分中,我们将更详细地研究变量。
变量是存储数据的地方。 每个变量都有一个唯一的名称。 有一些命名约定适用于变量名。 变量保存对象。 更准确地说,它们是指位于计算机内存中的特定对象。 每个对象都是特定的数据类型。 有内置数据类型和自定义数据类型。 Ruby 属于动态语言家族。 与 Java,C 或 Pascal 等强类型语言不同,动态语言不会将变量声明为某些数据类型。 取而代之的是,解释器在分配时确定数据类型。 随着时间的流逝,Ruby 中的变量可以包含不同的值和不同类型的值。
#!/usr/bin/ruby
i = 5
puts i
i = 7
puts i
变量一词来自这样一个事实,即变量与常量不同,可以随时间采用不同的值。 在上面的示例中,有一个名为i
的变量。 首先为它分配一个值 5,然后给它一个不同的值 7。
Ruby 变量命名约定
像其他任何编程语言一样,Ruby 也有一些变量标识符的命名约定。
Ruby 是区分大小写的语言。 这意味着age
和Age
是两个不同的变量名称。 大多数语言区分大小写。 BASIC 是一个例外。 这是一种不区分大小写的语言。 虽然我们可以通过更改字符的大小写来创建不同的名称,但不建议这样做。
#!/usr/bin/ruby
i = 5
p i
I = 7
p I
该代码示例定义了两个变量:I
和i
。 它们具有不同的值。
./case.rb
5
7
case.rb
示例的输出。
Ruby 中的变量名可以由字母数字字符和下划线_
字符创建。 变量不能以数字开头。 这使解释器更容易区分文字数和变量。 变量名不能以大写字母开头。 如果标识符以大写字母开头,则在 Ruby 中将其视为常量。
#!/usr/bin/ruby
name = "Jane"
placeOfBirth = "Bratislava"
placeOfBirth = "Kosice"
favorite_season = "autumn"
n1 = 2
n2 = 4
n3 = 7
p name, placeOfBirth, favorite_season
p n1, n2, n3
在此脚本中,我们显示了一些有效的变量名。
变量名应为有意义的。 选择变量的描述性名称是一种很好的编程习惯。 这些程序更具可读性。
#!/usr/bin/ruby
name = "Jane"
place_of_birth = "Bratislava"
occupation = "student"
i = 5
while i > 0 do
puts name
i -= 1
end
该脚本显示了三个描述性变量名。 与pob
相比,place_of_birth
对程序员的描述更多。 通常认为在循环中选择简单的变量名是可以的。
Ruby 标记
变量标识符可以以也称为信号符的特殊字符开头。 标记是附加到标识符的符号。 Ruby 中的变量信号表示变量作用域。 这与 Perl 相反,后者的信号符号表示数据类型。 Ruby 变量信号为$
和@
。
#!/usr/bin/ruby
tree_name = "pine"
$car_name = "Peugeot"
@sea_name = "Black sea"
@@species = "Cat"
p local_variables
p global_variables.include? :$car_name
p self.instance_variables
p Object.class_variables
我们有四个范围不同的变量。 范围是可以引用变量的范围。 我们使用特殊的内置方法来确定变量的范围。
tree_name = "pine"
没有符号的变量是局部变量。 局部变量仅在局部有效:例如,在方法,块或模块内部。
$car_name = "Peugeot"
全局变量以$
字符开头。 它们在任何地方都有效。 程序中应限制全局变量的使用。
@sea_name = "Black sea"
以@
标记开头的变量名称是实例变量。 此变量在对象内部有效。
@@species = "Cat"
最后,我们有一个类变量。 此变量对特定类的所有实例均有效。
p local_variables
local_variables
给出了在特定上下文中定义的所有局部变量的数组。 我们的上下文是 Ruby 顶级。
p global_variables.include? :$car_name
同样,global_variables
产生一个全局数组。 我们不会将所有全局变量打印到终端,因为其中有很多。 每个 Ruby 脚本都以一堆预定义变量开头。 取而代之的是,我们调用数组的include?
方法来检查是否在数组中定义了我们的全局变量。 还要注意,我们使用变量符号来引用变量。 (符号以冒号开头。)
p self.instance_variables
self
伪变量指向instance_variables
方法的接收者。 在我们的例子中,接收者是main
,这是 Ruby 顶级执行区域。
p Object.class_variables
最后,我们有一组类变量。 main
是Object
类的实例。
$ ./sigils.rb
[:tree_name]
true
[:@sea_name]
[:@@species]
示例的输出。 我们看到变量的符号名称。
Ruby 局部变量
局部变量是在 Ruby 源代码的局部区域内有效的变量。 该区域也称为本地范围。 局部变量存在于 Ruby 模块,方法,类的定义中。
#!/usr/bin/ruby
def method1
x = 5
p x
end
method1
p x
我们有一个叫做method1
的方法,它有一个变量。 该变量是局部的。 这意味着它仅在方法定义内有效。 我们只能在方法名称和end
关键字之间引用x
变量。
def method1
x = 5
p x
end
这是method1
方法的定义。 在方法内部,我们创建一个本地x
变量。 我们将变量的值打印到终端。
method1
该方法被调用。
p x
我们尝试引用方法定义之外的局部变量。 这导致NameError
。 Ruby 解释器找不到此类标识符。
$ ./locals.rb
5
./locals.rb:11:in `<main>': undefined local variable
or method `x' for main:Object (NameError)
运行示例将得到以上输出。
以下示例是对先前示例的略微修改。
#!/usr/bin/ruby
x = 5
def method1
x = 10
p x
end
method1
p x
我们有两个x
变量。 一个在method1
内部定义,另一个在外部定义。 它们是两个不同的局部变量。 他们不会互相冲突。
x = 5
我们创建了一个本地x
变量,该变量保留值 5。该变量在主执行区的本地范围内有效。 在method1
内部无效。
def method1
x = 10
p x
end
在method1
的定义内,定义了一个新的局部变量x
。 它的值为 10。它存在于method1
方法的主体中。 在end
关键字之后,它不再存在。
$ ./locals2.rb
10
5
输出。
如果方法采用参数,则会为每个参数创建一个局部变量。
#!/usr/bin/ruby
def rectangle_area a, b
puts local_variables
return a * b
end
puts rectangle_area 5, 6
我们有一个方法定义,它有两个值。 该方法返回矩形的面积。
def rectangle_area a, b
puts local_variables
return a * b
end
rectangular_area
方法采用两个参数。 它们是矩形的边,我们将为其计算面积。 将自动为标识符a
和b
创建两个局部变量。 我们调用local_variables
方法来查看该方法中包含哪些局部变量。
puts rectangle_area 5, 6
在这里,我们将两个值传递给方法rectangle_area
。 这些值将分配给在方法内部创建的两个局部变量。
$ ./parameters.rb
a
b
30
输出显示三件事。 前两个是rectangular_area
方法中局部变量的名称。 第三是给定矩形的计算面积。
可以在另一个方法内部定义一个方法。 内部方法具有自己的局部变量。
#!/usr/bin/ruby
def method1
def method2
def method3
m5, m6 = 3
puts "Level 3"
puts local_variables
end
m3, m4 = 3
puts "Level 2"
puts local_variables
method3
end
m1, m2 = 3
puts "Level 1"
puts local_variables
method2
end
method1
在这个 Ruby 脚本中,我们创建三个方法。 method2
和method3
是内部方法。 method2
定义在method1
内部,method3
定义在method2
内部。 每个方法的局部变量只能在定义它们的方法中访问。
$ ./lms.rb
Level 1
m1
m2
Level 2
m3
m4
Level 3
m5
m6
从输出中我们可以看到method1
有两个局部变量m1
和m2
。 内部的method2
具有局部变量m3
和m4
。 最里面的方法method3
具有局部变量m5
和m6
。
本节的最后一个示例将展示本地范围的一些演示。
module ModuleM
m1, m2 = 4
puts "Inside module"
puts local_variables
end
def method1
v, w = 3
puts "Inside method"
puts local_variables
end
class Some
x, y = 2
puts "Inside class"
puts local_variables
end
method1
t1, t2 = 7
puts "Inside toplevel"
puts local_variables
在代码示例中,我们在模块,方法,类和顶层内部创建局部变量。 local_variables
是Kernel
模块的一种方法,它返回所有当前的局部变量。
module ModuleM
m1, m2 = 4
puts "Inside module"
puts local_variables
end
模块是方法和常量的集合。 我们创建两个局部变量m1
和m2
。
def method1
v, w = 3
puts "Inside method"
puts local_variables
end
在method1
中创建了两个局部变量v
和w
。
class Some
x, y = 2
puts "Inside class"
puts local_variables
end
x
和y
局部变量在Some
类的定义内创建。
t1, t2 = 7
最后,创建两个属于 Ruby 顶级本地范围的本地变量。
$ ./locals3.rb
Inside module
m1
m2
Inside class
x
y
Inside method
v
w
Inside toplevel
t1
t2
输出显示每个局部作用域的局部变量。
Ruby 全局变量
全局变量在脚本中的任何地方都有效。 他们以 Ruby 中的$
标记开头。
不鼓励使用全局变量。 全局变量很容易导致许多编程错误。 仅在有理由时才使用全局变量。 建议程序员尽可能使用局部变量来代替全局变量。
#!/usr/bin/ruby
$gb = 6
module ModuleM
puts "Inside module"
puts $gb
end
def method1
puts "Inside method"
puts $gb
end
class Some
puts "Inside class"
puts $gb
end
method1
puts "Inside toplevel"
puts $gb
puts global_variables.include? :$gb
在示例中,我们有一个全局变量$gb
。 我们展示了可以在模块,方法,类和顶级中引用该变量。 全局变量$gb
在所有这些实体中均有效。
$gb = 6
创建一个全局变量$gb
; 它的值为 6。
module ModuleM
puts "Inside module"
puts $gb
end
在模块的定义内,我们打印全局变量的值。
def method1
puts "Inside method"
puts $gb
end
在方法的定义内,我们打印全局变量的值。
class Some
puts "Inside class"
puts $gb
end
在类的定义内,我们打印全局变量的值。
puts $gb
puts global_variables.include? :$gb
最后,在顶层执行区域中,我们将打印全局变量的值以及该变量是否在global_variables
方法生成的数组中。
$ ./globals.rb
Inside module
6
Inside class
6
Inside method
6
Inside toplevel
6
true
该示例的输出确认了全局变量可在任何地方访问。
当 Ruby 脚本启动时,它可以访问多个预定义的全局变量。 这些全局变量不被认为是有害的,可以帮助解决常见的编程工作。
#!/usr/bin/ruby
p $LOAD_PATH
p $:
该脚本显示了$LOAD_PATH
全局变量。 该变量列出了通过load
和require
方法搜索的目录。 $:
是$LOAD_PATH
名称的简短同义词。
更多全局变量将在本章的“预定义变量”部分中介绍。
Ruby 实例,类变量
在本节中,我们将简要介绍实例变量和类变量。 它们将在面向对象的编程章节中更详细地描述。
实例变量是属于特定对象实例的变量。 每个对象都有其自己的对象变量。 实例变量以@
标记开头。 类变量属于特定类。 从特定类创建的所有对象共享类变量。 类变量以@@
字符开头。
#!/usr/bin/ruby
class Being
@@is = true
def initialize nm
@name = nm
end
def to_s
"This is #{@name}"
end
def does_exist?
@@is
end
end
b1 = Being.new "Being 1"
b2 = Being.new "Being 2"
b3 = Being.new "Being 3"
p b1, b2, b3
p b1.does_exist?
p b2.does_exist?
p b3.does_exist?
我们创建一个自定义的Being
类。 Being
类具有一个类和一个实例变量。
class Being
@@is = true
@@is
是一个类变量。 Being
类的所有实例都共享此变量。 此示例的逻辑是Being
是,NotBeing
不是。
def initialize nm
@name = nm
end
initialize
方法是一个构造器。 创建对象时将调用该方法。 创建一个@name
实例变量。 此变量特定于具体对象。
def to_s
"This is #{@name}"
end
当对象是打印方法的参数(例如p
或puts
)时,将调用to_s
方法。 在我们的情况下,该方法给出了对象的简短人类可读描述。
def does_exist?
@@is
end
does_exist?
方法返回类变量。
b1 = Being.new "Being 1"
b2 = Being.new "Being 2"
b3 = Being.new "Being 3"
从Being
类中创建了三个对象。 每个对象都有不同的名称。 对象的名称将存储在实例方法中,该方法对于每个对象实例都是唯一的。 这将在to_s
方法中使用,该方法给出了对象的简短说明。
p b1, b2, b3
p
方法将创建的对象作为三个参数。 它在每个这些对象上调用to_s
方法。
p b1.does_exist?
p b2.does_exist?
p b3.does_exist?
最后,我们调用每个实例的does_exist?
方法并打印其返回值。 这三个方法的输出是相同的,因为每个方法都返回类变量。
$ ./icvars.rb
This is Being 1
This is Being 2
This is Being 3
true
true
true
示例的输出。 前三个消息是唯一的。 字符串存储在对象的实例变量中。 真实值是类变量的值,它被调用了三次。
Ruby 环境&命令行变量
ENV
常数允许访问环境变量。 这是一个 Ruby 哈希。 每个环境变量都是ENV
哈希的键。
ARGV
常量保存命令行参数值。 它们在脚本启动时由程序员传递。 ARGV
是一个将参数存储为字符串的数组。 $*
是ARGV
的别名。
ENV
和ARGV
都是全局常数。
#!/usr/bin/ruby
ARGV.each do |a|
puts "Argument: #{a}"
end
在脚本中,我们遍历ARGV
数组并打印其每个值。
$ ./commandline.rb 1 2 3
Argument: 1
Argument: 2
Argument: 3
我们给出了三个命令行参数。 它们被打印到控制台,每个都打印在单独的行上。
以下示例将处理环境变量。
#!/usr/bin/ruby
puts ENV['SHELL']
puts ENV['LANG']
puts ENV['TERM']
该脚本会将三个环境变量的值输出到终端。 这些值取决于我们操作系统的 OS 设置。
$ ./environment.rb
/bin/bash
en_US.utf8
xterm
样本输出。
Ruby 伪变量
Ruby 具有一些称为伪变量的变量。 它们不同于常规变量。 我们不能为伪变量赋值。
self
是当前方法的接收者。 nil
是NilClass
的唯一实例。 它表示一个值的缺失。 true
是TrueClass
的唯一实例。 它表示布尔值true
。 false
是FalseClass
的唯一实例。 它表示布尔值false
。
true
和false
是布尔数据类型的值。 从另一个角度来看,它们是特定类的实例。 这是因为 Ruby 中的所有内容都是一个对象。 这看起来不必要地复杂。 但这是上述 Ruby 习惯用法的结果。
#!/usr/bin/ruby
p self
p nil
p true
p false
p self.class
p nil.class
p true.class
p false.class
这是伪变量的示例。 我们使用p
方法打印所有四个伪变量。 然后我们找出所有它们的类名。
p self
在这种情况下,self
伪变量返回主执行上下文。
$ ./pseudo.rb
main
nil
true
false
Object
NilClass
TrueClass
FalseClass
示例输出。
在本节的第二个示例中,我们将进一步查看self
。
#!/usr/bin/ruby
class Some
puts self
end
class Other
puts self
end
puts self
如前所述,self
引用了当前方法的接收者。 上面的示例显示了不同接收器的三个示例。
class Some
puts self
end
接收器是称为Some
的类。
class Other
puts self
end
这是另一个接收器:名为Other
的类。
puts self
第三个接收器是 Ruby 顶级。
$ ./pseudoself.rb
Some
Other
main
示例输出。
本节的最后一个示例将展示其他三个伪变量。
#!/usr/bin/ruby
if true
puts "This message is shown"
end
if false
puts "This message is not shown"
end
p $name
p $age
上面的示例显示了正在使用的true
,false
和nil
伪变量。
if true
puts "This message is shown"
end
true
用于布尔表达式。 该消息始终被打印。
if false
puts "This message is not shown"
end
此消息从不打印。 不满足条件。 在布尔表达式中,我们总是得到一个负值。
p $name
p $age
如果引用了全局值且尚未初始化,则它们包含nil
伪变量。 它代表没有值。
$ ./pseudo2.rb
This message is shown
nil
nil
伪脚本pseudo2.rb
的输出。
Ruby 预定义变量
Ruby 有很多预定义的全局变量。 这是 Perl 语言的传承。 Ruby 受 Perl 的强烈影响。 当 Ruby 脚本启动时,它们是可访问的。 我们为预定义的 Ruby 变量提供了一些示例。
#!/usr/bin/ruby
print "Script name: ", $0, "\n"
print "Command line arguments: ", $*, "\n"
puts "Process number of this script: #{$$}"
已经使用了三个预定义变量:$0
,$*
和$$
。 $0
存储当前脚本名称。 $*
变量存储命令行参数。 并且$$
存储脚本的 PID(进程 ID)。
$ ./predefined.rb 1 2 3
Script name: ./predefined.rb
Command line arguments: ["1", "2", "3"]
Process number of this script: 3122
样本输出。
$?
全局变量存储最后执行的子进程的退出状态。
#!/usr/bin/ruby
system 'echo "Ruby"'
puts $?
%x[exit '1']
puts $?
我们运行两个外部子进程,并使用$?
变量检查其退出状态。
system 'echo "Ruby"'
puts $?
使用system
方法,我们启动了一个子进程。 这是一个echo
bash 命令,该命令将消息输出到终端。
%x[exit '1']
puts $?
在第二种情况下,我们以状态 1 执行 bash exit
命令。这一次,我们使用%x
运算符在两个选定的定界符之间执行命令。 我们选择了[]
字符。
$ ./predefined2.rb
Ruby
pid 3131 exit 0
pid 3133 exit 1
第一个子进程以状态 0 终止,第二个子进程以退出状态 1 终止。
$;
变量具有String
类的split
方法的默认分隔符。
#!/usr/bin/ruby
str = "1,2,3,4,5,6,7"
p str.split
$; = ","
p str.split
我们使用$;
变量来控制如何使用split
方法剪切字符串。 该方法带有一个参数,该参数指示应在何处分割字符串。 如果省略该参数,则使用$;
中的值。
$; = ","
p str.split
我们为$;
变量指定分隔符。 split
方法不带参数,因此使用$;
的值。
$ ./predefined3.rb
["1,2,3,4,5,6,7"]
["1", "2", "3", "4", "5", "6", "7"]
在第一种情况下,字符串未拆分。 在第二种情况下,按照我们的预期正确分割了字符串。
在最后一个示例中,我们显示了三个与正则表达式一起使用的全局预定义变量。
#!/usr/bin/ruby
"Her name is Jane" =~ /name/
p $`
p $&
p $'
当我们在字符串上应用=~
运算符时,Ruby 会设置一些变量。 $&
变量的字符串与最后一个正则表达式匹配相匹配。 $`
在$&
之前有一个字符串,$'
在$&
之后有一个字符串。
$ ./predefined4.rb
"Her "
"name"
" is Jane"
Example output.
在 Ruby 教程的这一部分中,我们更深入地研究了 Ruby 变量。
Ruby 中的对象
在 Ruby 教程的这一部分中,我们将简要介绍 Ruby 语言中对象的概念。 我们将在 OOP 一章中了解有关对象的更多信息。 我写了有关对象的本章,因为许多 Ruby 功能可能会使新手感到困惑,特别是如果他们已经知道任何其他编程语言的话。
Ruby 是一种面向对象的编程语言。 这意味着在 Ruby 程序中,我们使用对象。 从语言程序员的角度来看,Ruby 程序是令牌流。 这些标记是 Ruby 关键字,运算符,各种分隔符或字面值。 从语义的角度来看,Ruby 程序由对象组成。 这些对象是在 Ruby 脚本的生存期内创建和修改的。
有两种对象:内置对象和自定义对象。 内置对象是所有程序员都可以使用的预定义对象。 它们以 Ruby 语言的核心或各种库提供。 定制对象是由应用员为其应用域创建的。
必须先创建所有对象,然后才能使用它们。 我们经常使用术语对象实例化。 它是对象创建的同义词。 对象由数据和方法组成。 数据是对象的静态部分。 方法构成对象的动态部分。 对象被修改并通过方法彼此通信。
#!/usr/bin/ruby
puts "Ruby language"
我们有一个简单的 Ruby 脚本。 如果我们熟悉某些程序语言(例如 Pascal 或 C),我们可能会看到一个名为puts
的关键字或函数及其参数"Ruby language"
(它是一个字符串)。
Ruby 是一种纯粹的面向对象的语言,情况有所不同。 "Ruby language"
确实是一个字符串,它是一种常见的数据类型。 但这也是一个对象。 与所有对象一样,我们可以调用其方法。 这与其他语言有点不同。 puts
是一种方法。 方法是在对象中定义的函数。 方法本身并不存在。 实际上,puts
方法是Kernel
模块的一部分。
#!/usr/bin/ruby
Kernel.puts "Ruby language"
Kernel.puts "Ruby language".size
在上面的脚本中,我们有两个代码行。
Kernel.puts "Ruby language"
在第一个示例中,我们调用的puts
方法没有Kernel
部分,可以将其省略。 这样可以节省时间和一些打字。 实际上,这是Kernel.puts
正式电话的简写。 在 C# 中,我们有Console.writeln
在 Java 中System.println
。 想法是一样的。 方法必须与某个对象关联,或者,如果是类方法,则必须与一个类关联。
Kernel.puts "Ruby language".size
在此代码行中,我们将"Ruby language"
字符串的大小打印到控制台。 这可能会使使用其他语言编写代码的程序员感到困惑。 在其他语言中,字符串是无法修改的原始数据类型,并且缺少其自己的方法。 在 Ruby 中,字符串是完整对象,并且具有自己的方法。 size
方法就是其中一种。 它以字符为单位返回字符串的大小。
$ ./simple2.rb
Ruby language
13
代码示例的输出。
在下面的示例中,我们将看一个整数。 与字符串类似,整数值也是 Ruby 对象。
#!/usr/bin/ruby
puts 6.object_id
puts 6.even?
puts 6.zero?
puts 6.class
在示例中,我们有一个整数 6。我们在数字上调用了一些方法。
puts 6.object_id
6 是一个对象。 object_id
是一种方法。 该方法返回与该对象关联的 ID。 每个对象都有一个 ID。 如果我们在对象上调用方法,则必须始终在两者之间放置点字符。
puts 6.even?
puts 6.zero?
在这里,我们在 6 个对象上调用两个方法。 如果数字为偶数,则even?
返回true
。 如果数字等于零,则zero?
方法返回true
。 请注意,这两种方法都以问号结尾。 这是 Ruby 约定。 返回布尔值的方法以问号结尾。
puts 6.class
class
方法告诉我们正在处理哪种对象。 在我们的例子中 6 是Fixnum
$ ./objectnumber.rb
13
true
false
Fixnum
代码示例输出。
Ruby 对象创建
我们已经提到必须先创建 Ruby 对象,然后才能使用它们。 可以隐式或显式创建对象。 隐式对象创建是通过字面值符号创建对象。 显式对象的创建通过使用new
关键字进行。 始终使用new
关键字创建自定义对象。 必须从特定的类创建自定义对象。 类是对象的模板。 一个类可以用来创建许多对象。
#!/usr/bin/ruby
class Being
end
puts 67
puts "ZetCode"
s = String.new "ZetCode"
puts s
# n1 = Fixnum.new 67
# puts n1
b = Being.new
puts b
该代码示例演示了如何在 Ruby 中创建对象。
class Being
end
这是名为Being
的自定义对象的模板。 使用class
关键字创建模板。 自定义对象的模板通常放在源文件的顶部或单独的 Ruby 文件中。
puts 67
puts "ZetCode"
在这两行中,我们使用两个对象。 Fixnum
类型的 67 对象和String
类型的"ZetCode"
字符串。 67 和"ZetCode"
是我们所谓的字面值。 字面值是类型的特定值的字面值表示。 这两个对象是由 Ruby 解释器在后台创建的。 Ruby 中的某些对象是通过在源代码中指定其字面值来创建的。
s = String.new "ZetCode"
puts s
这是创建String
对象的正式方法。 它等于之前使用字符串字面值的隐式创建。
# n1 = Fixnum.new 67
# puts n1
并非所有内置对象都可以使用new
方法创建。 此代码无法编译。 到目前为止,Fixnum
数字只能用字面值表示法创建。
b = Being.new
puts b
在这里,我们创建了自定义对象的实例。 puts
方法为我们简要介绍了该对象。
$ ./ocreation.rb
67
ZetCode
ZetCode
#<Being:0x9944d9c>
示例的输出。
我们将继续进行一些正式的对象创建。
#!/usr/bin/ruby
s1 = String.new "Ruby"
puts s1.size
puts s1.downcase
a1 = Array.new
a1.push 1, 2, 3
puts a1.include? 3
puts a1.empty?
r1 = Range.new 1, 6
puts r1.class
puts r1.include? 4
在示例中,我们创建了三个内置对象并调用了其中的一些方法。
s1 = String.new "Ruby"
puts s1.size
puts s1.downcase
创建一个String
对象。 我们调用对象的两种方法。 size
方法返回字符串的大小。 downcase
方法将字符串的字符小写。
a1 = Array.new
a1.push 1, 2, 3
puts a1.include? 3
puts a1.empty?
在这里,我们创建一个Array
对象,并向其添加三个数字。 稍后我们调用两个数组方法。 include?
方法检查特定值(在我们的例子中为 3)是否是数组的一部分。 empty?
方法返回一个布尔值,指示该数组是否为空。
r1 = Range.new 1, 6
puts r1.class
puts r1.include? 4
创建Range
类的实例。 它包含从 1 到 6 的数字。class
方法返回对象的名称。 include?
方法检查数字 4 是否在范围内。 就我们而言。
$ ./formal.rb
4
ruby
true
false
Range
true
运行示例将给出此输出。
Ruby 对象字面值
正如我们已经提到的,可以使用对象字面值来创建一些内置对象。 以下示例显示了几个对象字面值。
#!/usr/bin/ruby
4.times { puts "Ruby" }
puts "Ruby".size
puts "Ruby".downcase
puts [1, 2, 3].include? 3
puts [1, 2, 3].empty?
puts :name.class
puts :name.frozen?
puts (1..6).class
puts (1..6).include? 4
在上面的示例中,我们使用字面值符号创建一个Fixnum
,字符串,数组,符号和范围。
4.times { puts "Ruby" }
我们可以立即在整数字面值上调用方法。 该行向终端打印四次"Ruby"
字符串。
puts "Ruby".size
puts "Ruby".downcase
我们在用字符串字面值创建的String
对象上调用两个方法。
puts [1, 2, 3].include? 3
puts [1, 2, 3].empty?
在这里,我们使用数组字面值符号创建两个Array
对象。 我们使用include?
方法检查特定的数字是否是数组的一部分。 empty?
方法检查数组对象是否为空。
puts :name.class
puts :name.frozen?
调用 Symbol 对象的两种方法。 该符号是用一个以冒号开头的符号字面值创建的。
puts (1..6).class
puts (1..6).include? 4
使用范围字面值创建两个Range
对象。 我们在那些对象上调用两个方法。 class
方法返回类的名称,include?
方法检查给定数字是否在范围内。
$ ./literals.rb
Ruby
Ruby
Ruby
Ruby
4
ruby
true
false
Symbol
false
Range
true
示例输出。
Ruby 对象层次结构
在许多面向对象的语言中,对象形成层次结构。 Ruby 也具有对象层次结构。 它是一个树状的层次结构,其中有父对象和子对象。 对象从其父对象继承数据和行为。 在层次结构的顶部,有根对象。 它称为Object
。 Ruby 中的每个对象都有至少一个父对象。 换句话说,每个对象都继承自基本的Object
对象。
根据 Ruby 的官方文档,Object
是 Ruby 类层次结构的根。 除非明确重写,否则它的方法可用于所有类。
#!/usr/bin/ruby
puts 4.is_a? Object
puts "Ruby".is_a? Object
puts [2, 3].is_a? Object
puts :name.is_a? Object
puts (1..2).is_a? Object
在上面的代码示例中,我们演示了所有对象均从根Object
继承
puts 4.is_a? Object
我们使用is_a?
方法检查数字是否为特定类型:换句话说,数字是否继承自给定的对象类型。
$ ./mother.rb
true
true
true
true
true
所有方法均返回true
,这意味着所有对象均从母对象继承。
即使对于非常基本的 Ruby 对象,继承层次结构也可能非常复杂。
#!/usr/bin/ruby
puts 6.class
puts 6.is_a? BasicObject
puts 6.is_a? Object
puts 6.is_a? Numeric
puts 6.is_a? Integer
puts 6.is_a? Fixnum
puts 6.is_a? Bignum
puts 6.is_a? String
在此示例中,我们对较小数值的继承层次结构有所了解。
puts 6.class
我们找出数字值为 6 的对象是什么。该行将Fixnum
打印到控制台。
puts 6.is_a? BasicObject
puts 6.is_a? Object
puts 6.is_a? Numeric
puts 6.is_a? Integer
puts 6.is_a? Fixnum
以上所有行均返回true
。 数字 6 是Fixnum
。 从 Ruby 文档中,我们发现其他四个对象是Fixnum
对象的父对象。
puts 6.is_a? Bignum
puts 6.is_a? String
以上两个对象不是 6 值的父对象。
$ ./inheritance.rb
Fixnum
true
true
true
true
true
false
false
示例的输出。
我们将以一个示例结束本节,该示例演示自定义用户对象的继承。
#!/usr/bin/ruby
class Being
def to_s
"This is Being"
end
def get_id
9
end
end
class Living < Being
def to_s
"This is Living"
end
end
l = Living.new
puts l
puts l.get_id
puts l.is_a? Being
puts l.is_a? Object
puts l.is_a? BasicObject
在示例中,我们创建两个对象Being
和Living
。 Living
对象继承自Being
。 第一个是父对象,第二个是子对象。
class Being
def to_s
"This is Being"
end
def get_id
9
end
end
这是自定义 Ruby 对象的定义。 定义位于class
和end
关键字之间。 在定义内部,我们创建两个方法。 当puts
方法将对象作为参数时,它将调用其to_s
方法。 它通常给出对象的字符串表示/描述。
class Living < Being
def to_s
"This is Living"
end
end
我们创建Living
对象的定义。 该对象继承自Being
对象。 <
运算符用于创建继承关系。 to_s
方法被覆盖。
l = Living.new
从上面的Living
对象模板,我们创建Living
对象的实例。 使用new
关键字创建自定义对象的实例。
puts l
puts
方法调用Living
对象的to_s
方法。 如果Living
类中未定义to_s
方法,则将调用Being
类中的to_s
方法。
puts l.get_id
Living
对象未定义get_id
方法。 在这种情况下,将检查父类是否存在这种方法。 在我们的情况下,Being
方法具有这种方法,因此被称为。
puts l.is_a? Being
该行返回true
。 Living
是Being
的一种; 例如它继承自Being
类。
puts l.is_a? Object
puts l.is_a? BasicObject
对于我们的Living
自定义对象,我们没有明确指定与Object
或BasicObject
对象的任何关系。 但是这两行返回true
。 这是因为 Ruby 中的每个对象自动都是这两个对象的后代。 这是由 Ruby 解释器在后台完成的。
$ ./custominher.rb
This is Living
9
true
true
true
Output of the example.
Ruby 顶级
Ruby 有一个特定的对象,称为 Ruby 顶级。 这是在任何其他上下文(例如类或模块定义)之外定义的默认执行环境。 顶级名称为main
。 它是Object
类型的实例。 有一个与main
关联的局部空间,所有局部变量都驻留在该空间中。
#!/usr/bin/ruby
n1 = 3
n2 = 5
puts local_variables
Kernel.puts self
puts self.class
这是描述 Ruby 顶层的第一个示例。
n1 = 3
n2 = 5
在这里,我们定义了两个数值变量。 这些变量是本地变量。
puts local_variables
在这里,我们列出了所有局部变量。 local_variables
是Kernel
模块的一种方法,已混入每个Object
(包括顶层对象)中。
Kernel.puts self
self
是 Ruby 伪变量。 它返回当前的对象接收者。 该行将"main"
打印到控制台。 它是 Ruby 顶层的名称。 Kernel.puts
代码的Kernel
部分可以省略。 通过完全指定名称,我们显示puts
方法属于Kernel
模块。
puts self.class
该行显示顶层的class
。 我们得到顶层的对象类型。 它是Object
,它是 Ruby 类层次结构的根。
$ ./toplevel.rb
n1
n2
main
Object
这是示例的输出。 n1
和n2
是与顶层关联的局部变量。 主要是为 Ruby 顶级执行环境指定的名称。 最后,Object
是顶层的类型。
我们将有另一个与 Ruby 顶层相关的示例。
#!/usr/bin/ruby
@name = "Jane"
@age = 17
def info
"#{@name} is #{@age} years old"
end
puts self.instance_variables
puts self.private_methods.include? :info
puts info
我们显示了属于顶级环境的实例变量和方法。
@name = "Jane"
@age = 17
我们定义了两个实例变量。 实例变量以 Ruby 中的@
字符开头。 实例变量属于特定的对象实例。 在这种情况下,它们属于 Ruby 顶级。
def info
"#{@name} is #{@age} years old"
end
这是一个方法定义。 每个方法必须属于某个对象。 此方法属于顶级对象。 所有顶级方法都是私有的。 私有方法的访问受到限制。
puts self.instance_variables
instance_variables
方法打印出self
的所有实例变量,在此上下文中指向 Ruby 顶层。
puts self.private_methods.include? :info
所有顶级方法都是自动私有的。 private_methods
返回对象的所有私有方法。 由于方法很多,我们调用include?
方法来检查info
方法是否在其中。 请注意,我们通过符号名称来引用info
方法。
$ ./toplevel2.rb
@name
@age
true
Jane is 17 years old
示例输出。
本章介绍了 Ruby 语言中对象的一些基础知识。
Ruby 数据类型
在 Ruby 教程的这一部分中,我们将讨论数据类型。
各种计算机程序(包括电子表格,文本编辑器,计算器和聊天客户端)都可以处理数据。 用于各种数据类型的工具是现代计算机语言的基本组成部分。 数据类型是一组值,以及对这些值的允许操作。
Ruby 数据类型列表
Ruby 有几种数据类型。 所有数据类型均基于类。 以下是 Ruby 识别的数据类型:
- 布尔值
- 符号
- 数字
- 字符串
- 数组
- 哈希
在以下示例中,我们具有所有重要的 Ruby 数据类型。
#!/usr/bin/ruby
h = { :name => "Jane", :age => 17 }
p true.class, false.class
p "Ruby".class
p 1.class
p 4.5.class
p 3_463_456_457.class
p :age.class
p [1, 2, 3].class
p h.class
我们打印他们的类名称。 一个类是从每个对象创建的模板。
p true.class, false.class
布尔值由true
和false
对象表示。
p "Ruby".class
这是字符串。
p 1.class
p 4.5.class
p 3_463_456_457.class
这些是数字。
p :age.class
这是一个符号,一种特定于 Ruby 的数据类型。
p [1, 2, 3].class
p h.class
这是两个容器,数组和哈希。
$ ./types.rb
TrueClass
FalseClass
String
Fixnum
Float
Bignum
Symbol
Array
Hash
该程序列出了属于 Ruby 数据类型的类。
Ruby 布尔值
我们的世界建立了双重性。 有天与地,水与火,阴与阳,男人与女人,爱与恨。 这就是我们生存的“布尔”性质。 在 Ruby 中,布尔数据类型可以具有两个值之一:true
或false
。 布尔是一种基本的数据类型:在计算机程序中非常常见的一种。
快乐的父母正在等待孩子的出生。 他们为两种可能性都选择了名称。 如果要成为男孩,他们选择了约翰。 如果要成为女孩,他们会选择维多利亚。
#!/usr/bin/ruby
# kid.rb
bool = [true, false]
male = bool[rand(2)]
if male
puts "We will use name John"
else
puts "We will use name Victoria"
end
该程序使用随机数生成器来模拟我们的情况。
bool = [true, false]
我们有一个bool
变量。 它是两个布尔值的数组。 用方括号创建一个数组。
male = bool[rand(2)]
我们使用rand()
方法创建一个随机数。 该方法返回 0 或 1。返回的数字是bool
数组的索引。
if male
puts "We will use name John"
else
puts "We will use name Victoria"
end
根据公变量,我们打印一条消息。 如果将male
变量设置为true
,则选择名称 John。 否则,我们选择名称 Victoria。 诸如if/else
语句之类的控制结构可使用布尔值。
$ ./kid.rb
We will use name Victoria
$ ./kid.rb
We will use name Victoria
$ ./kid.rb
We will use name John
$ ./kid.rb
We will use name John
$ ./kid.rb
We will use name John
多次运行该程序。
Ruby 符号
符号用于表示其他对象。 使用符号代替字符串可以节省一些资源。 符号是Symbol
类的实例对象。 通过在标识符(如:name
)之前使用冒号来生成符号。 几个对象也具有to_sym
方法。 这些方法将那些对象转换为符号。
Ruby 符号不能在运行时更改。 Ruby 符号通常用作哈希键,因为我们不需要键的字符串对象的全部功能。
#!/usr/bin/ruby
p :name
p :name.class
p :name.methods.size
p "Jane".methods.size
p :name.object_id
p :name.object_id
p "name".object_id
p "name".object_id
在第一个示例中,我们展示了一些使用 Ruby 符号的基本操作。
p :name
p :name.class
我们将符号及其类输出到控制台。 符号的类别为Symbol
。
p :name.methods.size
p "Jane".methods.size
我们比较了与符号和字符串实例关联的方法的数量。 字符串的方法数量是符号的两倍以上。
p :name.object_id
p :name.object_id
p "name".object_id
p "name".object_id
相同的符号具有相同的 ID。 相同的字符串具有不同的 ID。
$ ./symbols.rb
:name
Symbol
79
162
10328
10328
77344750
77344730
样本输出。
符号可以用作标志。 在这种情况下也可以使用常量。 在 C/C++ 中,我们将使用枚举。
#!/usr/bin/ruby
light = :on
if light == :on
puts "The light is on"
else
puts "The light is off"
end
light = :off
if light == :on
puts "The light is on"
else
puts "The light is off"
end
指示灯点亮或熄灭。 对于这两种状态,我们都可以定义符号。
light = :on
灯亮。
if light == :on
puts "The light is on"
else
puts "The light is off"
end
程序的逻辑取决于light
变量的状态。
符号通常用作哈希容器中的键。 它们比字符串更有效。
#!/usr/bin/ruby
domains = {:sk => "Slovakia", :no => "Norway", :hu => "Hungary"}
puts domains[:sk]
puts domains[:no]
puts domains[:hu]
在脚本中,我们有一个域哈希。 哈希中的键是符号。
puts domains[:sk]
puts domains[:no]
puts domains[:hu]
键用于访问哈希值。 在这里,我们打印哈希的三个值。
$ ./symbols3.rb
Slovakia
Norway
Hungary
示例的输出。
Ruby 解释器在内部将一些引用存储为符号。
#!/usr/bin/ruby
class Being
def initialize
@is = true
end
def say
"I am being"
end
end
b = Being.new
p b.method :say
p b.instance_variable_get :@is
定义了Being
类。 该类具有一个自定义实例变量@is
和say
方法。 这两个实体由 Ruby 使用符号存储。
p b.method :say
method
方法在b
对象中查找具有给定名称的接收器方法。 我们在寻找:say
符号。
p b.instance_variable_get :@is
我们使用instance_variable_get
检查@is
是否是b
对象的实例变量。 在内部,变量存储为:@is
符号。
$ ./symbols4.rb
#<Method: Being#say>
true
生成的输出。
所有符号都存储在符号表中。 在下一个示例中,我们看一下表格。 Symbol
类的all_symbols
方法返回表中所有符号的数组。
#!/usr/bin/ruby
def info
"info method"
end
@v = "Ruby"
@@n = "16"
p Symbol.all_symbols.include? :info
p Symbol.all_symbols.include? :@v
p Symbol.all_symbols.include? :@@n
在 Ruby 脚本中创建方法,实例变量和类变量。 我们检查这些实体是否存储在符号表中。
p Symbol.all_symbols.include? :info
我们检查:info
符号是否在符号表中。 该行返回true
。
$ ./symbols5.rb
true
true
true
这三个符号都存在于 Ruby 符号表中。
Ruby 整数
整数是实数的子集。 它们写时没有小数或小数部分。 整数属于集合Z = {..., -2, -1, 0, 1, 2, ......}
此集合是无限的。
在计算机语言中,整数是原始数据类型。 实际上,由于计算机的容量有限,因此计算机只能使用整数值的子集。 整数用于计算离散实体。 我们可以有 3、4 或 6 个人,但不能有 3.33 个人。 我们可以有 3.33 公斤。
整数是 Ruby 中Fixnum
或Bignum
类的实例对象。 与 Java 或 C 这样的语言不同,Ruby 中的整数是对象。 这两个类别的大小不同。 Fixnum
数字是不超过一定限制的整数。 该限制取决于机器。 Bignum
值保存Fixnum
范围之外的整数。 如果对Fixnum
的任何操作超出其范围,则该值将自动转换为Bignum
。 程序员通常不需要关心整数的类类型。
#!/usr/bin/ruby
p -2
p 121
p 123265
p -34253464356
p 34867367893463476
p 1.class
p 23453246.class
p 234532423563456346.class
p 2345324235632363463456456346.class
p 5 / 2
p 5.div 2
在此示例中,我们处理整数。
p -2
p 121
p 123265
p -34253464356
p 34867367893463476
这些是各种大小的正整数和负整数。
p 1.class
p 23453246.class
p 234532423563456346.class
p 2345324235632363463456456346.class
我们打印这些整数的类。 前两个整数是Fixnum
类的实例。 另外两个是Bignum
类的实例。
p 5 / 2
p 5.div 2
这两行显示整数除法。 当我们使用整数除法运算符/方法除以两个整数时,结果也是一个整数。
$ ./integers.rb
-2
121
123265
-34253464356
34867367893463476
Fixnum
Fixnum
Bignum
Bignum
2
2
示例的输出。
可以在 Ruby 中使用不同的表示法指定整数:十进制,十六进制,八进制和二进制。 据我们所知,通常使用十进制数。 十六进制数字以0x
字符开头,八进制以0
字符开头,二进制以0b
字符开头。
#!/usr/bin/ruby
puts 122
puts 0x7a
puts 0172
puts 0b1111010
在代码示例中,我们以所有这些符号打印十进制 122。
$ ./inotations.rb
122
122
122
122
示例的输出。
如果我们使用整数,那么我们将处理离散实体。 我们将使用整数来计算苹果。
#!/usr/bin/ruby
baskets = 16
apples_in_basket = 24
total = baskets * apples_in_basket
puts "There are total of #{total} apples"
在我们的程序中,我们计算了苹果的总量。 我们使用整数。
$ ./apples.rb
There are total of 384 apples
程序的输出。
大数字很难阅读。 如果我们有一个像 245342395423452 这样的数字,我们会发现很难快速阅读。 在计算机外部,大数字之间用空格或逗号分隔。 为了提高可读性,Ruby 允许整数包含下划线。 Ruby 解释器将忽略整数中的下划线。
#!/usr/bin/ruby
p 23482345629
p 23_482_345_629
p 23482345629 == 23_482_345_629
该示例演示了下划线的用法。
p 23482345629 == 23_482_345_629
此行表明两个数字相等。 它打印真实。
$ ./underscore.rb
23482345629
23482345629
true
示例输出。
Ruby 浮点数
浮点数表示计算中的实数。 实数测量连续的量,例如重量,高度或速度。 在 Ruby 中,十进制数字是Float
或BigDecimal
类的对象。 BigDecimal
类是 Ruby 的核心类,是 Ruby 标准库的一部分。 另外,我们也可以使用Rational
对象。
我们需要了解十进制数字并不精确。 Ruby 的官方文档明确指出,浮点对象表示不精确的实数。
#!/usr/bin/ruby
p 15.4
p 0.3455
p -343.4563
p 12.5.class
p -12.5.class
p (5.0 / 2).class
p 5.fdiv 2
p 12.to_f
在上面的程序中,我们使用浮点值。
p 15.4
p 0.3455
p -343.4563
在这里,我们打印三个十进制值。 小数点有小数点字符。
p 12.5.class
p -12.5.class
p (5.0 / 2).class
上面的代码行显示了数字的类型。 全部都是花车。 至少在一个Float
上应用整数除法也会产生Float
。
p 5.fdiv 2
p 12.to_f
在这里,我们通过使用浮点fdiv
除法和转换to_f
方法来创建浮点值。
$ ./decimals.rb
15.4
0.3455
-343.4563
Float
Float
Float
2.5
12.0
输出。
默认情况下,显示的十进制数字在小数点后最多 16 个数字。 我们可以使用sprintf
或printf
方法控制浮点值的格式。
#!/usr/bin/ruby
p 1/3.0
p 1.fdiv 2
puts sprintf "%.4f" % (1/3.0)
puts sprintf "%.7f" % (5/3.0)
格式化十进制数字。
p 1/3.0
p 13.fdiv 4
p 1.fdiv 2
第一行打印一个小数点后的 16 位。 第二行在该点之后打印两个数字,第三行打印。
puts sprintf "%.4f" % (1/3.0)
puts sprintf "%.7f" % (5/3.0)
在这里,我们使用sprintf
方法控制小数点后的值数量。 sprintf
方法的格式说明符存在精度。 它是%
字符后的数字。 f
是一个转换说明符,表示我们正在处理浮点值。
$ ./formatfloat.rb
0.3333333333333333
3.25
0.5
0.3333
1.6666667
Output.
Ruby 支持对浮点值使用科学计数法。 也称为指数表示法,它是一种写数字太大或太小而不能方便地用标准十进制表示法写的方式。
#!/usr/bin/ruby
p 1.2e-3
p 0.0012
p 1.5E-4
p 0.00015
该示例显示了两个用科学计数法表示的十进制数字。
$ ./scientific.rb
0.0012
0.0012
0.00015
0.00015
这是上面程序的输出。
如前所述,浮点值有些不准确。 对于许多计算,普通的浮点数足够精确: 如果我们的体重是 60kg 或 60.000023kg,那不是很重要。 对于其他计算,包括许多科学和工程应用,精度至关重要。
Ruby 在标准库中有一个BigDecimal
。 此类为非常大或非常准确的浮点数提供任意精度。
#!/usr/bin/ruby
require 'bigdecimal'
sum = 0
1000.times do
sum = sum + 0.0001
end
p sum
sum = BigDecimal.new("0")
1000.times do
sum = sum + BigDecimal.new("0.0001")
end
puts sum.to_s('F')
puts sum.to_s('E')
在这个简单的示例中,我们比较了Float
与BigDecimal
的精度。
require 'bigdecimal'
必须导入BigDecimal
类。
sum = 0
1000.times do
sum = sum + 0.0001
end
p sum
我们形成一个循环,在该循环中,我们将一个小的浮点值添加到sum
变量中。 最后,将有一个小的误差。
sum = BigDecimal.new("0")
1000.times do
sum = sum + BigDecimal.new("0.0001")
end
我们使用BigDecimal
值进行相同的循环。
puts sum.to_s('F')
puts sum.to_s('E')
总和以浮点数和工程符号形式打印。
$ ./bigdecimal.rb
0.10000000000000184
0.1
0.1E0
输出显示,使用BigDecimal
进行的计算比使用Floats
进行的计算更为精确。
假设一个短跑运动员跑了 1 个小时,跑了 9.87 秒。 他的公里/小时速度是多少?
#!/usr/bin/ruby
distance = 0.1
time = 9.87 / 3600
speed = distance / time
puts "The average speed of a sprinter is #{speed} km/h"
在此示例中,必须使用浮点值。
distance = 0.1
100m 是 0.1 公里。
time = 9.87 / 3600
9.87s 是9.87 / 60 * 60 h
speed = distance / time
为了获得速度,我们将距离除以时间。
$ ./speed.rb
The average speed of a sprinter is 36.4741641337386 km/h
这是speed.rb
脚本的输出。
Ruby 有理数
Ruby 支持有理数。 有理数是精确数。 使用有理数可以避免舍入误差。 在 Ruby 中,有理数是Rational
类的对象。 我们可以使用特殊的to_r
方法从某些对象创建有理数。
有理数是可以表示为两个整数a / b
的分数的任何数字,其中b != 0
。 由于b
可以等于 1,所以每个整数都是有理数。
#!/usr/bin/ruby
puts 2.to_r
puts "23".to_r
puts 2.6.to_r
p Rational 0
p Rational 1/5.0
p Rational 0.5
此示例显示了一些有理数。
puts 2.to_r
在这里,我们使用to_r
方法将 2 整数转换为2 / 1
有理数。
p Rational 0.5
我们使用Rational
类创建一个有理数。
$ ./rational.rb
2/1
23/1
5854679515581645/2251799813685248
(0/1)
(3602879701896397/18014398509481984)
(1/2)
Output of the example.
Ruby nil
值
Ruby 具有特殊值nil
。 这是没有值的。 nil
是NilClass
的单例对象。 只有一个nil
; 我们不能再拥有更多了。
#!/usr/bin/ruby
puts nil
p nil
p $val
p [1, 2, 3][4]
p $val1 == $val2
nil
值的示例。
puts nil
p nil
我们将nil
值打印到控制台。 puts
方法打印一个空字符串; p
方法显示'nil'字符串。
p $val
当我们引用一个未设置的全局变量时,我们得到nil
值。
p [1, 2, 3][3]
在此代码行中,我们引用三元素数组的第四个元素。 我们得到nil
。 Ruby 中的许多方法都会为无效值返回nil
。
p $val1 == $val2
该行返回true
。 这是由于nil
值是NilClass
的单例对象这一事实的结果。
$ ./nilvalue.rb
nil
nil
nil
true
Output.
Ruby 字符串
字符串是表示计算机程序中文本数据的数据类型。 Ruby 字符串是 unicode 字符的序列。 字符串是String
的实例。 字符串字面值是用双或单引号括起来的字符。
字符串是非常重要的数据类型。 它值得一章。 这里我们仅举一个小例子。
#!/usr/bin/ruby
p "Ruby"
p 'Python'
p "Ruby".size
p "Ruby".upcase
p 23.to_s
在此程序中,我们使用 Ruby 字符串。 我们使用p
方法进行打印,因为我们在输出中看到了数据类型。
p "Ruby"
p 'Python'
我们在终端上打印两个字符串字面值。 第一个字面值用双引号括起来,第二个字面值用单引号括起来。
p "Ruby".size
p "Ruby".upcase
这两行调用两个字符串方法。 size
方法返回字符串的大小:在本例中为 4 个字符。 upcase
以大写字母返回字符串。
p 23.to_s
to_s
方法将整数转换为字符串。
$ ./strings.rb
"Ruby"
"Python"
4
"RUBY"
"23"
在输出中,我们看到用引号引起来的字符串。 这是使用p
方法的结果。 print
和puts
方法不这样做。
Ruby 数组和哈希
数组和哈希是对象的集合。 他们将对象分组到一个位置。
数组是对象的有序集合。 哈希是键值对的集合。 我们将只有一章介绍数组和哈希。 以下示例仅简要介绍了这两个容器。
#!/usr/bin/ruby
nums = [1, 2, 3, 4]
puts "There are #{nums.size} items in the array"
nums.each do |num|
puts num
end
domains = { :de => "Germany", :sk => "Slovakia",
:us => "United States", :no => "Norway" }
puts domains.keys
puts domains.values
这是一个有关 Ruby 数组和哈希的快速示例。
nums = [1, 2, 3, 4]
puts "There are #{nums.size} items in the array"
nums.each do |num|
puts num
end
这些行创建一个包含 4 个项目的数组。 在第二行中,我们计算数组的项并将其合并到消息中。 稍后,我们使用each
方法遍历数组,并将每个元素打印到控制台。
domains = { :de => "Germany", :sk => "Slovakia",
:us => "United States", :no => "Norway" }
puts domains.keys
puts domains.values
在这里,我们创建一个 Ruby 哈希。 然后我们打印其键和值。
$ ./arrayshashes.rb
There are 4 items in the array
1
2
3
4
de
sk
us
no
Germany
Slovakia
United States
Norway
Example output.
Ruby 转换
我们经常一次处理多种数据类型。 将一种数据类型转换为另一种数据类型是编程中的常见工作。 类型转换或类型转换是指将一种数据类型的实体更改为另一种。 有两种转换类型:隐式转换和显式转换。 隐式类型转换,也称为强制转换,是编译器自动进行的类型转换。 Ruby 只有显式转换。
Ruby 具有内置的转换方法,例如to_i
,to_s
或to_f
。 Kernel
模块具有一些进行转换的公共方法,例如Integer
,String
或Float
。 这些方法不应与 Ruby 类混淆。
#!/usr/bin/ruby
p Array(1..6)
p Complex 6
p Float 12
p Integer "34"
p Rational 6
p String 22
这里我们展示Kernel
转换方法。
$ ./convertmethods.rb
[1, 2, 3, 4, 5, 6]
(6+0i)
12.0
34
(6/1)
"22"
Output of the example.
#!/usr/bin/ruby
p "12".to_i
p 12.5.to_i
p nil.to_i
p 12.to_f
p "11".to_f
p nil.to_f
在上面的示例中,我们显示了一些数值转换。 某些 Ruby 对象具有to_i
和to_f
方法,分别将对象转换为整数和浮点数。
p "12".to_i
p 12.5.to_i
p nil.to_i
在此代码中,我们将字符串,十进制和nil
转换为整数类型。
p 12.to_f
p "11".to_f
p nil.to_f
这三行将整数,字符串和nil
转换为十进制数据类型的对象。
$ ./conversions.rb
12
12
0
12.0
11.0
0.0
Example output.
第二个示例显示了一些字符串转换。
#!/usr/bin/ruby
p "12".to_i
p "13".to_f
p "12".to_r
p "13".to_c
p "Jane".to_sym
v = "Ruby Python Tcl PHP Perl".split
p v.class
在上面的示例中,我们将字符串转换为不同数据类型的对象。
p "12".to_i
p "13".to_f
p "12".to_r
p "13".to_c
在这里,字符串将转换为整数,十进制,有理数和复数。
p "Jane".to_sym
字符串成为符号。
v = "Ruby Python Tcl PHP Perl".split
p v.class
在这里,我们使用String
类的split
方法将字符串转换为数组。
$ ./stringconv.rb
12
13.0
(12/1)
(13+0i)
:Jane
Array
这就是我们得到的。
下一个小示例显示了数组哈希转换。
#!/usr/bin/ruby
h = {:de => "Germany", :sk => "Slovakia"}
p h.to_a
a = [:de, "Germany", :sk, "Slovakia",
:hu, "Hungary", :no, "Norway"]
p Hash[*a]
在示例代码中,我们创建了一个哈希并将其隐秘到数组中。 然后,我们创建一个数组并将其转换为哈希。
h = {:de => "Germany", :sk => "Slovakia"}
p h.to_a
使用to_a
方法创建哈希并将其转换为数组。
a = [:de, "Germany", :sk, "Slovakia",
:hu, "Hungary", :no, "Norway"]
p Hash[*a]
创建一个数组并将其转换为哈希。 在此上下文中,星号是拆分运算符。 这是从 Perl 提取的 Ruby 惯用语之一。 它将数组拆分为几个变量。
$ ./h2a.rb
[[:de, "Germany"], [:sk, "Slovakia"]]
{:de=>"Germany", :sk=>"Slovakia", :hu=>"Hungary", :no=>"Norway"}
Output of the example.
在 Ruby 教程的这一部分中,我们介绍了数据类型及其转换。
Ruby 字符串
在 Ruby 教程的这一部分中,我们将更详细地处理字符串数据。
字符串是计算机语言中最重要的数据类型之一。 这就是为什么我们将一整章专门讨论在 Ruby 中使用字符串的原因。
字符串是 Unicode 字符序列。 它是一种存储数据值序列的数据类型,其中元素通常根据字符编码代表字符。 当字符串按字面意义出现在源代码中时,称为字符串字面值。
Ruby 字符串第一个示例
在 Ruby 中,字符串字面值用单引号或双引号引起来。
#!/usr/bin/ruby
# first.rb
puts 'Python language'
puts "Ruby language"
该示例有两个字符串字面值。 第一个用单引号引起来。 另一个用双引号引起来。
$ ./first.rb
Python language
Ruby language
输出。
Ruby 使用引号
如果我们想显示报价,例如直接讲话怎么办? 基本上有两种方法可以做到这一点。
#!/usr/bin/ruby
puts "There are many stars"
puts "He said, \"Which one is your favourite?\""
puts 'There are many stars'
puts 'He said, "Which one is your favourite?"'
我们使用(\
)字符转义其他引号。 通常,双引号用于分隔字符串字面值。 但是,当转义时,其原始含义被抑制。 它显示为普通字符,可以在字符串字面值中使用。 在引号中使用引号的第二种方法是混合单引号和双引号。
$ ./quotes.exe
There are many stars.
He said, "Which one is your favourite?"
Output.
Ruby 转义序列
转义序列是在字符串字面值中使用时具有特定含义的特殊字符。
#!/usr/bin/ruby
puts "one two three four"
puts "one\ntwo\nthree\nfour"
最常见的转义序列之一是换行符\n
。 它有许多编程语言可用。 换行符后的下一个字符将出现在新行中。
$ ./newline.rb
one two three four
one
two
three
four
在上面脚本的输出中,换行符后的单词出现在换行符上。
r
,b
和t
字符是普通字母字符。 以\
字符开头时,它们具有特殊含义。
#!/usr/bin/ruby
puts " bbb\raaa"
puts "Joan\b\b\bane"
puts "Towering\tinferno"
在上面的示例中,我们使用了三个不同的转义字符。
puts " bbb\raaa"
回车\r
是行尾到行首的控制字符。 在将字符串打印到控制台之前,先对其进行处理。 转义序列使aaa
字符放置在bbb
字符之前。 输出为aaabbb
。
puts "Joan\b\b\bane"
\b
控制字符是一个空格。 删除前一个字符。 打印到控制台的字符串是"Jane"
而不是"Joan"
。
puts "Towering\tinferno"
最后,\t
转义序列在两个单词之间放置一个制表符空间。
$ ./escapes.rb
aaabbb
Jane
Towering inferno
示例的输出。
反斜杠字符\
是用于创建转义序列的特殊字符。 当需要打印反斜杠本身时,它前面会带有另一个反斜杠。 其默认含义被转义并被打印。 单引号和双引号用于在 Ruby 中定界字符串。 为了打印它们,它们之前还带有\
。
#!/usr/bin/ruby
puts "The special character \\"
puts "The special character \'"
puts "The special character \""
在这个简单的脚本中,我们将所有三个字符打印到终端。
$ ./specials.rb
The special character \
The special character '
The special character "
Output.
Ruby 访问字符串元素
可以在 Ruby 中访问字符串元素。 为此,我们使用方括号[]
。 在方括号内,我们可以放置字符串,索引或范围。
#!/usr/bin/ruby
msg = "Ruby language"
puts msg["Ruby"]
puts msg["Python"]
puts msg[0]
puts msg[-1]
puts msg[0, 3]
puts msg[0..9]
puts msg[0, msg.length]
此代码示例说明如何访问字符串的一部分。
msg = "Ruby language"
这是我们将要访问的字符串。
puts msg["Ruby"]
在此代码行中,我们测试字符串'Ruby'
是否为msg
字符串的子字符串。 如果为true
,则返回我们要查找的字符串。
puts msg[0]
字符串的字符可以通过其索引进行访问。 数字从 0 开始。换句话说,0 是第一个字符的索引。 msg[0]
返回字符串的第一个字符,即R
。
puts msg[-1]
在这里,我们访问字符串的最后一个字符。 -1 代表字符串的最后一个索引。
puts msg[0, 3]
用逗号分隔的两个索引返回从第一个索引开始到第二个索引结束的字符(不包括在内)。
puts msg[0..9]
也可以使用范围运算符。 在这里,我们打印msg
字符串的前十个字符。
puts msg[0, msg.length]
该行返回整个字符串。 msg.length
返回字符串的大小。
$ ./access.rb
Ruby
R
e
Rub
Ruby langu
Ruby language
Output of the example.
Ruby 多行字符串
在许多编程语言中,创建多行字符串需要付出额外的努力。 在 Visual Basic 中尤其如此。 在 Ruby 中,可以轻松创建多行字符串。
#!/usr/bin/ruby
puts "I hear Mariachi static on my radio
And the tubes they glow in the dark
And I'm there with her in Ensenada
And I'm here in Echo Park
"
puts %/
Carmelita hold me tighter
I think I'm sinking down
And I'm all strung out on heroin
On the outskirts of town/
puts <<STRING
Well, I'm sittin' here playing solitaire
With my pearl-handled deck
The county won't give me no more methadone
And they cut off your welfare check
STRING
在示例中,我们有 Carmelita 歌曲的歌词。 我们展示了三种打印多行字符串的方法。 可以在双引号中使用它们。 我们可以使用% 字符来构建多行字符串。 %
后面的字符也将字符串括起来。 最后,我们可以使用 heredoc 语法。 在此语法中,我们使用<<
,后跟一些字符串。 该字符串包含多行字符串。 它还必须保持对齐。
Ruby 变量插值
变量插值是用字符串字面值中的值替换变量。 要用值替换变量,将变量名放在字符串字面值内的#{
和}
字符之间。
#!/usr/bin/ruby
name = "Jane"
age = 17
puts "#{name} is #{age} years old"
在此示例中,我们替换字符串中的两个变量:name
和age
。
$ ./interpolation.rb
Jane is 17 years old
在替换中,可以使用表达式。
#!/usr/bin/ruby
x = 5
y = 6
puts "The product of #{x} and #{y} is #{x*y}"
这是替换表达式的一个示例。
$ ./interpolation2.rb
The product of 5 and 6 is 30
运行示例。
还有另一种替代变量的方法。 它类似于 Python 2.x 支持插值的方式。
#!/usr/bin/ruby
name = "Jane"
age = 17
message = "%s is %d years old" % [name, age]
puts message
例。
message = "%s is %d years old" % [name, age]
我们在使用它之前先构建一个字符串。 %s
和%d
分别是期望包含字符串和数字的格式化字符。 这些值在%
字符后的方括号中提供。
Ruby 连接字符串
连接字符串是从多个字符串中创建一个字符串。
#!/usr/bin/ruby
lang = "Ruby" + " programming" + " languge"
puts lang
lang = "Python" " programming" " language"
puts lang
lang = "Perl" << " programming" << " language"
puts lang
lang = "Java".concat(" programming").concat(" language")
puts lang
Ruby 提供了多种连接字符串的方法。
lang = "Ruby" + " programming" + " languge"
加号运算符是最常用的运算符,用于添加计算机语言中的字符串。 Perl 和 PHP 使用点.
运算符来连接字符串。
lang = "Python" " programming" " language"
Ruby 自动连接后续的字符串。
lang = "Perl" << " programming" << " language"
可以用来连接字符串的另一个运算符是<<
。
lang = "Java".concat(" programming").concat(" language")
实际上,每个字符串字面值都是一个对象。 我们可以在每个 Ruby 对象上调用各种方法。 在字符串对象上,我们可以调用concat
方法,该方法将添加两个字符串对象。 它还返回最终对象,我们可以在该对象上调用另一个方法。 因此,我们可以将这些方法放在一个链中。
$ ./concatenate.rb
Ruby programming languge
Python programming language
Perl programming language
Java programming language
Output.
Ruby 冻结字符串
在 Java 或 C# 中,字符串是不可变的。 这意味着我们无法修改现有字符串。 我们只能从现有的字符串中创建一个新字符串。 在 Ruby 中,默认情况下字符串不是不可变的。
Ruby 中的字符串对象具有freeze
方法,这使它们不可变。
#!/usr/bin/ruby
msg = "Jane"
msg << " is "
msg << "17 years old"
puts msg
msg.freeze
#msg << "and she is pretty"
在此示例中,我们演示了可以修改字符串。 但是,在对字符串对象调用freeze
方法之后,我们将无法再修改字符串。 如果我们取消注释代码行,则会收到“无法修改冻结的字符串”错误消息。
Ruby 比较字符串
比较字符串是编程中的常见工作。 我们可以使用==
运算符或eql?
方法比较两个字符串。 如果字符串相等,则返回true
;否则返回false
。
#!/usr/bin/ruby
puts "12" == "12"
puts "17" == "9"
puts "aa" == "ab"
puts "Jane".eql? "Jan"
puts "Jane".eql? "Jane"
在此代码示例中,我们比较了一些字符串。
puts "12" == "12"
这两个字符串相等,该行返回true
。
puts "aa" == "ab"
两个字符串的前两个字符相等。 接下来比较以下字符。 它们是不同的,因此该行返回false
。
puts "Jane".eql? "Jan"
eql?
方法用于比较两个字符串。 字符串是对象,并且都具有内置的eql?
方法。 该方法采用一个参数,即我们要与之比较的第一个字符串的字符串。
$ ./comparing.rb
true
false
false
false
true
程序的输出。
Ruby 具有飞船运算符<==>
。 该运算符来自 Perl 语言。 与上面两种比较字符串(返回true
或false
)的方式不同,此运算符将返回 1、0 或 -1。 取决于左参数相对于右参数的值。 如果左参数大于右参数,则运算符返回 1。如果左参数小于右参数,则运算符返回 -1。 如果两个参数相等,则运算符将返回 0。这意味着一个字符大于另一个字符? 字符在表中排序。 每个字符在表中都有一个位置。 比较字符时,我们将比较它们在此类表格中的位置。 例如,在 ASCII 表中,字符a
位于字符b
之前。 因此比较<==> b
会返回 -1,因为左侧参数的位置比b
低。
#!/usr/bin/ruby
puts "a" <==> "b"
puts "b" <==> "a"
puts "a" <==> "a"
将角色与太空飞船运算符进行比较。
$ ./spaceship.rb
-1
1
0
Output of the example.
不管大小写,都可以比较字符串。 为此,Ruby 具有casecmp
方法。 该方法的工作方式与飞船运算符相同。
#!/usr/bin/ruby
puts "Jane".casecmp "Jane"
puts "Jane".casecmp "jane"
puts "Jane".casecmp "Jan"
Example.
puts "Jane".casecmp "Jane"
puts "Jane".casecmp "jane"
这两行返回相同的结果,即 0。在这种情况下,我们不考虑字符是大写还是小写。
$ ./case.rb
0
0
1
Output of the example.
字符串是 Ruby 中的对象
Ruby 是一种面向对象的语言。 对象是 OOP 程序的基本构建块。 字符串也是对象。 对象是数据和方法的组合。 在 OOP 程序中,对象被创建并且它们之间相互通信。
#!/usr/bin/ruby
website = "google.com"
puts website
website = String.new "zetcode.com"
puts website
在上面的示例中,我们展示了在 Ruby 中创建字符串的两种基本方法。
website = "google.com"
这里,字符串字面值被分配给网站变量。 在后台,Ruby 解释器创建一个字符串对象。
website = String.new "zetcode.com"
这是创建字符串对象的标准方法。 但是,在大多数情况下都使用第一种方法,因为它不太冗长,并且是大多数计算机语言中创建字符串的常用方法。
#!/usr/bin/ruby
puts "zetcode".upcase
puts "zetcode".size
puts "zetcode".reverse
在此代码示例中,我们在字符串字面值上调用三个方法。 对于熟悉 Java,C 和类似语言的人,这可能会令人困惑。 在 Ruby 中,字符串字面值被转换为可以在其上调用方法的字符串对象。
$ ./stringobject2.rb
ZETCODE
7
edoctez
Output of the example.
Ruby 字符串方法
Ruby 字符串对象具有可用于处理字符串的有用方法。 我们已经看到了几种字符串方法,例如concat
或eql?
。
#!/usr/bin/ruby
word = "Determination"
puts "The word #{word} has #{word.size} characters"
puts word.include? "tion"
puts word.include? "tic"
puts
puts word.empty?
word.clear
puts word.empty?
我们有一个字符串变量。 提出了四种字符串方法。
puts "The word #{word} has #{word.size} characters"
size
方法返回字符串中的字符数。
puts word.include? "tion"
include?
方法确定在测试的字符串中是否存在子字符串。 在这种情况下,代码行返回true
。
puts word.empty?
word.clear
empty?
方法检查字符串是否为空。 它返回布尔值true
或false
。 clear
方法使字符串为空。
$ ./basicmethods.rb
The word Determination has 13 characters
true
false
false
true
输出。
在下一个示例中,我们将提供适用于字符大小写的方法。
#!/usr/bin/ruby
ruby = "Ruby programming language"
puts ruby.upcase
puts ruby.downcase
puts ruby.capitalize
puts ruby.swapcase
Ruby 有四种字符大小写方法。 upcase
方法返回字符串的副本,其中所有字符均大写。 downcase
方法返回字符串的副本,其中所有字符均为小写。 capitalize
方法返回字符串的副本,其中第一个字符转换为大写,其余字符转换为小写。 最后,swapcase
方法返回字符串的副本,其中大写字母转换为小写,反之亦然。
$ ./rubylang.rb
RUBY PROGRAMMING LANGUAGE
ruby programming language
Ruby programming language
rUBY PROGRAMMING LANGUAGE
Output.
接下来,我们介绍两种 Ruby 字符串方法:start_with?
和end_with?
。 这两个方法均返回布尔值true
或false
。 它们分别确定字符串以特定字符串开头还是结尾。
#!/usr/bin/ruby
ws1 = "zetcode.com"
ws2 = "www.gnome.org"
puts ws1.start_with? "www."
puts ws2.start_with? "www."
puts
puts ws1.end_with? ".com"
puts ws2.end_with? ".com"
这是上述方法的示例。
puts ws1.start_with? "www."
在这里,我们检查字符串是否以"www"
开头。 否,因此控制台的输出为布尔值false
。
puts ws1.end_with? ".com"
我们检查ws1
字符串变量是否以".com"
后缀结尾。 确实如此,因此我们在控制台中看到了一个真实的结果。
$ ./startend.rb
false
true
true
false
Output.
在下面的示例中,我们将处理inspect
方法。 该方法返回一个原始字符串,该字符串用引号引起来,带有未解释的特殊字符。 当我们要检查什么字符组成字符串时,这很有用。
#!/usr/bin/ruby
msg = "Jane\t17\nThomas\t23"
puts msg
puts msg.inspect
inspect
字符串方法的示例。
msg = "Jane\t17\nThomas\t23"
这是一个带有一些特殊字符的字符串。
puts msg
puts msg.inspect
在第一种情况下,特殊字符被解释。 字符串部分之间有一个制表符和换行符。 在第二种情况下,我们以原始格式获取字符串。
$ ./inspectmethod.rb
Jane 17
Thomas 23
"Jane\t17\nThomas\t23"
Output of the example.
chomp
方法返回一个新字符串,其中记录分隔符已从字符串末尾删除。 默认的分隔符是换行符(\n
)。
#!/usr/bin/ruby
print "Are you sure to download? (Yes/No) "
response = gets
if (response.downcase == "yes")
puts "Downloaded"
else
puts "Download cancelled"
end
puts response.inspect
在上面的脚本中,我们从用户那里得到了输入。 我们对用户的反应作出反应。
$ ./chomp.rb
Are you sure to download? (Yes/No) Yes
Download cancelled
"Yes\n"
该脚本无法正常运行。 当我们考虑inspect
返回时,原因就很清楚了。 来自用户的输入以回车键结束。 换行符也包含在响应变量中。 并且"yes"
不等于"yes\n"
。 要更正脚本,我们使用chomp
方法。 它从变量中删除换行符。
#!/usr/bin/ruby
print "Are you sure to download? (Yes/No) "
response = gets
if (response.downcase.chomp == "yes")
puts "Downloaded"
else
puts "Download cancelled"
end
puts response.inspect
这是更正的示例。
if (response.downcase.chomp == "yes")
在这里,我们在将输入与"yes"
字符串进行比较之前先对其进行处理。 chomp
方法删除换行符。
$ ./chomp.rb
Are you sure to download? (Yes/No) Yes
Downloaded
"Yes\n"
现在该示例正常工作。
Ruby 格式化字符串
Ruby 具有格式说明符。 格式说明符确定字符串的外观。 它以%
字符开头。 格式说明符放在单引号或双引号中。
格式说明符具有以下字段:
%[flags][field width][precision]conversion specifier
方括号中的字段是可选的。
转换说明符指定如何将数据转换为可显示形式。
#!/usr/bin/ruby
puts "There are %d oranges in the basket." % 12
puts "There are %d oranges and %d apples in the basket." % [12, 10]
这是一些格式说明符的示例。
puts "There are %d oranges in the basket" % 12
当我们在字符串中使用%d
表示法时,我们期望此时有一个数字。 d
是十进制数字的转换说明符。 该数字在%
字符后给出。
puts "There are %d oranges and %d apples in the basket" % [12, 10]
我们可以在字符串中使用多个格式说明符。 每个字母都以% 字符开头。 多个值位于[]
字符之间,并用逗号分隔。
$ ./formatspecifiers.rb
There are 12 oranges in the basket.
There are 12 oranges and 10 apples in the basket.
Output of the example.
在下面的示例中,我们将介绍一些基本的转换说明符。
#!/usr/bin/ruby
puts "There are %d apples." % 5
puts "I can see %i oranges." % 3
puts "The width of iPhone 3G is %f mm." % 62.1
puts "This animal is called a %s" % "rhinoceros."
我们有整数,浮点数和字符串的转换说明符。
puts "There are %d apples." % 5
puts "I can see %i oranges." % 3
d
和i
均可用于整数。
puts "The width of iPhone 3G is %f mm." % 62.1
f
是浮点值的转换说明符。 默认情况下,浮点数有六个小数位。
puts "This animal is called a %s" % "rhinoceros."
s
字符用于字符串。
$ ./basicspecifiers.rb
There are 5 apples.
I can see 3 oranges.
The width of iPhone 3G is 62.100000 mm.
This animal is called a rhinoceros.
Output of the example.
接下来,我们有一个使用格式说明符的实际示例。
#!/usr/bin/ruby
website = "zetcode.com"
website.each_char do |c|
print "#{c} has ASCII code %d\n" % c.ord
end
在此示例中,我们遍历字符串的所有字符并将其 ASCII 值打印到终端。
website.each_char do |c|
print "#{c} has ASCII code %d\n" % c.ord
end
each_char
方法将网站字符串的每个字符传递到块,每个周期一个字符,而当前字符存储在c
变量中。 我们使用ord
方法获得字符的 ASCII 码,该方法返回一个字符串的序数。
$ ./character.rb
z has ASCII code 122
e has ASCII code 101
t has ASCII code 116
c has ASCII code 99
o has ASCII code 111
d has ASCII code 100
e has ASCII code 101
. has ASCII code 46
c has ASCII code 99
o has ASCII code 111
m has ASCII code 109
示例的输出。
数字可以以多种形式显示。 转换说明符可用于格式化数字。
#!/usr/bin/ruby
# decimal
puts "%d" % 300
# hexadecimal
puts "%x" % 300
# octal
puts "%o" % 300
# binary
puts "%b" % 300
# scientific
puts "%e" % (5/3.0)
在上面的示例中,我们以十进制,十六进制,八进制,二进制和科学格式打印数字。
# hexadecimal
puts "%x" % 300
x
转换说明符用于将数字转换为十六进制格式。
# binary
puts "%b" % 300
x
转换说明符用于将数字转换为二进制格式。
$ ./various.rb
300
12c
454
100101100
1.666667e+00
Output.
精度是格式说明符中的一个字段。 指定为小数点后的数字。 对于整数,浮点数和字符串,它具有不同的含义。 与整数一起使用时,它指示要打印的最小位数。 如果数字的位数少于精度,则以零作为前缀。 整数的默认精度为 1,表示不填充零。 与浮点数一起使用时,精度是小数点后显示的位数。 最后,对于字符串,精度是打印的最大字符数。
#!/usr/bin/ruby
puts 'Height: %f %s' % [172.3, 'cm']
puts 'Height: %.1f %s' % [172.3, 'cm']
puts "%d" % 16
puts "%.5d" % 16
puts "%s" % "zetcode"
puts "%.5s" % "zetcode"
在此示例中,我们将使用precision
字段。
puts 'Height: %f %s' % [172.3, 'cm']
puts 'Height: %.1f %s' % [172.3, 'cm']
172.3 是浮点数。 如果未指定精度,则小数点后将有 6 个小数位。 在我们的情况下,将有 5 个零。 第二条代码行中的.1
是精度。 对于浮点值,它将小数位数减少到 1。
puts "%d" % 16
puts "%.5d" % 16
整数的默认精度为 1。在第二行中,我们指定了精度.5,该精度将 3 个零添加(添加)到 16 个数字中。
puts "%s" % "zetcode"
puts "%.5s" % "zetcode"
第一行显示字符串的所有字符。 第二行仅打印其中五个。 删除两个字符。
$ ./precision.rb
Height: 172.300000 cm
Height: 172.3 cm
16
00016
zetcode
zetco
Output.
字段宽度指定要显示的数据的最小宽度。 它是一个数字,如果存在,则在小数点之前。 如果输出较短,则用空格填充,并使其右对齐。 如果我们在字段宽度之前放置减号,则它将保持对齐。 如果输出长于字段宽度,则会完整显示。
#!/usr/bin/ruby
puts "%d" % 1
puts "%d" % 16
puts "%d" % 165
puts "%d" % 1656
puts "%d" % 16567
puts "%10d" % 1
puts "%10d" % 16
puts "%10d" % 165
puts "%10d" % 1656
puts "%10d" % 16567
在第一种情况下,我们打印五个数字而不指定字段宽度。 输出的宽度等于要显示的字符数。 在第二种情况下,字段宽度为 10。5 个输出中的每个输出的最小长度为 10 个字符。 数字右对齐。
puts "%d" % 1
puts "%d" % 16
我们打印两个数字。 输出的宽度分别为 1、2 个字符。
puts "%10d" % 1
puts "%10d" % 16
两种情况下的长度均为 10 个字符。 这两个数字以给定的顺序填充 9 和 8 空格。
$ ./fieldwidth.rb
1
16
165
1656
16567
1
16
165
1656
16567
我们可以看到,在第二种情况下,数字是右对齐的。
标志限定符修改格式的行为。
#
标志分别将0b
,0
和0x
前缀添加到二进制,八进制和十六进制格式。 即使精度限制了小数位数,它也会向浮点值添加小数点。
#!/usr/bin/ruby
puts "%#b" % 231
puts "%#x" % 231
puts "%#o" % 231
puts "%.0e" % 231
puts "%#.0e" % 231
puts "%.0f" % 231
puts "%#.0f" % 231
在代码示例中,我们使用x
标志。
puts "%#b" % 231
puts "%#x" % 231
puts "%#o" % 231
十进制 231 以二进制,八进制和十六进制格式打印。 #
标志为它们添加前缀。
puts "%.0e" % 231
puts "%#.0e" % 231
在此,.0
精度替代数字的小数位。 但是,当与#
标志一起使用时,即使没有十进制数字,也会显示小数点。
$ ./flags1.rb
0xe7
0b11100111
0347
2e+02
2.e+02
231
231.
Output.
+
标志为正的十进制数字添加一个加号。 对于二进制,八进制和十六进制的负数,它将添加一个负号并使用一个绝对值。
#!/usr/bin/ruby
puts "%d" % 231
puts "%+d" % 231
puts "%d" % -231
puts "%+d" % -231
puts "%b" % -231
puts "%o" % -231
puts "%x" % -231
puts "%+b" % -231
puts "%+o" % -231
puts "%+x" % -231
演示格式说明符的+
标志的示例。
puts "%d" % 231
puts "%+d" % 231
通常,正数会省略其符号。 如果要显示正数的加号,请指定+
标志。
puts "%d" % -231
puts "%+d" % -231
+
标志对负数无效。 输出是相同的。
puts "%b" % -231
puts "%o" % -231
puts "%x" % -231
二进制,八进制和十六进制数具有创建负数的自己的方式。
puts "%+b" % -231
puts "%+o" % -231
puts "%+x" % -231
如果我们为这些负数指定+
标志,则会将数字转换为其他格式并添加减号。 没有表示负数的特殊方法。
$ ./flags2.rb
231
+231
-231
-231
..100011001
..7431
..f19
-11100111
-347
-e7
Output of the example.
在这里,我们介绍0
标志和-
标志。 0
标志使数字以零而不是空格填充。 -
标志使输出左对齐。
#!/usr/bin/ruby
puts "%010d" % 1
puts "%010d" % 16
puts "%010d" % 165
puts "%010d" % 1656
puts "%010d" % 16567
puts "%-10d" % 1
puts "%-10d" % 16
puts "%-10d" % 165
puts "%-10d" % 1656
puts "%-10d" % 16567
例。
puts "%010d" % 1
puts "%010d" % 16
数字将用零填充。
puts "%-10d" % 1
puts "%-10d" % 16
小于字段宽度的数字被对齐。 并且-
标志使其左对齐。
$ ./fieldwidth2.rb
0000000001
0000000016
0000000165
0000001656
0000016567
1
16
165
1656
16567
示例的输出。
*
标志可用于精度和宽度。 每当我们使用*
标志时,我们都必须指定精度或宽度作为参数。
#!/usr/bin/ruby
puts "%.*f" % [3, 1.1111111]
puts "%0*d" % [10, 2]
puts "%0*.*f" % [10, 3, 1.1111]
*标志的示例。
puts "%.*f" % [3, 1.1111111]
在这里,我们将*
标志用于精度。 第一个数字 3 是精度的参数。 它说 1.1111111 浮点数将只显示三个小数位。
puts "%0*d" % [10, 2]
在此代码行中,我们使用*
标志作为宽度。 我们必须在[]
括号之间添加宽度。 第一个数字是宽度,第二个数字是转换说明符的值。
puts "%0*.*f" % [10, 3, 1.1111]
*
标志可用于宽度和精度。 我们必须在[]
括号中都指定它们。
$ ./flags3.rb
1.111
0000000002
000001.111
Output of the example.
Ruby 教程的这一部分介绍了字符串。
Ruby 表达式
在 Ruby 教程的这一部分中,我们将讨论表达式。
表达式是根据操作数和运算符构造的。 表达式的运算符指示将哪些运算应用于操作数。 表达式中运算符的求值顺序由运算符的优先级和关联性确定。
运算符是特殊符号,表示已执行某个过程。 编程语言的运算符来自数学。 程序员处理数据。 运算符用于处理数据。 操作数是运算符的输入(参数)之一。
Ruby 运算符
下表显示了按优先级排序的常见 Ruby 运算符(优先级最高):
类别 | 符号 |
---|---|
解析,访问运算符 | :: . |
数组运算符 | [ ] [ ]= |
乘方 | ** |
否定,补码,一元加,减 | ! ~ + - |
乘,除,模 | * / % |
加,减 | + - |
移位运算符 | << >> |
按位与 | & |
按位异或,逻辑异或 | ^ | |
关系运算符 | > >= < <= |
按位或,逻辑或 | ^ | |
相等,模式匹配运算符 | <=> == === != =~ !~ |
逻辑与 | && |
逻辑或 | || |
范围运算符 | .. ... |
三元 | ?: |
赋值运算符 | = += -= *= **= /= %= &= |= ^= <<= >>= ||= &&= |
备选逻辑非 | not |
备选逻辑或,与 | or and |
表的同一行上的运算符具有相同的优先级。
一个运算符通常有一个或两个操作数。 那些仅使用一个操作数的运算符称为一元运算符。 那些使用两个操作数的对象称为二进制运算符。 还有一个三元运算符?:
,它可以处理三个操作数。
某些运算符可以在不同的上下文中使用。 例如+
运算符。 从上表中我们可以看到它在不同情况下使用。 它添加数字,连接字符串,指示数字的符号。 我们说运算符是重载。
Ruby 符号运算符
有两个符号运算符:+
和-
。 它们用于指示或更改值的符号。
#!/usr/bin/ruby
puts +2
puts -2
+和-表示值的符号。 加号可以用来表示我们有一个正数。 可以将其省略,并且通常可以这样做。
在下面的示例中,我们将使用减号。
#!/usr/bin/ruby
a = 1
puts a
puts -(a)
puts -(-(a))
减号更改值的符号。
$ ./sign.rb
1
-1
1
这是示例的输出。
Ruby 赋值运算符
赋值运算符=将值赋给变量。 变量是值的占位符。 在数学中,=
运算符具有不同的含义。 在等式中,=
运算符是一个相等运算符。 等式的左侧等于右侧。
x = 1
puts x # prints 1
在这里,我们为x
变量分配一个数字。
x = x + 1
puts x # prints 2
先前的表达式在数学上没有意义。 但这在编程中是合法的。 该表达式将x
变量加 1。 右边等于 2,并且 2 分配给x
。
3 = x;
此代码示例导致语法错误。 我们无法为字面值分配值。
Ruby 解析,成员访问运算符
这两个运算符的优先级最高。 这意味着始终要首先求值它们。
#!/usr/bin/ruby
class MyMath
Pi = 3.1415926535
end
module People
Name = "People"
end
puts MyMath::Pi
puts People::Name
在第一个示例中,我们介绍::
名称空间解析运算符。 它允许访问在另一个类或模块内定义的常量,模块或类。 它用于提供名称空间,以使方法和类名不会与不同作者的其他类冲突。
class MyMath
Pi = 3.1415926535
end
module People
Name = "People"
end
我们有一个简单的模块和一个类。 每个都有一个定义的常数。
puts MyMath::Pi
puts People::Name
我们使用::
运算符从这两者访问常量。
$ ./resolution.rb
3.1415926535
People
这是resolution.rb
程序的输出。
点.
运算符是成员访问运算符。 它用于调用对象的方法。
#!/usr/bin/ruby
class Person
def initialize name, age
@name = name
@age = age
end
def info
"#{@name} is #{@age} years old"
end
end
p = Person.new "Jane", 17
puts p.info
puts "ZetCode".reverse
在我们的示例中,我们有两个对象。 一位用户定义和一位预定义。 我们使用点运算符来处理这些对象。
p = Person.new "Jane", 17
puts p.info
在这两行中,点运算符调用两种方法:new
和info
。
puts "ZetCode".reverse
字符串是内置对象,具有反向方法。 这被称为。
$ ./memberaccess.rb
Jane is 17 years old
edoCteZ
这是示例的输出。
Ruby 连接字符串
在 Ruby 中,+
运算符还用于连接字符串。 当在不同的上下文中不同地使用运算符时,我们说它是重载。
#!/usr/bin/ruby
puts "Return " + "of " + "the " + "King"
puts "Return ".+"of ".+ "the ".+"King"
我们使用字符串连接运算符将三个字符串连接在一起。
puts "Return " + "of " + "the " + "King"
我们使用+运算符连接四个字符串。
puts "Return ".+"of ".+ "the ".+"King"
在后台,+
运算符是 Ruby 方法。 字符串字面值是一个对象。 我们使用访问.
运算符调用对象的方法。
$ ./catstrings.rb
Return of the King
Return of the King
这就是我们运行catstrings.rb
程序时得到的。
Ruby 自增自减运算符
Ruby 没有此类运算符。
x++;
x = x + 1;
...
y--;
y = y - 1;
这些是 C 中的递增,递减运算符。
如果您熟悉 Java,C,C++ ,则知道这些运算符。 它们在 Ruby 中不可用。 Python 语言也没有。
Ruby 算术运算符
下表是 Ruby 中的算术运算符表。
符号 | 名称 |
---|---|
+ |
加法 |
- |
减法 |
* |
乘法 |
/ |
除法 |
% |
余数 |
** |
乘方 |
在下一个示例中,我们使用算术运算。
#!/usr/bin/ruby
a = 10
b = 11
c = 12
puts a + b + c
puts c - a
puts a * b
puts c / 3
puts c % a
puts c ** a
在前面的示例中,我们使用加法,减法,乘法,除法和余数运算。 这些都是数学所熟悉的。
puts c % a
% 运算符称为余数或取模运算符。 它找到一个数除以另一个的余数。 例如,9 % 4
,9 模 4 为 1,因为 4 两次进入 9 且余数为 1。
$ ./arithmetic.rb
33
2
110
4
2
61917364224
This is the output of the example.
接下来,我们将说明整数除法和浮点除法之间的区别。
#!/usr/bin/ruby
puts 5 / 2
puts 5 / 2.0
puts 5.0 / 2
puts 5.to_f / 2
在前面的示例中,我们将两个数字相除。
puts 5 / 2
表达式中的两个操作数都是整数。 我们已经完成了整数除法。 除法运算的返回值为整数。 当我们将两个整数相除时,结果是一个整数。
puts 5 / 2.0
puts 5.0 / 2
puts 5.to_f / 2
如果其中一个值是浮点数(或两者皆有),则执行浮点除法。 浮点值具有小数点。 我们还可以调用to_f
方法将整数转换为浮点数。
$ ./division.rb
2
2.5
2.5
2.5
在这里,我们看到了division.rb
程序的结果。
Ruby 还有其他执行除法的方法。 这些可用作方法调用。
#!/usr/bin/ruby
puts 5.div 2.0
puts 5.fdiv 2
puts 5.quo 2
puts 5.0.quo 2.0
在上面的示例中,我们有div
,fdiv
和quo
方法。
puts 5.div 2.0
div
方法始终执行整数除法。 即使操作数是浮点值。
puts 5.fdiv 2
fdiv
方法始终执行浮点除法。
puts 5.quo 2
puts 5.0.quo 2.0
quo
方法执行最精确的划分。 如果两个操作数均为float
,则返回float
,否则返回有理数。
$ ./otherdivision.rb
2
2.5
5/2
2.5
Ruby 布尔运算符
在 Ruby 中,我们具有以下逻辑运算符。 布尔运算符也称为逻辑运算符。
符号 | 名称 |
---|---|
&& |
逻辑与 |
|| |
逻辑或 |
! |
否定 |
布尔运算符处理真值。 Ruby 还有其他的布尔运算符。 这些是and
,or
& not
。 除了它们的优先级较低之外,它们的作用相同。 这种双重性来自 Perl 语言,在这种语言中,需要使用具有较低优先级的布尔运算符。
#!/usr/bin/ruby
x = 3
y = 8
puts x == y
puts y > x
if y > x then
puts "y is greater than x"
end
许多表达式导致布尔值。 布尔值用于条件语句中。
puts x == y
puts y > x
关系运算符始终导致布尔值。 这两行分别显示false
和true
。
if y > x then
puts "y is greater than x"
end
仅在满足括号内的条件时才执行if
语句的主体。 表达式x > y
返回true
,因此消息"y
大于x"
被打印到终端。
下一个示例显示逻辑and
运算符。
#!/usr/bin/ruby
puts true && true
puts true && false
puts false && true
puts false && false
仅当两个操作数均为true
时,and
运算符的计算结果才为true
。
$ ./andoperator.rb
true
false
false
false
仅其中一个表达式为true
。
如果两个操作数中的任何一个为true
,则逻辑或||
运算符的计算结果为true
。
#!/usr/bin/ruby
puts true || true
puts true || false
puts false || true
puts false || false
如果运算符的任一侧为真,则操作的结果为真。
$ ./oroperator.rb
true
true
true
false
三个表达式得出布尔值true
。
否定!
表示真假和假真。
#!/usr/bin/ruby
puts !0
puts !1
puts !true
puts !false
puts ! (4<3)
puts ! "Ruby".include?("a")
该示例显示了否定运算符的作用。
$ ./not.rb
false
false
false
true
true
true
This is the output of the example.
||
和&&
运算符经过短路求值。 短路求值意味着仅当第一个参数不足以确定表达式的值时才求值第二个参数:当逻辑的第一个参数的值等于false
时,总值必须为false
; 当逻辑或的第一个参数为true
时,总值必须为true
。 短路求值主要用于提高性能。
一个例子可以使这一点更加清楚。
#!/usr/bin/ruby
def one
puts "Inside one"
false
end
def two
puts "Inside two"
true
end
puts "Short circuit"
if one && two
puts "Pass"
end
puts "##############################"
if two || one
puts "Pass"
end
在示例中,我们有两种方法。 它们在布尔表达式中用作操作数。 我们将看看它们是否被调用。
if one && two
puts "Pass"
end
一种方法返回false
。 短路&&
无法求值第二种方法。 没有必要。 一旦操作数为假,逻辑结论的结果始终为假。 仅将"Inside one"
打印到控制台。
puts "##############################"
if two || one
puts "Pass"
end
在第二种情况下,我们使用||
运算符,并使用two
方法作为第一个操作数。 在这种情况下,"Inside two"
和"Pass"
字符串将打印到终端。 同样,也不必求值第二个操作数,因为一旦第一个操作数计算为true
,则逻辑或始终为true
。
$ ./shortcircuit.rb
Short circuit
Inside one
##############################
Inside two
Pass
我们看到了shortcircuit.rb
程序的结果。
Ruby 关系运算符
关系运算符用于比较值。 这些运算符总是产生布尔值。
符号 | 含义 |
---|---|
< |
小于 |
<= |
小于或等于 |
> |
大于 |
>= |
大于或等于 |
关系运算符也称为比较运算符。
#!/usr/bin/ruby
p 3 < 4
p 3 > 5
p 3 >= 3
3 < 4
表达式返回true
,因为 3 小于 4。3 > 5
表达式返回false
,因为 3 大于 5 并不成立。
Ruby 按位运算符
小数对人类是自然的。 二进制数是计算机固有的。 二进制,八进制,十进制或十六进制符号仅是相同数字的符号。 按位运算符使用二进制数的位。
符号 | 含义 |
---|---|
~ |
按位取反 |
^ |
按位异或 |
& |
按位与 |
| |
按位或 |
<< |
左移 |
>> |
右移 |
很少在像 Ruby 这样的高级语言中使用按位运算符。
#!/usr/bin/ruby
puts ~ 7 # prints -8
puts ~ -8 # prints 7
puts 6 & 3 # prints 2
puts 3 & 6 # prints 2
puts 6 ^ 3 # prints 5
puts 3 ^ 6 # prints 5
puts 6 | 3 # prints 7
puts 3 | 6 # prints 7
puts 6 << 1 # prints 12
puts 1 << 6 # prints 64
puts 6 >> 1 # prints 3
puts 1 >> 6 # prints 0
在上面的代码示例中,我们显示了所有 6 个运算符。
puts ~ 7 # prints -8
puts ~ -8 # prints 7
按位取反运算符分别将 1 更改为 0,将 0 更改为 1。该运算符还原数字 7 的所有位。其中一位还确定数字是否为负。 如果我们再一次对所有位取反,我们将再次得到 7。
puts 6 & 3 # prints 2
puts 3 & 6 # prints 2
按位,运算符在两个数字之间进行逐位比较。 仅当操作数中的两个对应位均为 1 时,位位置的结果才为 1。
puts 6 ^ 3 # prints 5
puts 3 ^ 6 # prints 5
按位互斥或运算符在两个数字之间进行逐位比较。 如果操作数中对应位中的一个或另一个(但不是全部)为 1,则位位置的结果为 1。
puts 6 | 3 # prints 7
puts 3 | 6 # prints 7
按位或运算符在两个数字小体之间进行逐位比较。 如果操作数中的任何对应位为 1,则位位置的结果为 1。
puts 6 << 1 # prints 12
puts 1 << 6 # prints 64
puts 6 >> 1 # prints 3
puts 1 >> 6 # prints 0
按位移位运算符向右或向左移位。 这些运算符也称为算术移位。
Ruby 复合赋值运算符
复合赋值运算符由两个运算符组成。 他们是速记员。
#!/usr/bin/ruby
a = 0
a = a + 1
a += 1
puts a
b = 0
b = b - 8
b -= 8
puts b
+=
和-=
复合运算符是这些速记运算符之一。 它们比完整的表达式可读性差,但是经验丰富的程序员经常使用它们。
a = a + 1
a += 1
这两行是相同的。 他们将 1 加到变量。
其他复合运算符是:
-= *= **= /= %= &= |= <<= >>=
Ruby 运算符优先级
运算符优先级告诉我们首先求值哪个运算符。 优先级对于避免表达式中的歧义是必要的。
以下表达式 28 或 40 的结果是什么?
3 + 5 * 5
像数学中一样,乘法运算符的优先级高于加法运算符。 结果是 28。
(3 + 5) * 5
要更改求值的顺序,可以使用括号。 括号内的表达式始终首先被求值。
#!/usr/bin/ruby
puts 3 + 5 * 5
puts (3 + 5) * 5
puts ! true | true
puts ! (true | true)
在此代码示例中,我们显示一些常见的表达式。 每个表达式的结果取决于优先级。
puts 3 + 5 * 5
该行打印 28。乘法运算符的优先级高于加法。 首先计算5*5
的乘积。 然后添加 3。
puts ! true | true
在这种情况下,否定运算符具有更高的优先级。 首先,第一个true
值与|
运算符将false
和true
组合在一起,最后给出true
。
$ ./precedence.rb
28
40
true
false
Ruby 关联性
有时,优先级不能令人满意地确定表达式的结果。 还有另一个规则称为关联性。 运算符的关联性确定优先级与相同的运算符的求值顺序。
9 / 3 * 3
此表达式的结果是 9 还是 1? 乘法,删除和模运算符从左到右关联。 因此,该表达式的计算方式为:(9 / 3) * 3
,结果为 9。
算术,布尔,关系和按位运算符都是从左到右关联的。
另一方面,赋值运算符是正确关联的。
a = b = c = d = 0
print a, b, c, d # prints 0000
如果关联从左到右,则以前的表达式将不可能。
复合赋值运算符从右到左关联。
j = 0
j *= 3 + 1
puts j
您可能期望结果为 1,但是由于关联性,实际结果为 0。 首先求值右边的表达式,然后应用复合赋值运算符。
Ruby 范围运算符
Ruby 有两个范围运算符。 它们用于快速创建一系列对象。 通常是一系列数字或字母。
..
范围运算符(两个点)创建一个包含范围。 ...
运算符(三个点)创建一个互斥范围,该范围的高值被排除在外。
#!/usr/bin/ruby
p (1..3).to_a
p (1...3).to_a
p ('a' .. 'l').to_a
在示例中,我们使用了两个范围运算符来创建数字和字符的范围。
p (1..3).to_a
p (1...3).to_a
这两行使用两个范围运算符创建两个范围。 范围对象将转换为数组。 第一个范围的值为 1、2 和 3,而第二个范围的值为 1 和 2。
p ('a' .. 'l').to_a
在这里,我们使用..
范围运算符来创建从'a'到'l'的字母数组。
$ ./range.rb
[1, 2, 3]
[1, 2]
["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l"]
这是示例输出。
Ruby 三元运算符
三元运算符?:
是条件运算符。 对于要根据条件表达式选择两个值之一的情况,它是一个方便的运算符。
cond-exp ? exp1 : exp2
如果cond-exp
为true
,则求值exp1
并返回结果。 如果cond-exp
为false
,则求值exp2
并返回其结果。
#!/usr/bin/ruby
age = 32
adult = age >= 18 ? true : false
if adult then
puts "Adult"
else
puts "Not adult"
end
在大多数国家/地区,成年取决于您的年龄。 如果您的年龄超过特定年龄,则您已经成年。 在这种情况下,我们可以使用三元运算符。
adult = age >= 18 ? true : false
首先,求值赋值运算符右侧的表达式。 三元运算符的第一阶段是条件表达式求值。 因此,如果年龄大于或等于 18,则返回?
字符后的值。 如果不是,则返回:
字符后的值。 然后将返回值分配给成人变量。
$ ./ternary.rb
Adult
32 岁的成年人是成人。
计算素数
我们将计算素数。
#!/usr/bin/ruby
nums = (4..50).to_a
puts "Prime numbers:"
print "2 3 "
nums.each do |i|
not_prime = false
(2..Math.sqrt(i).ceil).each do |j|
not_prime = true if i % j == 0
end
print i, " " unless not_prime
end
puts
在上面的示例中,我们处理了几个运算符。 质数(或质数)是一个自然数,它具有两个截然不同的自然数除数:1 和它本身。 我们拾取一个数字并将其除以数字,从 2 到拾取的数字。 实际上,我们不必尝试所有较小的数字,我们可以将数字除以所选数字的平方根。 该公式将起作用。 在算法的核心,我们使用余数除法运算符,也称为模运算符。
nums = (4..50).to_a
我们将从这些数字计算素数。
print "2 3 "
我们跳过了 2、3 个数字的计算。 他们是素数。
not_prime = false
not_prime
是一个标志,指示所选的数字不是素数。 我们假定所选的数字是质数,除非稍后证明。
(2..Math.sqrt(i).ceil).each do |j|
not_prime = true if i % j == 0
end
如果我们仅对小于所讨论数字平方根的数字进行模除,就可以了。 如果余数除法运算符针对任何i
值返回 0,则说明该数字不是质数。
print i, " " unless not_prime
如果未设置not_prime
标志,我们将打印数字。
上面的示例旨在演示几个运算符。 实际上,存在一种更简单的方法来计算质数。 Ruby 有一个用于计算素数的模块。
#!/usr/bin/ruby
require 'prime'
Prime.each(50) do |i|
print i, " "
end
puts
使用 Ruby 素数模块计算最多 50 个素数的示例。
require 'prime'
我们包括主要模块。
Prime.each(50) do |i|
print i, " "
end
我们计算素数直至上限 -50。
$ ./primes.rb
2 3 5 7 11 13 17 19 23 29 31 37 41 43 47
从此输出中,我们看到 2 和 50 之间的质数。
在 Ruby 教程的这一部分中,我们介绍了这些表达式。
Ruby 控制流
在 Ruby 教程的这一部分中,我们将讨论流控制。 我们将定义几个关键字,这些关键字使我们能够控制 Ruby 程序的流程。
条件和循环会改变 Ruby 程序的流程。 条件语句是在特定条件下执行特定语句的关键字。 循环是多次执行的程序块。 当程序运行时,语句从源文件的顶部到底部逐一执行。
Ruby if
语句
if
关键字用于检查表达式是否为真。 如果为true
,则执行一条语句。 该语句可以是单个语句或复合语句。 复合语句由该块包围的多个语句组成。 块是由end
关键字括起来的代码。 then
关键字是可选的。
#!/usr/bin/ruby
num = gets.to_i
if num > 0 then
puts "num variable is positive"
puts "num variable equals to #{num}"
end
我们从用户那里读取了一个号码。 如果数字大于零,那么我们将两个消息打印到控制台。 如果没有,则什么也不做。
$ ./simpleif.rb
4
num variable is positive
num variable equals to 4
满足条件,并将消息写入控制台。
我们可以使用else
关键字来创建一个简单的分支。 如果if
关键字后方括号内的表达式的值为假,则将自动执行else
关键字后方的语句。 代码块包含在end
关键字中。
#!/usr/bin/ruby
age = 17
if age > 18
puts "Driving license issued"
else
puts "Driving license not permitted"
end
我们有一个年龄变量。 布尔表达式的计算结果为false
,并且在控制台中得到"Driving license not permitted"
。
$ ./licence.rb
Driving license not permitted
我们可以使用elsif
关键字创建多个分支。 仅当不满足先前条件时,elsif
关键字才会测试其他条件。 请注意,我们可以在测试中使用多个elsif
关键字。
#!/usr/bin/ruby
print "Enter a number: "
num = gets.to_i
if num < 0
puts "#{num} is negative"
elsif num == 0
puts "#{num} is zero"
elsif num > 0
puts "#{num} is positive"
end
我们有一个数值变量,并测试它是否为负数或正数或等于零。 根据从用户读取的值,我们将其中一条消息打印到控制台。
Ruby case
语句
case
语句是选择控制流语句。 它允许变量或表达式的值通过多路分支控制程序执行的流程。 与使用if
和elsif
语句的组合相比,它以更简单的方式创建多个分支。
我们有一个变量或一个表达式。 case
关键字用于根据值列表测试变量或表达式中的值。 值列表用when
关键字显示。 如果值匹配,则执行when
之后的语句。 有一个可选的else
语句。 如果找不到其他匹配项,则执行该命令。
#!/usr/bin/ruby
print "Enter top level domain: "
domain = gets.chomp
case domain
when "us"
puts "United States"
when "de"
puts "Germany"
when "sk"
puts "Slovakia"
when "hu"
puts "Hungary"
else
puts "Unknown"
end
在我们的程序中,我们有一个域变量。 我们从命令行读取变量的值。 我们使用when
语句测试变量的值。 有几种选择。 例如,如果该值等于"us"
,则将"United States"
字符串打印到控制台。
domain = gets.chomp
我们使用gets
方法从用户那里得到输入。 输入还包括换行符。 chomp
方法排除换行符。
$ ./domains.rb
Enter top level domain: hu
Hungary
我们在控制台输入了"hu"
字符串,程序以"Hungary"
作为响应。
Ruby while
,until
语句
while
语句是一个控制流语句,它允许根据给定的布尔条件重复执行代码。 条件为真时,它将执行代码。
while
关键字在end
关键字包围的块内执行语句。 每次将表达式求值为true
时都会执行这些语句。
#!/usr/bin/ruby
i = 0
sum = 0
while i < 10 do
i = i + 1
sum = sum + i
end
puts "The sum of 0..9 values is #{sum}"
在代码示例中,我们从一系列数字计算值的总和。
while
循环包含三个部分:初始化,测试和更新。 语句的每次执行都称为循环。
i = 0
sum = 0
我们启动i
和sum
变量。 i
用作计数器。
while i < 10 do
...
end
while
和do
关键字之间的表达式是第二阶段,即测试。 请注意,do
关键字是可选的。 执行主体中的语句,直到表达式的计算结果为false
。
i = i + 1
这是while
循环的最后一个第三阶段-更新。 我们增加计数器。 请注意,对while
循环的不正确处理可能会导致循环不断。
$ ./while.rb
The sum of 0..9 values is 55
这是示例的输出。
until
是一个控制流语句,在条件为false
时执行代码。 当条件为真时,循环停止。
#!/usr/bin/ruby
hours_left = 12
until hours_left == 0
if hours_left == 1
puts "There is #{hours_left} hour left"
else
puts "There are #{hours_left} hours left"
end
hours_left -= 1
end
在我们的示例中,我们有一个变量hours_left
。 我们开始倒数。 在每个循环周期中,我们打印那里还有多少小时。 当变量等于零时,循环停止。
$ ./until.rb
There are 12 hours left
There are 11 hours left
There are 10 hours left
There are 9 hours left
There are 8 hours left
There are 7 hours left
There are 6 hours left
There are 5 hours left
There are 4 hours left
There are 3 hours left
There are 2 hours left
There is 1 hour left
运行示例,我们得到了这个结果。
Ruby for
语句
如果在启动循环之前知道周期数,则可以使用for
语句。 for
循环与范围结合使用。 对于范围的每个元素,将执行语句块。 语句包含在end
关键字中。 do
关键字是可选的。
#!/usr/bin/ruby
for i in 0..9 do
puts "#{i}"
end
在此示例中,我们将数字0..9
打印到控制台。 在每个循环中,i
变量均包含一个数字范围内的值。 该值将打印到控制台。 ..
范围运算符创建一个数字列表,包括最后一个数字。
$ ./forloop.rb
0
1
2
3
4
5
6
7
8
9
This is the output of the example.
要使用for
循环遍历元素数组,可以使用数组的length
方法。
#!/usr/bin/ruby
planets = ["Mercury", "Venus", "Earth", "Mars", "Jupiter",
"Saturn", "Uranus", "Neptune"]
for i in 0...planets.length
puts planets[i]
end
在这个例子中,我们有一系列的行星。 我们遍历数组并打印数组的每个元素。
planets = ["Mercury", "Venus", "Earth", "Mars", "Jupiter",
"Saturn", "Uranus", "Neptune"]
这是一系列行星。
for i in 0...planets.length
length
方法返回数组的长度。 由于数组以 0 开头,因此最后一个索引为n-1
。 ...
范围运算符创建一个数字范围,但最后一个高值除外。
puts planets[i]
我们在数组中打印具有特定索引的元素。
$ ./planets2.rb
Mercury
Venus
Earth
Mars
Jupiter
Saturn
Uranus
Neptune
运行上面的 Ruby 程序将给出此输出。
Ruby each
方法
在 Ruby 中,我们可以使用each
方法来遍历数组的各项。 它有两个参数。 一个元素和一个块。 元素放置在管道之间。 它是当前迭代项目的占位符。 块是在每次迭代中执行的代码。
#!/usr/bin/ruby
planets = ["Mercury", "Venus", "Earth", "Mars", "Jupiter",
"Saturn", "Uranus", "Neptune"]
planets.each do |planet|
puts planet
end
在此示例中,我们使用each
迭代器遍历了一系列行星。
planets.each do |planet|
puts planet
end
each
迭代器是一种适用于行星数组的方法。 该行星是迭代中当前项目的占位符。 我们可以把任何想要的角色放在那里。 我们可以使用{}
字符代替do
和end
关键字。
Ruby break
,next
语句
break
语句可用于终止由while
,for
或case
语句定义的块。
#!/usr/bin/ruby
while true
r = 1 + rand(30)
print "#{r} "
if r == 22
break
end
end
puts
我们定义了一个无限的while
循环。 我们使用break
语句退出此循环。 我们从 1 到 30 中选择一个随机值。我们打印该值。 如果该值等于 22,则结束无穷的while
循环。
while true
...
end
这是一个无尽的循环。 while 循环的条件始终为 true。 摆脱这种无穷循环的唯一方法就是突破。
r = 1 + rand(30)
print "#{r} "
我们计算一个从 1 到 30 的随机数,并将其打印到控制台。
if r == 22
break
end
如果数字等于 22,我们将打破循环。 while 循环终止。
$ ./break.rb
20 14 6 26 30 12 2 10 18 29 28 11 30 26 20 22
我们可能会得到这样的东西。
next
语句用于跳过循环的一部分,并继续循环的下一个迭代。 它可以与for
和while
语句结合使用。
在下面的示例中,我们将打印一个数字列表,这些数字不能除以 2 而没有余数。
#!/usr/bin/ruby
num = 0
while num < 100
num += 1
if (num % 2 == 0)
next
end
print "#{num} "
end
puts
我们使用while
循环遍历数字1..99
。
if (num % 2 == 0)
next
end
如果表达式num % 2
返回 0,则可以将所讨论的数字除以 2。执行next
语句,并跳过循环的其余部分。 在我们的例子中,循环的最后一条语句将被跳过,并且数字不会输出到控制台。 下一个迭代开始。
$ ./next.rb
1 3 5 7 9 11 13 15 17 19 21 23 25 27 29 31 33 35 37 39
41 43 45 47 49 51 53 55 57 59 61 63 65 67 69 71 73 75 77
79 81 83 85 87 89 91 93 95 97 99
这是程序的示例输出。
Ruby redo
语句
redo
语句重新启动循环的迭代,而无需检查循环条件。 最后一个示例将是一个更复杂的示例。 还将演示redo
语句和其他功能。
#!/usr/bin/ruby
options = ["rock", "scissors", "paper"]
while true
print <<TEXT
1 - rock
2 - scissors
3 - paper
9 - end game
TEXT
val = gets.to_i
r = rand(3) + 1
if val == 9
puts "End"
exit
end
if ![1, 2, 3, 9].include?(val)
puts "Invalid option"
redo
end
computer = options[r-1]
human = options[val-1]
puts "I have #{computer}, you have #{human}"
if val == r
puts "Tie, next throw"
redo
end
if val == 1 and r == 2
puts "Rock blunts scissors, you win"
elsif val == 2 and r == 1
puts "Rock blunts scissors, you loose"
elsif val == 2 and r == 3
puts "Scissors cut paper, you win"
elsif val == 3 and r == 2
puts "Scissors cut paper, you loose"
elsif val == 3 and r == 1
puts "Paper covers rock, you win"
elsif val == 1 and r == 3
puts "Paper covers rock, you loose"
end
end
我们有一个简单的剪刀石头布游戏。 在此代码示例中,我们将利用redo
语句,条件,随机数,数组和用户输入。
options = ["rock", "scissors", "paper"]
在选项数组中,我们拥有游戏的所有可能性。 将消息打印到控制台时,将使用这三个词。
print <<TEXT
1 - rock
2 - scissors
3 - paper
9 - end game
TEXT
我们使用 heredoc 语法将菜单打印到控制台。 Heredoc 以<<
开头,后跟一个字符串。 相同的字符串关闭构造。 它必须保持对齐。 这使我们可以一步打印多行。 每个游戏周期都会打印此菜单。
val = gets.to_i
r = rand(3) + 1
在这些代码行中,我们从终端读取一个值。 然后,我们从 1、2 和 3 中随机选择一个数字。请注意rand(3)
返回 0、1 和 2 中的一个数字。这就是为什么要加 1 的原因。
if val == 9
puts "End"
exit
end
如果来自用户的输入等于 9,我们将"End"
打印到终端并结束游戏。 exit
方法终止程序。
if ![1, 2, 3, 9].include?(val)
puts "Invalid option"
redo
end
如果用户选择的值与菜单中提供的值不同,我们将告知无效选项并重做周期。
computer = options[r-1]
human = options[val-1]
puts "I have #{computer}, you have #{human}"
数字将转换为字符串。 我们同时打印用户和计算机的选择。
if val == r
puts "Tie, next throw"
redo
end
如果双方有相同的选择,那就是平局。 我们开始一个新的游戏周期。 我们利用redo
关键字。
if val == 1 and r == 2
puts "Rock blunts scissors, you win"
elsif val == 2 and r == 1
puts "Rock blunts scissors, you loose"
...
使用多个if
和elsif
分支,我们比较用户和计算机的选择。 我们决定谁是赢家。
$ ./redo.rb
1 - rock
2 - scissors
3 - paper
9 - end game
3
I have paper, you have paper
Tie, next throw
1 - rock
2 - scissors
3 - paper
9 - end game
2
I have rock, you have scissors
Rock blunts scissors, you loose
1 - rock
2 - scissors
3 - paper
9 - end game
1
I have scissors, you have rock
Rock blunts scissors, you win
1 - rock
2 - scissors
3 - paper
9 - end game
9
End
这是一个示例输出。
在 Ruby 教程的这一部分中,我们正在讨论控制流结构。
Ruby 数组
在 Ruby 教程的这一部分中,我们将介绍数组。 数组是对象的有序集合。
Ruby 数组定义
变量一次只能容纳一项。 数组可以容纳多个项目。 这些项目称为数组的元素。 数组可以保存任何数据类型的对象。 每个元素都可以由索引引用。 数组从零开始。 第一个元素的索引为零。
请注意,Ruby 数组与 C,C++ 或 Java 之类的语言中的数组有很大不同。
#!/usr/bin/ruby
nums = [1, 2, 3, 4, 5]
nums.each do |num|
puts num
end
我们的第一个示例将创建一个包含五个整数的数组。 数组的元素将打印到控制台。
nums = [1, 2, 3, 4, 5]
该行创建了一个由五个整数组成的数组。 元素之间用逗号分隔,并放在方括号之间。
nums.each do |num|
puts num
end
我们使用each
方法遍历数组,并将每个元素打印到控制台。
$ ./array.rb
1
2
3
4
5
这是程序的输出。
Ruby 数组创建
Ruby 中的数组是一个对象。 数组可以用new
方法实例化。
#!/usr/bin/ruby
nums = Array.new
nums.push 1
nums.push 2
nums.push 3
nums.push 4
nums.push 5
puts nums
在脚本中,我们首先创建一个nums
数组。 然后,我们添加五个整数。
nums = Array.new
创建一个数组对象。
nums.push 1
push
方法将一个项目附加到数组的末尾。
我们将继续使用new
方法创建数组对象。
#!/usr/bin/ruby
a1 = Array.new
a2 = Array.new 3
a3 = Array.new 6, "coin"
a4 = Array.new [11]
a5 = Array.new (15) {|e| e*e}
puts [a1, a2, a3, a4, a5].inspect
Array
类的new
方法可能有一些选项。
a1 = Array.new
创建一个空数组。 我们应该在以后用数据填充它。
a2 = Array.new 3
在这里,我们创建了一个包含三个nil
对象的数组。
a3 = Array.new 6, "coin"
创建一个包含六个"coin"
字符串的数组。 第一个选项是数组的大小。 第二个选项是填充数组的对象。
a4 = Array.new [11]
第四个数组将具有一项。
a5 = Array.new (15) {|e| e*e}
我们创建一个包含 15 个元素的数组。 每个元素都在块中创建。 在那里我们计算平方整数的序列。
puts [a1, a2, a3, a4, a5].inspect
我们将所有数组放入一个数组。 数组可以放入其他数组中。 然后我们在数组上调用inspect
方法。 这将在所有元素上调用该方法。 inspect
方法返回数组的字符串表示形式。 当我们需要快速检查数组的内容时,这很有用。
$ ./arraynew.rb
[[], [nil, nil, nil], ["coin", "coin", "coin", "coin", "coin", "coin"],
[11], [0, 1, 4, 9, 16, 25, 36, 49, 64, 81, 100, 121, 144, 169, 196]]
我们可以看到所有创建的数组的内容。
以下脚本显示了在 Ruby 中创建数组的各种方法。
#!/usr/bin/ruby
integers = [1, 2, 3, 4, 5]
animals = %w( donkey dog cat dolphin eagle )
weights = Array.new
weights << 4.55 << 3.22 << 3.55 << 8.55 << 3.23
puts integers.inspect
puts animals.inspect
puts weights.inspect
我们创建三个由整数,字符串和小数组成的数组。
integers = [1, 2, 3, 4, 5]
该行创建一个包含 5 个整数的数组。 这是经典的数组创建。 数组的元素放在方括号之间,并用逗号分隔。
animals = %w( donkey dog cat dolphin eagle )
代码行创建一个包含五个元素的字符串数组。 在这种模式下,我们保存一些输入。 我们不使用逗号和双引号。
weights = Array.new
weights << 4.55 << 3.22 << 3.55 << 8.55 << 3.23
在第三种方法中,有两个步骤。 首先,我们创建一个Array
对象,然后使用数据对其进行初始化。 这是一个正式的数组创建。 上述方式实际上是该表示法的简写。
puts integers.inspect
inspect
方法将数组的字符串表示形式打印到终端。
$ ./creation.rb
[1, 2, 3, 4, 5]
["donkey", "dog", "cat", "dolphin", "eagle"]
[4.55, 3.22, 3.55, 8.55, 3.23]
这是代码示例的输出。
数组项不限于数字和字符串。 数组可以包含所有 Ruby 数据类型。
#!/usr/bin/ruby
class Empty
end
nums = [1, 2, 3, 4, 5]
various = [1, -1, "big", 3.4, Empty.new, nums, :two]
puts various.inspect
我们将各种 Ruby 对象放入各种数组中。
various = [1, -1, "big", 3.4, Empty.new, nums, :two]
该数组包含数字,字符串,自定义对象,另一个数组和符号。
$ ./arrayobjects.rb
[1, -1, "big", 3.4, #<Empty:0x987f704>, [1, 2, 3, 4, 5], :two]
运行arrayobjects.rb
示例,我们收到此输出。
最后一个示例显示了一个嵌套数组; 一个数组在另一个数组中。 在 Ruby 中,可以将数组嵌套到数组中。
#!/usr/bin/ruby
numbers = [1, 2, 3, [2, 4, 6, [11, 12]]]
puts numbers.length
puts numbers[0], numbers[1]
puts numbers[3][0]
puts numbers[3][1]
puts numbers[3][3][0]
puts numbers[3][3][1]
puts numbers.flatten!.inspect
数组[11, 12]
嵌套在[2, 4, 6, ...]
数组中,该数组也嵌套在[1,2,3,...]
数组中。
puts numbers.length
length
方法返回 4。内部数组被计为一个元素。
puts numbers[0], numbers[1]
在这种情况下,[]
字符用于访问数组元素。 上面的行返回numbers
数组的第一个和第二个元素(数字 1 和 2)。 方括号内的数字是数组的索引。 第一个索引为 0,返回第一个元素。
puts numbers[3][0]
puts numbers[3][1]
在这里,我们从嵌套数组访问元素。 [3]
获取第四个元素,即数组[2, 4, 6, [11, 12]]
。 [3][0]
返回内部数组的第一个元素,在本例中为 2。 [3][1]
以类似的方式返回内部数组的第二个元素,即数字 4。
puts numbers[3][3][0]
puts numbers[3][3][1]
现在我们更加深入。 我们访问最里面的数组的元素。 [3][3]
返回[11, 12]
数组。 然后从该数组中获得第一个(11)和第二个(12)元素。
puts numbers.flatten!.inspect
flatten!
方法拉平数组。 它从内部数组中获取所有元素,并创建一个没有任何内部数组的新元素。
$ ./arrayofarrays.rb
4
1
2
2
4
11
12
[1, 2, 3, 2, 4, 6, 11, 12]
This is the output of the code example.
Ruby 打印数组内容
常见的工作是将数组元素打印到控制台。 我们有几种方法可以完成此任务。
#!/usr/bin/ruby
integers = [1, 2, 3, 4, 5]
puts integers
puts integers.inspect
integers.each do |e|
puts e
end
在此脚本中,我们将数组的所有元素打印三次。
puts integers
将数组作为puts
或print
方法的参数是打印数组内容的最简单方法。 每个元素都打印在单独的行上。
puts integers.inspect
使用inspect
方法,输出更具可读性。 该行将数组的字符串表示形式输出到终端。
integers.each do |e|
puts e
end
each
方法为数组中的每个元素调用一次块,并将该元素作为参数传递。 我们仅在每个元素上使用puts
方法。
$ ./printarray1.rb
1
2
3
4
5
[1, 2, 3, 4, 5]
1
2
3
4
5
该数组将打印到控制台三遍。
在第二个示例中,我们提供了另外两种打印数组元素的方式。
#!/usr/bin/ruby
integers = [1, 2, 3, 4, 5]
integers.length.times do |idx|
puts integers[idx]
end
integers.each_with_index do |num, idx|
puts "value #{num} has index #{idx}"
end
在第一种情况下,我们使用length
和times
方法的组合。 在第二种情况下,我们使用each_with_index
方法。
integers.length.times do |idx|
puts integers[idx]
end
length
方法返回数组的大小。 times
方法迭代以下块长度时间,将值从 0 传递到length-1
。 这些数字用作有关数组的索引。
integers.each_with_index do |num, idx|
puts "value #{num} has index #{idx}"
end
each_with_index
迭代数组,并将元素及其索引传递到给定的块。 这样,我们可以轻松地一次打印元素及其索引。
$ ./printarray2.rb
1
2
3
4
5
value 1 has index 0
value 2 has index 1
value 3 has index 2
value 4 has index 3
value 5 has index 4
这是示例的输出。
Ruby 读取数组元素
在本节中,我们将从数组中读取数据。
#!/usr/bin/ruby
lts = %w{ a b c d e f g h}
puts lts.first
puts lts.last
puts lts.at(3)
在第一个示例中,我们展示了三种简单的数据检索方法。
puts lts.first
puts lts.last
first
方法读取数组的第一个元素。 last
方法读取数组的最后一个元素。
puts lts.at(3)
at
方法返回具有特定索引的数组元素。 该行读取数组的第四个元素。
$ ./retrieval.rb
a
h
d
这是retrieval.rb
程序的输出。
[]
字符可用于访问数据。 这是许多其他编程语言所使用的访问数组中数据的传统方式。 它节省了一些打字。
#!/usr/bin/ruby
lts = %w{ a b c d e f g h }
puts lts[0]
puts lts[-1]
puts lts[0, 3].inspect
puts lts[2..6].inspect
puts lts[2...6].inspect
我们展示了使用[]
字符读取数据的五个示例。
puts lts[0]
puts lts[-1]
我们得到数组的第一项和最后一项。 我们将项目的索引号放在[]
字符之间。 第一项的索引为 0,最后一项的索引为 -1。
puts lts[0, 3].inspect
当我们在方括号之间有两个数字时,第一个是开始索引,第二个是长度。 在此代码行中,我们从索引 0 开始返回 3 个元素。请注意inspect
方法是可选的,仅用于产生更具可读性的输出。
puts lts[2..6].inspect
puts lts[2...6].inspect
我们可以在方括号内使用范围运算符。 在第一行中,我们从索引 2 到 6 读取元素,在第二行中从 2 到 5 读取元素。
接下来,我们将演示values_at
方法。 此方法的优点是我们可以在[]
字符之间放置多个索引以获取各种元素。
#!/usr/bin/ruby
lts = %w{ a b c d e f g h}
puts lts.values_at(1..5).inspect
puts lts.values_at(1, 3, 5).inspect
puts lts.values_at(1, 3, 5, 6, 8).inspect
puts lts.values_at(-1, -3).inspect
values_at
方法返回一个数组,其中包含与给定选择器相对应的元素。 inspect
方法是可选的。 它用于获取更具可读性的输出。
puts lts.values_at(1..5).inspect
此代码行返回索引为 1 到 5 的元素。
puts lts.values_at(1, 3, 5).inspect
在这里,我们读取索引为 1、3 和 5 的元素。
puts lts.values_at(1, 3, 5, 6, 8).inspect
我们放置了任意数量的索引。 如果没有带有特定索引的元素,则为零。
puts lts.values_at(-1, -3).inspect
负索引从数组末尾返回元素。
$ ./retrieval3.rb
["b", "c", "d", "e", "f"]
["b", "d", "f"]
["b", "d", "f", "g", nil]
["h", "f"]
这是脚本的输出。
我们将使用fetch
方法从数组读取数据。
#!/usr/bin/ruby
lts = [0, 1, 2, 3, 4, 5, 6]
puts lts.fetch(0)
puts lts.fetch(-2)
puts lts.fetch(8, 'undefined')
puts lts.fetch(8) { |e| -2*e }
我们展示了使用fetch
方法的几种形式。
puts lts.fetch(0)
puts lts.fetch(-2)
第一行打印数组中的第一个元素。 第二行从数组末尾打印第二个元素。
puts lts.fetch(8, 'undefined')
fetch
方法的第三种形式返回具有给定索引的元素。 如果索引位于数组元素之外,则该方法返回默认值,在本例中为"undefined"
。 如果没有第二个参数,则fetch
方法将引发IndexError
。
puts lts.fetch(8) { |e| -2*e }
在fetch
方法的最后一种形式中,我们有一个块。 如果找不到具有给定索引的值,则该方法将返回调用该块的值,并传入索引。
$ ./retrieval4.rb
0
5
undefined
-16
This is the output of the script.
我们将展示take
和take_while
方法的用法。
#!/usr/bin/ruby
lts = %w{ a b c d e f g h}
puts lts.take(4).inspect
lts2 = lts.take_while { |e| e < 'f' }
puts lts2.inspect
take n
方法返回数组的前n
个元素。 take_while
方法将元素传递到块,直到该块返回nil
或 false,然后停止迭代并返回所有先前元素的数组。
puts lts.take(4).inspect
在这里,我们返回数组的前四个元素。
lts2 = lts.take_while { |e| e < 'f' }
puts lts2.inspect
在这里,我们从原始数组创建一个新数组。 在新数组中,所有字符都位于'f'字符之前。
$ ./retrieval5.rb
["a", "b", "c", "d"]
["a", "b", "c", "d", "e"]
在这里,我们看到retrieval5.rb
程序的输出。
slice
方法与[]
字符相同。 该方法从数组中返回一个或多个元素。
#!/usr/bin/ruby
lts = %w{ a b c d e f g h}
puts lts.slice(0)
puts lts.slice(-1)
puts lts.slice(0, 3).inspect
puts lts.slice(2..6).inspect
puts lts.slice(2...6).inspect
我们介绍slice
方法的五个示例。
puts lts.slice(0)
puts lts.slice(-1)
这些slice
方法的形式返回一个数组元素。 第一行代码返回lts
数组的最后一个元素,第二行返回。
puts lts.slice(0, 3).inspect
第一个参数是起始索引,第二个参数是长度。 在此代码行中,我们从索引 0 开始返回 3 个元素。
puts lts.slice(2..6).inspect
puts lts.slice(2...6).inspect
我们可以将范围运算符与slice
方法一起使用。 在第一行中,我们从索引 2 到 6 读取元素,在第二行中从 2 到 5 读取元素。
$ ./retrieval6.rb
a
h
["a", "b", "c"]
["c", "d", "e", "f", "g"]
["c", "d", "e", "f"]
slice
方法返回数组的一部分,数组的一个或多个元素。
可以从数组中选择一个随机数。 Ruby 为此具有sample
方法。
#!/usr/bin/ruby
lts = %w{ a b c d e f g h}
puts lts.sample
puts lts.sample(3).inspect
sample
方法有两种形式。 在第一种形式中,我们选择一个随机元素。 在第二种形式中,我们从数组中选择 n 个随机元素。
$ ./random.rb
b
["c", "f", "d"]
$ ./random.rb
d
["c", "d", "e"]
两次运行示例将得出不同的结果。
Ruby 数组操作
在以下示例中,我们将介绍几种 Ruby 数组方法。
#!/usr/bin/ruby
num1 = [1, 2, 3, 4, 5]
num2 = [6, 7, 8, 9, 10]
puts num1 + num2
puts num1.concat num2
我们有两个数组。 我们添加这两个数组。
puts num1 + num2
puts num1.concat num2
有两种添加数组的方法。 我们可以使用+运算符或concat
方法。 结果是一样的。
Ruby 有很多使用数组的方法。 例如,length
方法返回数组中的元素数。
#!/usr/bin/ruby
lts = %w{ a b c d e f}
puts lts.inspect
puts "Array has #{lts.length} elements"
puts "The first element is #{lts.first}"
puts "The last element is #{lts.last}"
puts lts.eql? lts.dup
puts lts.eql? lts.dup.delete_at(0)
lts.clear
puts lts.inspect
puts lts.empty?
在上面的脚本中,我们介绍了七个新方法。
puts "Array has #{lts.length} elements"
length
方法确定数组的大小。
puts "The first element is #{lts.first}"
puts "The last element is #{lts.last}"
在这里,我们获得数组的第一个和最后一个元素。
puts lts.eql? lts.dup
eql?
方法确定两个数组是否相等。 在我们的例子中,该行返回true
。 dup
方法创建对象的浅表副本。 它是从Object
父级继承的。
puts lts.eql? lts.dup.delete_at(0)
delete_at
方法删除数组的第一个元素。 这次两个数组不相等。
lts.clear
clear
方法从数组中删除所有元素。
puts lts.empty?
empty?
方法检查数组是否为空。 在我们的例子中,代码行返回true
,因为我们刚刚删除了所有元素。
$ ./basics.rb
["a", "b", "c", "d", "e", "f"]
Array has 6 elements
The first element is a
The last element is f
true
false
[]
true
这是示例的输出。
一些 Ruby 数组方法以感叹号结尾。 这是一个 Ruby 习惯用法。 感叹号告诉程序员该方法将修改数据。 感叹号本身没有任何作用。 它只是一个命名约定。
#!/usr/bin/ruby
chars = %w{a b c d e}
reversed_chars = chars.reverse
puts reversed_chars.inspect
puts chars.inspect
reversed_chars = chars.reverse!
puts reversed_chars.inspect
puts chars.inspect
Ruby 除其他外,还有两种相似的方法,即reverse
方法和reverse!
方法。 这两种方法会更改元素的顺序,并将它们颠倒过来。 不同之处在于reverse
方法返回一个反向数组,并使原始数组保持原样,而reverse!
方法都修改了原始数组的内容。
$ ./twotypes.rb
["e", "d", "c", "b", "a"]
["a", "b", "c", "d", "e"]
["e", "d", "c", "b", "a"]
["e", "d", "c", "b", "a"]
我们可以清楚地看到前两个数组是不同的。 第三和第四数组相同。
接下来的代码示例中将介绍其他一些方法。
#!/usr/bin/ruby
numbers = [1, 2, 2, 2, 3, 4, 5, 8, 11]
puts numbers.index 2
puts numbers.index 11
puts numbers.rindex 2
puts numbers.include? 3
puts numbers.include? 10
puts numbers.join '-'
puts numbers.uniq!.inspect
我们介绍了另外五种方法。
puts numbers.index 2
puts numbers.index 11
index
方法返回数组元素的索引。 它从左边返回第一个元素的索引。 第一行返回 1,它是数组中前 2 个的索引。 数组中只有 11 个,其索引为 8。
puts numbers.rindex 2
rindex
从右边返回第一个元素的索引。 在我们的例子中,最右边的 2 具有索引 3。
puts numbers.include? 3
puts numbers.include? 10
include?
方法检查数组中是否存在元素。 第一行返回true
; 3 存在。 第二行返回false
; 我们的数组中没有 10。 按照约定,以问号结尾的 Ruby 方法返回一个布尔值。 同样,问号对数组没有影响。 这只是对程序员的提示。
puts numbers.join '-'
join
方法返回由数组元素创建的字符串,由提供的分隔符分隔。
puts numbers.uniq!.inspect
uniq!
方法从数组中删除重复的元素。 我们数组中的数字是 2 的三倍。 方法调用后,仅剩 1 个 2。
$ ./methods2.rb
1
8
3
true
false
1-2-2-2-3-4-5-8-11
[1, 2, 3, 4, 5, 8, 11]
注意join
方法的乘积。 它是一个字符串,其中数组的数字由-
字符连接。
Ruby 修改数组
在本节中,我们将仔细研究修改数组的方法。 基本上,我们将对数组执行各种插入和删除操作。
#!/usr/bin/ruby
lts = []
lts.insert 0, 'E', 'F', 'G'
lts.push 'H'
lts.push 'I', 'J', 'K'
lts << 'L' << 'M'
lts.unshift 'A', 'B', 'C'
lts.insert(3, 'D')
puts lts.inspect
我们从一个空数组开始。 我们使用不同的插入方法来构建数组。
lts.insert 0, 'E', 'F', 'G'
insert
方法将三个元素插入lts
数组。 第一个字母的索引为 0,第二个为 1,第三个为 3。
lts.push 'H'
lts.push 'I', 'J', 'K'
push
方法将元素附加到数组。 我们可以附加一个或多个元素。
lts << 'L' << 'M'
<<
是push
方法的同义词。 它将元素添加到数组。 可以在链中调用此运算符/方法。
lts.unshift 'A', 'B', 'C'
unshift
方法将元素添加到数组的前面。
lts.insert(3, 'D')
在这种情况下,insert
方法会在特定索引处插入"D"
字符。
$ ./insertion.rb
["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M"]
使用上述数组插入方法,我们构建了此大写字母数组。
有几种删除数组元素的方法。
#!/usr/bin/ruby
lts = %w{ a b c d e f g h}
lts.pop
lts.pop
puts lts.inspect
lts.shift
lts.shift
puts lts.inspect
lts.delete_at(0)
lts.delete('d')
puts lts.inspect
puts lts.clear
puts lts.inspect
在此脚本中,我们演示了从数组中删除元素的五种方法。
lts = %w{ a b c d e f g h}
我们有 8 个元素组成的数组。
lts.pop
pop
方法从数组中删除最后一个元素。
lts.shift
shift
方法从数组中删除第一个元素。
lts.delete_at(0)
delete_at
删除特定位置的元素。 我们删除其余元素的第一个元素。
puts lts.clear
clear
方法清除数组中的所有元素。
lts.delete('d')
delete
方法从数组中删除特定项目。
$ ./deletion.rb
["a", "b", "c", "d", "e", "f"]
["c", "d", "e", "f"]
["e", "f"]
[]
在这里,我们看到了示例的输出。
到目前为止,我们已经使用了方法(clear
方法除外),这些方法通过一次添加或删除项目来修改数组。 Ruby 的方法可以同时影响多个数组项。
#!/usr/bin/ruby
nms = [2, -1, -4, 0, 4, 3, -2, 3, 5]
nms.delete_if { |x| x < 0 }
puts nms.inspect
该示例引入了delete_if
方法,该方法删除满足块中显示的条件的所有项目。
nms.delete_if { |x| x < 0 }
此行从数组中删除所有负数。
$ ./delete_if.rb
[2, 0, 4, 3, 3, 5]
我们从nms
数组中删除了所有负数。
我们提出了另外两种处理多个数组项的方法。
#!/usr/bin/ruby
lts = %w{ a b c d e f g}
puts lts.inspect
lts.reject! do |e|
e =~ /[c-y]/
end
puts lts.inspect
lts.replace(["x", "y", "z"])
puts lts.inspect
我们使用两种方法,reject!
方法和replace
方法。
lts.reject! do |e|
e =~ /[c-y]/
end
reject!
方法删除块内满足特定条件的所有数组项。 就我们而言,我们删除所有符合正则表达式的字母; 从c
到y
的任何字母。 =~
运算符将字符串与正则表达式匹配。
lts.replace(["x", "y", "z"])
replace
方法将用其他给定项目替换项目。 如有必要,它会截断或扩展数组。
$ ./modify.rb
["a", "b", "c", "d", "e", "f", "g"]
["a", "b"]
["x", "y", "z"]
这是modify.rb
示例的输出。
Ruby 集操作
在本节中,我们介绍适用于 Ruby 数组的集操作。 在数学中,集合是不同对象的集合。
#!/usr/bin/ruby
A = [1, 2, 3, 4, 5]
B = [4, 5, 6, 7, 8]
union = A | B
isect = A & B
diff1 = A - B
diff2 = B - A
sdiff = (A - B) | (B - A)
puts "Union of arrays: #{union}"
puts "Intersection of arrays: #{isect}"
puts "Difference of arrays A - B: #{diff1}"
puts "Difference of arrays B - A: #{diff2}"
puts "Symmetric difference of arrays: #{sdiff}"
在上面的脚本中,我们演示了几个集合操作,并集,交集,差和对称差。
nums1 = [1, 2, 3, 4, 5]
nums2 = [4, 5, 6, 7, 8]
我们定义两个整数数组。 两者都是集合,因为数组中的每个元素仅显示一次。 这两个数组有两个共同的数字,即 4 和 5。
union = nums1 | nums2
此操作是两个数组的并集。 这两个数组被添加。 最终数组中的每个元素仅显示一次。
isect = A & B
以上操作是两组的交集。 结果是具有两个数组中都存在的元素的数组。 在我们的例子中,是 4 和 5。
diff1 = A - B
diff2 = B - A
在这里,我们有两个不同的运算,也称为补数。 在第一行中,我们获得 A 中存在但在 B 中不存在的所有元素。在第二行中,我们获得属于 B 而不是 A 的所有元素。
sdiff = (A - B) | (B - A)
在这里,我们有一个对称的差异。 对称差给出的元素要么在 A 要么在 B 中,但不在两个集合中。
$ ./setoperations.rb
Union of arrays: [1, 2, 3, 4, 5, 6, 7, 8]
Intersection of arrays: [4, 5]
Difference of arrays A - B: [1, 2, 3]
Difference of arrays B - A: [6, 7, 8]
Symmetric difference of arrays: [1, 2, 3, 6, 7, 8]
This is the output of the example.
Ruby 数组选择,收集,映射方法
在下一个示例中,我们将介绍三种方法:select
,collect
和map
方法。
#!/usr/bin/ruby
nums = [1, 3, 2, 6, 7, 12, 8, 15]
selected = nums.select do |e|
e > 10
end
puts selected.inspect
collected = nums.collect do |e|
e < 10
end
puts collected.inspect
mapped = nums.map do |e|
e*2
end
puts mapped.inspect
所有这些方法都对数组的元素执行批量操作。
selected = nums.select do |e|
e > 10
end
在上面的代码中,我们使用select
方法创建一个新数组。 对于新创建的数组,我们选择符合块内条件的元素。 在我们的例子中,我们选择所有大于 10 的元素。
collected = nums.collect do |e|
e < 10
end
collect
方法的工作方式略有不同。 它为每个元素调用附加的块,并从该块返回值。 新数组包含true
,false
值。
mapped = nums.map do |e|
e*2
end
map
方法的作用与collect
方法相同。 在以上几行中,我们从现有数组创建了一个新数组。 每个元素乘以 2。
$ ./mass.rb
[12, 15]
[true, true, true, true, true, false, true, false]
[2, 6, 4, 12, 14, 24, 16, 30]
这些是新创建的数组。
Ruby 数组排序元素
最后,我们将对数组中的元素进行排序。
#!/usr/bin/ruby
planets = %w{ Mercury Venus Earth Mars Jupiter
Saturn Uranus Neptune Pluto }
puts "#{planets.sort}"
puts "#{planets.reverse}"
puts "#{planets.shuffle}"
该示例使用三种 Ruby 数组方法来重组数组中的元素。
puts "#{planets.sort}"
sort
方法按字母顺序对数组元素进行排序。
puts "#{planets.reverse}"
reverse
方法返回一个新数组,其中所有元素的顺序相反。
puts "#{planets.shuffle}"
shuffle
方法随机重组数组元素。
$ ./ordering.rb
["Earth", "Jupiter", "Mars", "Mercury", "Neptune", "Pluto", "Saturn", ...]
["Pluto", "Neptune", "Uranus", "Saturn", "Jupiter", "Mars", "Earth", ...]
["Earth", "Jupiter", "Mercury", "Saturn", "Mars", "Venus", "Uranus", ...]
这是代码示例的示例输出。
在本章中,我们使用了 Ruby 数组。
Ruby 哈希
在 Ruby 教程的这一部分中,我们将使用 Ruby 哈希。
Ruby 哈希定义
Ruby 哈希是键-值对的集合。 它类似于数组。 与数组不同,哈希可以将任意对象作为索引。 数组只能具有整数。 哈希值按插入相应键的顺序枚举其值。 哈希有时称为关联数组。
哈希是功能强大的集合。 他们有许多方法可供程序员用来完成工作。
Ruby 哈希创建
可以通过两种基本方式创建哈希:使用new
关键字或哈希字面值。
#!/usr/bin/ruby
names = Hash.new
names[1] = "Jane"
names[2] = "Thomas"
puts names
第一个脚本创建一个哈希并将两个键值对添加到哈希对象中。
names = Hash.new
创建一个哈希对象。
names[1] = "Jane"
names[2] = "Thomas"
我们将两对值添加到哈希中。 数字 1、2 是哈希的键。 键放在方括号内。 名称是属于键的值。
puts names
puts
方法将哈希的字符串表示形式打印到控制台。 它也是哈希的字符串字面值。
$ ./create.rb
{1=>"Jane", 2=>"Thomas"}
从输出中,我们可以看到名称哈希的字面表示。 哈希以大括号为界。 键和值与=>
字符配对。
store
方法可用于使用某些值初始化哈希。 可以使用它代替方括号。
#!/usr/bin/ruby
names = Hash.new
names.store(1, "Jane")
names.store(2, "Thomas")
names.store(3, "Rebecca")
puts names
我们有一个类似的脚本。 这次我们使用store
方法。 该方法将给定键与给定值相关联,并将该对存储在哈希中。
names.store(1, "Jane")
store
方法的第一个参数是键,第二个参数是值。
在第三个脚本中,我们使用哈希字面值表示法创建哈希。 值用大括号括起来。 键值对与=>
字符相关联。
#!/usr/bin/ruby
domains = { "de" => "Germany",
"sk" => "Slovakia",
"hu" => "Hungary",
"us" => "United States",
"no" => "Norway"
}
puts domains["de"]
puts domains["sk"]
我们创建具有 5 对的域哈希。 这次键和值都是字符串类型。
domains = { "de" => "Germany",
"sk" => "Slovakia",
"hu" => "Hungary",
"us" => "United States",
"no" => "Norway"
}
这是哈希字面值表示法。 键值对放在大括号之间。 这些项目用逗号分隔。 使用=>
字符组合将键与值关联。
puts domains["de"]
在这里,我们打印与"de"
键关联的域名名称。
$ ./create3.rb
Germany
Slovakia
这是代码示例的输出。
Ruby 哈希基本操作
在本节中,我们介绍了一些用于 Ruby 哈希基础知识的方法。
#!/usr/bin/ruby
names = Hash.new
names[1] = "Jane"
names[2] = "Thomas"
names[3] = "Robert"
names[4] = "Julia"
names[5] = "Rebecca"
puts "The size of the hash is #{names.size}"
puts names.keys.inspect
puts names.values.inspect
在上面的 Ruby 脚本中,我们创建具有五个值的哈希。 我们介绍了三种哈希方法。
puts "The size of the hash is #{names.size}"
size
方法返回哈希的大小。 它是length
方法的同义词。
puts names.keys.inspect
puts names.values.inspect
keys
方法返回哈希的所有键。 values
方法以类似的方式返回哈希的所有值。 返回的数据为数组形式。 为了获得更可读的输出,我们还对返回的数组调用inspect
方法。
$ ./basic.rb
The size of the hash is 5
[1, 2, 3, 4, 5]
["Jane", "Thomas", "Robert", "Julia", "Rebecca"]
我们看到示例的输出。 请注意,最后两个方法的输出是两个数组。
本节的第二个示例介绍了三种不同的哈希方法。
#!/usr/bin/ruby
names1 = Hash.new
names1[1] = "Jane"
names1[2] = "Thomas"
names1[3] = "Robert"
names1[4] = "Julia"
names1[5] = "Rebecca"
names2 = names1.dup
puts names1.eql? names2
puts names1.empty?
names1.clear
puts names1.empty?
Ruby 脚本创建一个名称哈希。 它在对象上调用三种哈希方法。
names2 = names1.dup
我们通过调用dup
方法来创建哈希的副本。 该方法由父对象的哈希继承。
puts names1.eql? names2
eql?
方法比较两个哈希对象。 在我们的例子中,哈希值是相等的,并且该行打印正确。
puts names1.empty?
empty?
方法检查哈希是否为空。 该行打印false
,因为names1
哈希包含五个项目。
names1.clear
puts names1.empty?
clear
方法从哈希中删除所有项目。 连续调用empty?
方法将返回true
。
$ ./basic2.rb
true
false
true
这是示例输出。
我们有一些方法可以确定哈希中是否存在键或值。
#!/usr/bin/ruby
domains = { :de => "Germany", :sk => "Slovakia",
:no => "Norway", :us => "United States"
}
puts domains.has_key? :de
puts domains.include? :no
puts domains.key? :me
puts domains.member? :sk
puts domains.has_value? "Slovakia"
puts domains.value? "Germany"
我们用四个对创建一个域哈希。 键是符号。 通常将符号用作键,因为它们更有效。
puts domains.has_key? :de
puts domains.include? :no
puts domains.key? :me
puts domains.member? :sk
在这里,我们有四种确定键是否在哈希中的方法。 他们都做同样的事。 它们是同义词。
puts domains.has_value? "Slovakia"
puts domains.value? "Germany"
这两种方法检查两个字符串是否在哈希中。
$ ./has.rb
true
true
false
true
true
true
这是示例的输出。
在本节的最后一个示例中,我们将从哈希中读取值。
#!/usr/bin/ruby
stones = { 1 => "garnet", 2 => "topaz",
3 => "opal", 4 => "amethyst"
}
puts stones.fetch 1
puts stones[2]
puts stones.values_at 1, 2, 3
Ruby 脚本提供了三种用于读取哈希值的哈希方法。
puts stones.fetch 1
fetch
方法读取给定键的值。
puts stones[2]
方括号可用于获取值。 在我们的例子中,该行将"topaz"
打印到控制台。
puts stones.values_at 1, 2, 3
values_at
方法可用于一步获得多个值。 该方法返回给定键的值的数组。
$ ./read.rb
garnet
topaz
garnet
topaz
opal
This is the output of the example.
Ruby 哈希迭代
有几种方法可用于遍历 Ruby 哈希。
#!/usr/bin/ruby
stones = { 1 => "garnet", 2 => "topaz",
3 => "opal", 4 => "amethyst"
}
stones.each { |k, v| puts "Key: #{k}, Value: #{v}" }
stones.each_key { |key| puts "#{key}" }
stones.each_value { |val| puts "#{val}" }
stones.each_pair { |k, v| puts "Key: #{k}, Value: #{v}" }
在上面的示例中,我们介绍了四种方法。 我们使用它们显示哈希的所有键,值以及键和值。
stones.each { |k, v| puts "Key: #{k}, Value: #{v}" }
each
方法为哈希中的每个键调用给定的块,并将键值对作为参数传递。
stones.each_key { |key| puts "#{key}" }
我们使用each_key
方法循环遍历哈希的所有键。 它们被打印到控制台。
stones.each_value { |val| puts "#{val}" }
each_value
可用于循环遍历哈希值。
stones.each_pair { |k, v| puts "Key: #{k}, Value: #{v}" }
each_pair
方法是each
方法的同义词。 我们遍历石头哈希的键和值。
$ ./loop.rb
Key: 1, Value: garnet
Key: 2, Value: topaz
Key: 3, Value: opal
Key: 4, Value: amethyst
1
2
3
4
garnet
topaz
opal
amethyst
Key: 1, Value: garnet
Key: 2, Value: topaz
Key: 3, Value: opal
Key: 4, Value: amethyst
输出显示石头哈希的键和值,键,值。
Ruby 删除哈希中的对
在以下示例中,我们将关注从哈希中删除对的方法。 这包括删除单个对的方法以及可以一步删除多个键值的方法。
#!/usr/bin/ruby
names = Hash.new
names[1] = "Jane"
names[2] = "Thomas"
names[3] = "Robert"
names[4] = "Julia"
names[5] = "Rebecca"
names.delete 4
names.shift
puts names
在脚本中,我们有两种方法:delete
和shift
。 delete
方法删除并返回指定键的值。 shift
方法从哈希中删除第一对。 它还以数组形式返回删除的对。
names.delete 4
在这里,我们删除一对4 => "Julia"
。
names.shift
该代码行删除了第一对,即1 => "Jane"
。
$ ./deleteitem.rb
{2=>"Thomas", 3=>"Robert", 5=>"Rebecca"}
在输出中,我们可以看到剩下的哈希对。
reject
和delete_if
方法可以从哈希中删除多对。 这些方法删除对块中给定条件返回true
的对。 两种方法之间有重要区别。 reject
方法适用于哈希的副本,而delete_if
方法适用于原始哈希。
#!/usr/local/bin/ruby
names1 = Hash.new
names1[1] = "Jane"
names1[2] = "Thomas"
names1[3] = "Robert"
names1[4] = "Julia"
names1[5] = "Rebecca"
puts names1.reject { |k, v| v =~ /R.*/ }
puts names1
puts names1.delete_if { |k, v| k<=3 }
puts names1
该示例使用前面提到的方法删除多个对。
puts names1.reject { |k, v| v =~ /R.*/ }
reject
方法删除适合块中正则表达式的所有值。 返回修改后的哈希,并且原始哈希不会更改。
puts names1
该行的输出确认原始哈希是完整的。
puts names1.delete_if { |k, v| k<=3 }
在这种情况下,我们将删除所有键小于或等于 3 的对。该方法将修改原始哈希。
$ ./massdelete.rb
{1=>"Jane", 2=>"Thomas", 4=>"Julia"}
{1=>"Jane", 2=>"Thomas", 3=>"Robert", 4=>"Julia", 5=>"Rebecca"}
{4=>"Julia", 5=>"Rebecca"}
{4=>"Julia", 5=>"Rebecca"}
示例的输出。
Ruby 在哈希中添加元素
Ruby 的merge
和update
方法将(键,值)对添加到哈希。 Ruby 具有用于添加哈希的方法。
#!/usr/bin/ruby
names1 = Hash.new
names1[1] = "Jane"
names1[2] = "Thomas"
names2 = Hash.new
names2[3] = "Robert"
names2[4] = "Julia"
names = names1.merge names2
puts names
names = names1.update names2
puts names
在 Ruby 脚本中,我们创建两个哈希。 然后我们在它们上应用merge
和update
方法。
names = names1.merge names2
puts names
names1
和names2
哈希组合在一起。 结果分配给名称哈希。 我们打印新创建的哈希。
$ ./merge.rb
{1=>"Jane", 2=>"Thomas", 3=>"Robert", 4=>"Julia"}
{1=>"Jane", 2=>"Thomas", 3=>"Robert", 4=>"Julia"}
如我们所见,最终的哈希包含names1
和names2
哈希对。
Ruby 哈希merge
和merge!
方法
在最后一节中,我们回顾了一个常见的 Ruby 习惯用法。 几种 Ruby 方法的对应方法都以感叹号结尾。 此标记没有语法意义,表示方法修改了调用该方法的对象。
#!/usr/bin/ruby
names1 = Hash.new
names1[1] = "Jane"
names1[2] = "Thomas"
names2 = Hash.new
names2[3] = "Robert"
names2[4] = "Julia"
names = names1.merge names2
puts names
puts names1
names = names1.merge! names2
puts names
puts names1
我们将演示merge
和merge!
方法的区别。
names = names1.merge names2
merge
不会修改names1
哈希。 它可以在其副本上工作。
names = names1.merge! names2
merge!
方法适用于原始哈希。 names1
哈希已更改。
$ ./merge2.rb
{1=>"Jane", 2=>"Thomas", 3=>"Robert", 4=>"Julia"}
{1=>"Jane", 2=>"Thomas"}
{1=>"Jane", 2=>"Thomas", 3=>"Robert", 4=>"Julia"}
{1=>"Jane", 2=>"Thomas", 3=>"Robert", 4=>"Julia"}
这是merge2.rb
程序的输出。
在本章中,我们使用了 Ruby 哈希。
Ruby 中的面向对象编程
在 Ruby 教程的这一部分中,我们将讨论 Ruby 中的面向对象编程。
编程语言具有过程编程,函数式编程和面向对象的编程范例。 Ruby 是一种具有某些函数式和过程特性的面向对象的语言。
面向对象编程(OOP)是一种使用对象及其相互作用设计应用和计算机程序的编程范例。
OOP 中的基本编程概念是:
- 抽象
- 多态
- 封装
- 继承
抽象通过建模适合该问题的类来简化复杂的现实。 多态是将运算符或函数以不同方式用于不同数据输入的过程。 封装对其他对象隐藏了类的实现细节。 继承是一种使用已经定义的类形成新类的方法。
Ruby 对象
对象是 Ruby OOP 程序的基本构建块。 对象是数据和方法的组合。 在 OOP 程序中,我们创建对象。 这些对象通过方法进行通信。 每个对象都可以接收消息,发送消息和处理数据。
创建对象有两个步骤。 首先,我们定义一个类。 类是对象的模板。 它是一个蓝图,描述了类对象共享的状态和行为。 一个类可以用来创建许多对象。 在运行时从类创建的对象称为该特定类的实例。
#!/usr/bin/ruby
class Being
end
b = Being.new
puts b
在第一个示例中,我们创建一个简单的对象。
class Being
end
这是一个简单的类定义。 模板的主体为空。 它没有任何数据或方法。
b = Being.new
我们创建Being
类的新实例。 为此,我们有new
方法。 b
变量存储新创建的对象。
puts b
我们将对象打印到控制台以获取该对象的一些基本描述。 实际上,当我们打印对象时,我们将其称为to_s
方法。 但是我们还没有定义任何方法。 这是因为创建的每个对象都继承自基本Object
。 它具有一些基本功能,这些功能在所有创建的对象之间共享。 其中之一是to_s
方法。
$ ./simple.rb
#<Being:0x9f3c290>
我们得到对象类名。
Ruby 构造器
构造器是一种特殊的方法。 创建对象时会自动调用它。 构造器不返回值。 构造器的目的是初始化对象的状态。 Ruby 中的构造器称为initialize
。 构造器不返回任何值。
使用super
方法调用父对象的构造器。 它们按继承顺序被调用。
#!/usr/bin/ruby
class Being
def initialize
puts "Being is created"
end
end
Being.new
我们有一个存在类。
class Being
def initialize
puts "Being is created"
end
end
Betweening
类具有一个名为initialize
的构造方法。 它将消息打印到控制台。 Ruby 方法的定义位于def
和end
关键字之间。
Being.new
创建Being
类的实例。 创建对象时,将调用构造方法。
$ ./constructor.rb
Being is created
这是程序的输出。
对象的属性是捆绑在该对象内部的数据项。 这些项目也称为实例变量或成员字段。 实例变量是在类中定义的变量,该类中的每个对象都有一个单独的副本。
在下一个示例中,我们初始化类的数据成员。 变量的初始化是构造器的典型工作。
#!/usr/bin/ruby
class Person
def initialize name
@name = name
end
def get_name
@name
end
end
p1 = Person.new "Jane"
p2 = Person.new "Beky"
puts p1.get_name
puts p2.get_name
在上面的 Ruby 代码中,我们有一个带有一个成员字段的Person
类。
class Person
def initialize name
@name = name
end
在Person
类的构造器中,我们将一个member
字段设置为一个值名称。 名称参数在创建时传递给构造器。 构造器是称为initialize
的方法,该方法在创建实例对象时被调用。 @name
是一个实例变量。 实例变量在 Ruby 中以@
字符开头。
def get_name
@name
end
get_name
方法返回成员字段。 在 Ruby 中,成员字段只能通过方法访问。
p1 = Person.new "Jane"
p2 = Person.new "Beky"
我们创建Person
类的两个对象。 字符串参数传递给每个对象构造器。 名称存储在每个对象唯一的实例变量中。
puts p1.get_name
puts p2.get_name
我们通过在每个对象上调用get_name
来打印成员字段。
$ ./person.rb
Jane
Beky
我们看到了程序的输出。 Person
类的每个实例都有其自己的名称成员字段。
我们可以创建对象而无需调用构造器。 Ruby 为此有一种特殊的allocate
方法。 allocate
方法为类的新对象分配空间,并且不会在新实例上调用initialize
。
#!/usr/bin/ruby
class Being
def initialize
puts "Being created"
end
end
b1 = Being.new
b2 = Being.allocate
puts b2
在示例中,我们创建两个对象。 第一个对象使用new
方法,第二个对象使用allocate
方法。
b1 = Being.new
在这里,我们使用new
关键字创建对象的实例。 调用构造器方法initialize
,并将消息打印到控制台。
b2 = Being.allocate
puts b2
在allocate
方法的情况下,不调用构造器。 我们使用puts
关键字调用to_s
方法以显示该对象已创建。
$ ./allocate.rb
Being created
#<Being:0x8ea0044>
在这里,我们看到了程序的输出。
Ruby 构造器重载
构造器重载是在一个类中具有多种类型的构造器的能力。 这样,我们可以创建具有不同数量或不同类型参数的对象。
Ruby 没有我们从某些编程语言中知道的构造器重载。 可以使用 Ruby 中的默认参数值在某种程度上模拟此行为。
#!/usr/bin/ruby
class Person
def initialize name="unknown", age=0
@name = name
@age = age
end
def to_s
"Name: #{@name}, Age: #{@age}"
end
end
p1 = Person.new
p2 = Person.new "unknown", 17
p3 = Person.new "Becky", 19
p4 = Person.new "Robert"
p p1, p2, p3, p4
本示例说明了如何在具有两个成员字段的Person
类上模拟构造器重载。 如果未指定name
参数,则使用字符串"unknown"
。 对于未指定的年龄,我们为 0。
def initialize name="unknown", age=0
@name = name
@age = age
end
构造器有两个参数。 它们具有默认值。 如果在创建对象时未指定自己的值,则使用默认值。 请注意,必须保留参数的顺序。 首先是名字,然后是年龄。
p1 = Person.new
p2 = Person.new "unknown", 17
p3 = Person.new "Becky", 19
p4 = Person.new "Robert"
p p1, p2, p3, p4
我们创建四个对象。 构造器采用不同数量的参数。
$ ./consover.rb
Name: unknown, Age: 0
Name: unknown, Age: 17
Name: Becky, Age: 19
Name: Robert, Age: 0
这是示例的输出。
Ruby 方法
方法是在类主体内定义的函数。 它们用于通过对象的属性执行操作。 在 OOP 范式的封装概念中,方法至关重要。 例如,我们的AccessDatabase
类中可能有一个connect
方法。 我们无需告知该方法如何准确地连接到数据库。 我们只需要知道它用于连接数据库。 这对于划分编程中的职责至关重要,尤其是在大型应用中。
在 Ruby 中,只能通过方法访问数据。
#!/usr/bin/ruby
class Person
def initialize name
@name = name
end
def get_name
@name
end
end
per = Person.new "Jane"
puts per.get_name
puts per.send :get_name
该示例显示了两种调用方法的基本方法。
puts per.get_name
常见的方法是在对象上使用点运算符,后跟方法名称。
puts per.send :get_name
替代方法是使用内置的send
方法。 它以方法的符号作为参数。
方法通常对对象的数据执行某些操作。
#!/usr/bin/ruby
class Circle
@@PI = 3.141592
def initialize
@radius = 0
end
def set_radius radius
@radius = radius
end
def area
@radius * @radius * @@PI
end
end
c = Circle.new
c.set_radius 5
puts c.area
在代码示例中,我们有一个Circle
类。 我们定义了两种方法。
@@PI = 3.141592
我们在Circle
类中定义了@@PI
变量。 它是一个类变量。 类变量以 Ruby 中的@@
信号开头。 类变量属于类,而不属于对象。 每个对象都可以访问其类变量。 我们使用@@PI
计算圆的面积。
def initialize
@radius = 0
end
我们只有一个成员字段。 它是圆的半径。 如果要从外部修改此变量,则必须使用公共可用的set_radius
方法。 数据受到保护。
def set_radius radius
@radius = radius
end
这是set_radius
方法。 它为@radius
实例变量提供了一个新值。
def area
@radius * @radius * @@PI
end
area
方法返回圆的面积。 这是方法的典型任务。 它可以处理数据并为我们带来一些价值。
c = Circle.new
c.set_radius 5
puts c.area
我们创建Circle
类的实例,并通过在圆对象上调用set_radius
方法来设置其半径。 我们使用点运算符来调用该方法。
$ ./circle.rb
78.5398
运行示例,我们得到此输出。
Ruby 访问修饰符
访问修饰符设置方法和成员字段的可见性。 Ruby 具有三个访问修饰符:public
,protected
和private
。 在 Ruby 中,所有数据成员都是私有的。 访问修饰符只能在方法上使用。 除非我们另有说明,否则 Ruby 方法是公共的。
可以从类的定义内部以及从类的外部访问public
方法。 protected
和private
方法之间的区别很细微。 在类的定义之外都无法访问它们。 只能在类本身内部以及继承的或父类访问它们。
请注意,与其他面向对象的编程语言不同,继承在 Ruby 访问修饰符中不扮演的角色。 只有两件事很重要。 首先,如果我们在类定义的内部或外部调用方法。 其次,如果我们使用或不使用指向当前接收器的self
关键字。
访问修饰符可防止意外修改数据。 它们使程序更强大。 某些方法的实现可能会发生变化。 这些方法是很好的私有方法。 公开给用户的接口仅应在确实必要时更改。 多年来,用户习惯于使用特定的方法,并且通常不赞成向后兼容。
#!/usr/bin/ruby
class Some
def method1
puts "public method1 called"
end
public
def method2
puts "public method2 called"
end
def method3
puts "public method3 called"
method1
self.method1
end
end
s = Some.new
s.method1
s.method2
s.method3
该示例说明了公共 Ruby 方法的用法。
def method1
puts "public method1 called"
end
即使我们未指定public
访问修饰符,method1
也是公共的。 这是因为如果没有另外指定,默认情况下方法是公共的。
public
def method2
puts "public method2 called"
end
...
public
关键字后面的方法是公共的。
def method3
puts "public method3 called"
method1
self.method1
end
从公共method3
内部,我们调用带有和不带有self
关键字的其他公共方法。
s = Some.new
s.method1
s.method2
s.method3
公共方法是唯一可以在类定义之外调用的方法,如下所示。
$ ./public_methods.rb
public method1 called
public method2 called
public method3 called
public method1 called
public method1 called
运行示例,我们将获得以下输出。
下一个示例着眼于私有方法。
#!/usr/bin/ruby
class Some
def initialize
method1
# self.method1
end
private
def method1
puts "private method1 called"
end
end
s = Some.new
# s.method1
私有方法是 Ruby 中最严格的方法。 只能在类定义内调用它们,而不能使用self
关键字。
def initialize
method1
# self.method1
end
在方法的构造器中,我们称为私有method1
。 带有self
的方法被注释。 接收者无法指定私有方法。
private
def method1
puts "private method1 called"
end
关键字private
之后的方法在 Ruby 中是私有的。
s = Some.new
# s.method1
我们创建Some
类的实例。 禁止在类定义之外调用该方法。 如果我们取消注释该行,则 Ruby 解释器将给出错误。
$ ./private_methods.rb
private method called
示例代码的输出。
最后,我们将使用受保护的方法。 Ruby 中的保护方法和私有方法之间的区别非常微妙。 受保护的方法就像私有的。 只有一个小差异。 可以使用指定的self
关键字来调用它们。
#!/usr/bin/ruby
class Some
def initialize
method1
self.method1
end
protected
def method1
puts "protected method1 called"
end
end
s = Some.new
# s.method1
上面的示例显示了使用中的受保护方法。
def initialize
method1
self.method1
end
可以使用self
关键字来调用受保护的方法。
protected
def method1
puts "protected method1 called"
end
受保护的方法前面带有protected
关键字。
s = Some.new
# s.method1
不能在类定义之外调用受保护的方法。 取消注释该行将导致错误。
Ruby 继承
继承是一种使用已经定义的类形成新类的方法。 新形成的类称为派生的类,我们派生的类称为基类。 继承的重要好处是代码重用和降低程序的复杂性。 派生类(后代)将覆盖或扩展基类(祖先)的功能。
#!/usr/bin/ruby
class Being
def initialize
puts "Being class created"
end
end
class Human < Being
def initialize
super
puts "Human class created"
end
end
Being.new
Human.new
在此程序中,我们有两个类:基础Being
类和派生的Human
类。 派生类继承自基类。
class Human < Being
在 Ruby 中,我们使用<
运算符创建继承关系。 Human
类继承自Being
类。
def initialize
super
puts "Human class created"
end
super
方法调用父类的构造器。
Being.new
Human.new
我们实例化Being
和Human
类。
$ ./inheritance.rb
Being class created
Being class created
Human class created
首先创建Being
类。 派生的Human
类还调用其父级的构造器。
对象可能涉及复杂的关系。 一个对象可以有多个祖先。 Ruby 有一个方法ancestors
,它提供了特定类的祖先列表。
每个 Ruby 对象自动是Object
和BasicObject
类以及Kernel
模块的后代。 它们内置在 Ruby 语言的核心中。
#!/usr/bin/ruby
class Being
end
class Living < Being
end
class Mammal < Living
end
class Human < Mammal
end
p Human.ancestors
在此示例中,我们有四个类:Human
是Mammal
,Living
和Being
。
p Human.ancestors
我们打印人类类的祖先。
$ ./ancestors.rb
[Human, Mammal, Living, Being, Object, Kernel, BasicObject]
人类类具有三个习俗和三个内置祖先。
接下来是一个更复杂的示例。
#!/usr/bin/ruby
class Being
@@count = 0
def initialize
@@count += 1
puts "Being class created"
end
def show_count
"There are #{@@count} beings"
end
end
class Human < Being
def initialize
super
puts "Human is created"
end
end
class Animal < Being
def initialize
super
puts "Animal is created"
end
end
class Dog < Animal
def initialize
super
puts "Dog is created"
end
end
Human.new
d = Dog.new
puts d.show_count
我们有四个类。 继承层次更加复杂。 Human
和Animal
类继承自Being
类。 Dog
类直接继承自Animal
类,并且进一步继承自Being
类。 我们还使用一个类变量来计算创建的生物的数量。
@@count = 0
我们定义一个类变量。 类变量以@@
信号开头,并且它属于该类,而不是该类的实例。 我们用它来计算创造的生物数量。
def initialize
@@count += 1
puts "Being class created"
end
每次实例化Being
类时,我们都会将@@count
变量加 1。 这样,我们就可以跟踪创建的实例数。
class Animal < Being
...
class Dog < Animal
...
Animal
继承自Being
,Dog
继承自Animal
。 另外,Dog
也继承自Being
。
Human.new
d = Dog.new
puts d.show_count
我们从Human
和Dog
类创建实例。 我们在Dog
对象上调用show_count
方法。 Dog
类没有这种方法。 然后调用祖父级Being
的方法。
$ ./inheritance2.rb
Being class created
Human is created
Being class created
Animal is created
Dog is created
There are 2 beings
Human
对象调用两个构造器。 Dog
对象调用三个构造器。 有两个实例化的存在。
继承在方法和数据成员的可见性中不起作用。 与许多常见的面向对象的编程语言相比,这是一个明显的区别。
在 C# 或 Java 中,公共和受保护的数据成员和方法是继承的。 私有数据成员和方法不是。 与此相反,私有数据成员和方法也在 Ruby 中继承。 数据成员和方法的可见性不受 Ruby 中继承的影响。
#!/usr/bin/ruby
class Base
def initialize
@name = "Base"
end
private
def private_method
puts "private method called"
end
protected
def protected_method
puts "protected_method called"
end
public
def get_name
return @name
end
end
class Derived < Base
def public_method
private_method
protected_method
end
end
d = Derived.new
d.public_method
puts d.get_name
在示例中,我们有两个类。 Derived
类继承自Base
类。 它继承了所有三种方法和一个数据字段。
def public_method
private_method
protected_method
end
在Derived
类的public_method
中,我们调用一种私有方法和一种受保护方法。 它们在父类中定义。
d = Derived.new
d.public_method
puts d.get_name
我们创建Derived
类的实例。 我们称为public_method
,也称为get_name
,它返回私有@name
变量。 请记住,所有实例变量在 Ruby 中都是私有的。 无论@name
是私有的还是在父类中定义的,get_name
方法都会返回该变量。
$ ./inheritance3.rb
private method called
protected_method called
Base
该示例的输出确认,在 Ruby 语言中,子对象从其父级继承了public
,protected
,private
方法和private
成员字段。
Ruby super
方法
super
方法在父类的类中调用相同名称的方法。 如果该方法没有参数,它将自动传递其所有参数。 如果我们写super()
,则不会将任何参数传递给父方法。
#!/usr/bin/ruby
class Base
def show x=0, y=0
p "Base class, x: #{x}, y: #{y}"
end
end
class Derived < Base
def show x, y
super
super x
super x, y
super()
end
end
d = Derived.new
d.show 3, 3
在示例中,我们在层次结构中有两个类。 它们都有显示方法。 派生类中的show
方法使用super
方法调用基类中的show
方法。
def show x, y
super
super x
super x, y
super()
end
不带任何参数的super
方法使用传递给Derived
类的show
方法的参数调用父级的show
方法:此处,x = 3
和y = 3
。 super()
方法不将任何参数传递给父级的show
方法。
$ ./super.rb
"Base class, x: 3, y: 3"
"Base class, x: 3, y: 0"
"Base class, x: 3, y: 3"
"Base class, x: 0, y: 0"
这是示例的输出。
这是 Ruby 中 OOP 描述的第一部分。
Ruby 中的面向对象编程 II
在 Ruby 教程的这一部分中,我们将继续讨论 Ruby 中的面向对象编程。
我们从属性访问器开始。 我们将介绍类常量,类方法和运算符重载。 我们将定义多态,并展示如何在 Ruby 中使用它。 我们还将提到模块和异常。
Ruby 属性访问器
所有 Ruby 变量都是私有的。 只能通过方法访问它们。 这些方法通常称为设置器和获取器。 创建一个设置器和一个获取器方法是非常常见的任务。 因此,Ruby 具有方便的方法来创建两种类型的方法。 它们是attr_reader
,attr_writer
和attr_accessor
。
attr_reader
创建获取器方法。 attr_writer
方法为该设置器创建设置器方法和实例变量。 attr_accessor
方法同时创建获取器方法,设置器方法及其实例变量。
#!/usr/bin/ruby
class Car
attr_reader :name, :price
attr_writer :name, :price
def to_s
"#{@name}: #{@price}"
end
end
c1 = Car.new
c2 = Car.new
c1.name = "Porsche"
c1.price = 23500
c2.name = "Volkswagen"
c2.price = 9500
puts "The #{c1.name} costs #{c1.price}"
p c1
p c2
我们有汽车类。 在类的定义中,我们使用attr_reader
和attr_writer
为Car
类创建两个获取器和设置器方法。
attr_reader :name, :price
在这里,我们创建两个名为name
和price
的实例方法。 注意,attr_reader
将方法的符号作为参数。
attr_writer :name, :price
attr_writer
创建两个名为name
和price
的设置方法,以及两个实例变量@name
和@price
。
c1.name = "Porsche"
c1.price = 23500
在这种情况下,调用了两个设置器方法以用一些数据填充实例变量。
puts "The #{c1.name} costs #{c1.price}"
在这里,调用了两个获取器方法以从c1
对象的实例变量获取数据。
$ ./arw.rb
The Porsche costs 23500
Porsche: 23500
Volkswagen: 9500
这是示例的输出。
如前所述,attr_accessor
方法创建获取器,设置器方法及其实例变量。
#!/usr/bin/ruby
class Book
attr_accessor :title, :pages
end
b1 = Book.new
b1.title = "Hidden motives"
b1.pages = 255
p "The book #{b1.title} has #{b1.pages} pages"
我们有一个Book
类,其中attr_accessor
创建两对方法和两个实例变量。
class Book
attr_accessor :title, :pages
end
设置标题和页面方法以及@title
和@pages
实例变量的attr_accessor
方法。
b1 = Book.new
b1.title = "Hidden motives"
b1.pages = 255
创建一个Book
类的对象。 两种设置方法填充对象的实例变量。
p "The book #{b1.title} has #{b1.pages} pages"
在此代码行中,我们使用两种获取器方法来读取实例变量的值。
$ ./accessor.rb
"The book Hidden motives has 255 pages"
这是示例输出。
Ruby 类常量
Ruby 使您可以创建类常量。 这些常量不属于具体对象。 他们属于阶级。 按照约定,常量用大写字母表示。
#!/usr/bin/ruby
class MMath
PI = 3.141592
end
puts MMath::PI
我们有一个带有PI
常量的MMath
类。
PI = 3.141592
我们创建一个PI
常量。 请记住,Ruby 中的常量不是强制性的。
puts MMath::PI
我们使用::
运算符访问PI
常量。
$ ./classconstant.rb
3.141592
运行示例,我们看到此输出。
Ruby to_s
方法
每个对象都有一个to_s
方法。 它返回对象的字符串表示形式。 请注意,当puts
方法将对象作为参数时,将调用该对象的to_s
。
#!/usr/bin/ruby
class Being
def to_s
"This is Being class"
end
end
b = Being.new
puts b.to_s
puts b
我们有一个Beinging
类,其中我们重写了to_s
方法的默认实现。
def to_s
"This is Being class"
end
创建的每个类都从基Object
继承。 to_s
方法属于此类。 我们覆盖to_s
方法并创建一个新的实现。 我们提供了一个易于理解的对象描述。
b = Being.new
puts b.to_s
puts b
我们创建一个Beinging
类,并调用to_s
方法两次。 第一次是显式的,第二次是隐式的。
$ ./tostring.rb
This is Being class
This is Being class
这是我们运行示例时得到的。
运算符重载
运算符重载是指不同的运算符根据其参数具有不同的实现的情况。
在 Ruby 中,运算符和方法之间只有微小的区别。
#!/usr/bin/ruby
class Circle
attr_accessor :radius
def initialize r
@radius = r
end
def +(other)
Circle.new @radius + other.radius
end
def to_s
"Circle with radius: #{@radius}"
end
end
c1 = Circle.new 5
c2 = Circle.new 6
c3 = c1 + c2
p c3
在示例中,我们有一个Circle
类。 我们在类中重载了+
运算符。 我们使用它来添加两个圆形对象。
def +(other)
Circle.new @radius + other.radius
end
我们定义一个带有+
名称的方法。 该方法将两个圆形对象的半径相加。
c1 = Circle.new 5
c2 = Circle.new 6
c3 = c1 + c2
我们创建两个圆形对象。 在第三行中,我们添加这两个对象以创建一个新对象。
$ ./operatoroverloading.rb
Circle with radius: 11
将这两个圆形对象相加会创建第三个半径为 11 的对象。
Ruby 类方法
Ruby 方法可以分为类方法和实例方法。 类方法在类上调用。 不能在类的实例上调用它们。
类方法不能访问实例变量。
#!/usr/bin/ruby
class Circle
def initialize x
@r = x
end
def self.info
"This is a Circle class"
end
def area
@r * @r * 3.141592
end
end
p Circle.info
c = Circle.new 3
p c.area
上面的代码示例展示了Circle
类。 除了构造器方法外,它还具有一个类和一个实例方法。
def self.info
"This is a Circle class"
end
以self
关键字开头的方法是类方法。
def area
"Circle, radius: #{@r}"
end
实例方法不能以self
关键字开头。
p Circle.info
我们称为类方法。 注意,我们在类名上调用该方法。
c = Circle.new 3
p c.area
要调用实例方法,我们必须首先创建一个对象。 实例方法总是在对象上调用。 在我们的例子中,c
变量保存对象,然后在圆对象上调用area
方法。 我们利用点运算符。
$ ./classmethods.rb
"This is a Circle class"
28.274328
描述 Ruby 中类方法的代码示例的输出。
有三种方法可以在 Ruby 中创建类方法。
#!/usr/bin/ruby
class Wood
def self.info
"This is a Wood class"
end
end
class Brick
class << self
def info
"This is a Brick class"
end
end
end
class Rock
end
def Rock.info
"This is a Rock class"
end
p Wood.info
p Brick.info
p Rock.info
该示例包含三个类。 它们每个都有一个类方法。
def self.info
"This is a Wood class"
end
类方法可以以self
关键字开头。
class << self
def info
"This is a Brick class"
end
end
另一种方法是将方法定义放在class << self
构造之后。
def Rock.info
"This is a Rock class"
end
这是在 Ruby 中定义类方法的第三种方法。
$ ./classmethods2.rb
"This is a Wood class"
"This is a Brick class"
"This is a Rock class"
我们看到在Wood
,Brick
和Rock
类上调用所有三个类方法的输出。
在 Ruby 中创建实例方法的三种方法
Ruby 有三种创建实例方法的基本方法。 实例方法属于对象的实例。 使用点运算符在对象上调用它们。
#!/usr/bin/ruby
class Wood
def info
"This is a wood object"
end
end
wood = Wood.new
p wood.info
class Brick
attr_accessor :info
end
brick = Brick.new
brick.info = "This is a brick object"
p brick.info
class Rock
end
rock = Rock.new
def rock.info
"This is a rock object"
end
p rock.info
在示例中,我们从Wood
,Brick
和Rock
类创建三个实例对象。 每个对象都有一个定义的实例方法。
class Wood
def info
"This is a wood object"
end
end
wood = Wood.new
p wood.info
这可能是定义和调用实例方法的最常用方法。 info
方法在Wood
类中定义。 之后,创建对象,然后在对象实例上调用info
方法。
class Brick
attr_accessor :info
end
brick = Brick.new
brick.info = "This is a brick object"
p brick.info
另一种方法是使用属性访问器创建方法。 这是一种方便的方法,可以节省程序员的键入时间。 attr_accessor
创建两个方法,即获取器和设置器方法。它还创建一个实例变量来存储数据。 使用信息设置器方法,将创建砖对象,并将数据存储在@info
变量中。 最后,该消息由info
获取器方法读取。
class Rock
end
rock = Rock.new
def rock.info
"This is a rock object"
end
p rock.info
在第三种方法中,我们创建一个空的Rock
类。 该对象被实例化。 以后,将动态创建一个方法并将其放置到对象中。
$ ./threeways.rb
"This is a wood object"
"This is a brick object"
"This is a rock object"
示例输出。
Ruby 多态
多态是对不同的数据输入以不同方式使用运算符或函数的过程。 实际上,多态意味着如果类 B 从类 A 继承,则不必继承关于类 A 的所有内容; 它可以完成 A 类所做的某些事情。 (维基百科)
通常,多态是以不同形式出现的能力。 从技术上讲,它是重新定义派生类的方法的能力。 多态与将特定实现应用于接口或更通用的基类有关。
请注意,在静态类型的语言(例如 C++ ,Java 或 C# )和动态类型的语言(例如 Python 或 Ruby)中,多态的定义有所不同。 在静态类型的语言中,当编译器在编译时或运行时确定方法定义时,这一点很重要。 在动态类型的语言中,我们专注于具有相同名称的方法执行不同操作的事实。
#!/usr/bin/ruby
class Animal
def make_noise
"Some noise"
end
def sleep
puts "#{self.class.name} is sleeping."
end
end
class Dog < Animal
def make_noise
'Woof!'
end
end
class Cat < Animal
def make_noise
'Meow!'
end
end
[Animal.new, Dog.new, Cat.new].each do |animal|
puts animal.make_noise
animal.sleep
end
我们有一个简单的继承层次结构。 有一个Animal
基类和两个后代,即Cat
和Dog
。 这三个类中的每一个都有其自己的make_noise
方法实现。 后代方法的实现替换了Animal
类中方法的定义。
class Dog < Animal
def make_noise
'Woof!'
end
end
Dog
类中的make_noise method
的实现替换了Animal
类中的make_noise
的实现。
[Animal.new, Dog.new, Cat.new].each do |animal|
puts animal.make_noise
animal.sleep
end
我们为每个类创建一个实例。 我们在对象上调用make_noise
和sleep
方法。
$ ./polymorhism.rb
Some noise
Animal is sleeping.
Woof!
Dog is sleeping.
Meow!
Cat is sleeping.
这是polymorhism.rb
脚本的输出。
Ruby 模块
Ruby Module
是方法,类和常量的集合。 模块与类相似,但有一些区别。 模块不能有实例,也不能是子类。
模块用于对相关的类进行分组,方法和常量可以放入单独的模块中。 这也防止了名称冲突,因为模块封装了它们所包含的对象。 在这方面,Ruby 模块类似于 C# 名称空间和 Java 包。
模块还支持在 Ruby 中使用 mixins。 mixin 是 Ruby 工具,用于创建多重继承。 如果一个类从一个以上的类继承功能,那么我们说的是多重继承。
#!/usr/bin/ruby
puts Math::PI
puts Math.sin 2
Ruby 具有内置的Math
模块。 它具有多种方法和一个常数。 我们使用::
运算符访问PI
常量。 和类中一样,方法由点运算符访问。
#!/usr/bin/ruby
include Math
puts PI
puts sin 2
如果我们在脚本中包含模块,则可以直接引用Math
对象,而无需使用Math
名称。 使用include
关键字将模块添加到脚本中。
$ ./modules.rb
3.141592653589793
0.9092974268256817
程序的输出。
在下面的示例中,我们展示了如何使用模块来组织代码。
#!/usr/bin/ruby
module Forest
class Rock ; end
class Tree ; end
class Animal ; end
end
module Town
class Pool ; end
class Cinema ; end
class Square ; end
class Animal ; end
end
p Forest::Tree.new
p Forest::Rock.new
p Town::Cinema.new
p Forest::Animal.new
p Town::Animal.new
Ruby 代码可以在语义上进行分组。 岩石和树木属于森林。 游泳池,电影院,广场属于一个城镇。 通过使用模块,我们的代码具有一定的顺序。 动物也可以在森林和城镇中。 在一个脚本中,我们不能定义两个动物类。 他们会发生冲突。 将它们放在不同的模块中,我们可以解决问题。
p Forest::Tree.new
p Forest::Rock.new
p Town::Cinema.new
我们正在创建属于森林和城镇的对象。 要访问模块中的对象,我们使用::运算符。
p Forest::Animal.new
p Town::Animal.new
将创建两个不同的动物对象。 Ruby 解释器可以在它们之间进行区分。 它通过模块名称来标识它们。
$ ./modules3.rb
#<Forest::Tree:0x97f35ec>
#<Forest::Rock:0x97f35b0>
#<Town::Cinema:0x97f3588>
#<Forest::Animal:0x97f3560>
#<Town::Animal:0x97f3538>
这是modules3.rb
程序的输出。
本节的最终代码示例将演示使用 Ruby 模块的多重继承。 在这种情况下,这些模块称为混合模块。
#!/usr/bin/ruby
module Device
def switch_on ; puts "on" end
def switch_off ; puts "off" end
end
module Volume
def volume_up ; puts "volume up" end
def vodule_down ; puts "volume down" end
end
module Pluggable
def plug_in ; puts "plug in" end
def plug_out ; puts "plug out" end
end
class CellPhone
include Device, Volume, Pluggable
def ring
puts "ringing"
end
end
cph = CellPhone.new
cph.switch_on
cph.volume_up
cph.ring
我们有三个模块和一个类。 模块代表一些功能。 可以打开和关闭设备。 许多对象可以共享此功能,包括电视,移动电话,计算机或冰箱。 我们没有为每个对象类创建这种被切换为开/关的功能,而是将其分离到一个模块中,如有必要,该模块可以包含在每个对象中。 这样,代码可以更好地组织和紧凑。
module Volume
def volume_up ; puts "volume up" end
def vodule_down ; puts "volume down" end
end
Volume
模块组织负责控制音量级别的方法。 如果设备需要这些方法,则仅将模块包含在其类中。
class CellPhone
include Device, Volume, Pluggable
def ring
puts "ringing"
end
end
手机使用include
方法添加所有三个模块。 模块的方法在CellPhone
类中混合。 并且可用于该类的实例。 CellPhone
类还具有特定于它的自己的ring
方法。
cph = CellPhone.new
cph.switch_on
cph.volume_up
cph.ring
创建一个CellPhone
对象,然后在该对象上调用三个方法。
$ ./mixins.rb
on
volume up
ringing
运行示例将给出此输出。
Ruby 异常
异常是表示偏离正常程序执行流程的对象。 引发,引发或引发异常。
在执行应用期间,许多事情可能出错。 磁盘可能已满,我们无法保存文件。 互联网连接可能断开,我们的应用尝试连接到站点。 所有这些都可能导致我们的应用崩溃。 为了防止这种情况的发生,我们应该预期并应对预期程序操作中的错误。 为此,我们可以使用异常处理。
异常是对象。 它们是内置Exception
类的后代。 异常对象携带有关异常的信息。 它的类型(异常的类名),可选的描述性字符串和可选的回溯信息。 程序可以是Exception
的子类,或更常见的是StandardError
的子类,以获取提供有关操作异常的其他信息的自定义Exception
对象。
#!/usr/bin/ruby
x = 35
y = 0
begin
z = x / y
puts z
rescue => e
puts e
p e
end
在上面的程序中,我们有意将数字除以零。 这会导致错误。
begin
z = x / y
puts z
可能失败的语句放置在begin
关键字之后。
rescue => e
puts e
p e
end
在rescue
关键字后面的代码中,我们处理了一个异常。 在这种情况下,我们将错误消息打印到控制台。 e
是发生错误时创建的异常对象。
$ ./zerodivision.rb
divided by 0
#<ZeroDivisionError: divided by 0>
在示例的输出中,我们看到了异常消息。 最后一行显示名为ZeroDivisionError
的异常对象。
程序员可以使用raise
关键字自己引发异常。
#!/usr/bin/ruby
age = 17
begin
if age < 18
raise "Person is a minor"
end
puts "Entry allowed"
rescue => e
puts e
p e
exit 1
end
18 岁以下的年轻人不允许进入俱乐部。 我们在 Ruby 脚本中模拟这种情况。
begin
if age < 18
raise "Person is a minor"
end
puts "Entry allowed"
如果此人是未成年人,则会引发异常情况。 如果raise
关键字没有特定的异常作为参数,则会引发RuntimeError
异常,将其消息设置为给定的字符串。 该代码未到达puts "Entry allowed"
行。 代码的执行被中断,并在救援块处继续执行。
rescue => e
puts e
p e
exit 1
end
在救援块中,我们输出错误消息和RuntimeError
对象的字符串表示形式。 我们还调用exit
方法来通知环境脚本执行错误结束。
$ ./raise_exception.rb
Person is a minor
#<RuntimeError: Person is a minor>
$ echo $?
1
未成年人未获准进入俱乐部。 bash $?
变量设置为脚本的退出错误。
Ruby 的ensure
子句创建一个始终执行的代码块,无论是否存在异常。
#!/usr/bin/ruby
begin
f = File.open("stones", "r")
while line = f.gets do
puts line
end
rescue => e
puts e
p e
ensure
f.close if f
end
在代码示例中,我们尝试打开并读取Stones
文件。 I/O 操作容易出错。 我们很容易会有异常。
ensure
f.close if f
end
在确保块中,我们关闭文件处理器。 我们检查处理器是否存在,因为它可能尚未创建。 分配的资源通常放置在确保块中。
如果需要,我们可以创建自己的自定义异常。 Ruby 中的自定义异常应继承自StandardError
类。
#!/usr/bin/ruby
class BigValueError < StandardError ; end
LIMIT = 333
x = 3_432_453
begin
if x > LIMIT
raise BigValueError, "Exceeded the maximum value"
end
puts "Script continues"
rescue => e
puts e
p e
exit 1
end
假设我们处于无法处理大量数字的情况。
class BigValueError < StandardError ; end
我们有一个BigValueError
类。 该类派生自内置的StandardError
类。
LIMIT = 333
超出此常数的数字在我们的程序中被视为big
。
if x > LIMIT
raise BigValueError, "Exceeded the maximum value"
end
如果该值大于限制,则抛出自定义异常。 我们给异常消息"Exceeded the maximum value"
。
$ ./custom_exception.rb
Exceeded the maximum value
#<BigValueError: Exceeded the maximum value>
运行程序。
在本章中,我们结束了关于 Ruby 语言的面向对象编程的讨论。
Ruby 正则表达式
在 Ruby 教程的这一部分中,我们将讨论 Ruby 中的正则表达式。
正则表达式用于文本搜索和更高级的文本操作。 正则表达式内置在grep
,sed
等工具中; 诸如 vi,emacs 之类的文本编辑器; Tcl,Perl,Python 等编程语言。 Ruby 也有对正则表达式的内置支持。
从另一个角度来看,正则表达式语法构成了用于匹配文本的特定于域的语言。
模式是一个正则表达式,用于定义我们正在搜索或操纵的文本。 它由文本文字和元字符组成。 该模式放置在两个定界符内。 在 Ruby 中,这些是//
字符。 它们通知正则表达式函数模式的开始和结束位置。
以下是部分元字符列表:
. |
匹配任何单个字符。 |
* |
与前面的元素匹配零次或多次。 |
[ ] |
括号表达。 与方括号内的字符匹配。 |
[^ ] |
匹配方括号中未包含的单个字符。 |
^ |
匹配字符串中的起始位置。 |
` | |
--- | --- |
. |
匹配任何单个字符。 |
* |
与前面的元素匹配零次或多次。 |
[ ] |
括号表达。 与方括号内的字符匹配。 |
[^ ] |
匹配方括号中未包含的单个字符。 |
^ |
匹配字符串中的起始位置。 |
匹配字符串中的结束位置。 | |
| |
备用运算符。 |
=~
运算符将正则表达式与字符串进行匹配,如果找到匹配项,则返回匹配项与字符串的偏移量,否则返回nil
。 Regexp
类用于开发正则表达式。 还有两种创建正则表达式的简便方法。 以下示例将显示它们。
#!/usr/bin/ruby
re = Regexp.new 'Jane'
p "Jane is hot".match re
p "Jane is hot" =~ /Jane/
p "Jane is hot".match %r{Jane}
在第一个示例中,我们展示了在字符串上应用正则表达式的三种方式。
re = Regexp.new 'Jane'
p "Jane is hot".match re
在以上两行中,我们创建一个Regexp
对象,其中包含一个简单的正则表达式文本。 使用match
方法,我们将此正则表达式对象应用于"Jane is hot"
句子。 我们检查句子中是否包含"Jane"
一词。
p "Jane is hot" =~ /Jane/
p "Jane is hot".match %r{Jane}
这两行是相同的。 两个正斜杠//和% r {}字符是更冗长的第一种方式的简写。 在本教程中,我们将使用正斜杠。 这是许多语言的事实上的标准。
$ ./regex.rb
#<MatchData "Jane">
0
#<MatchData "Jane">
在所有三种情况下,都有一个匹配项。 match
方法返回匹配的数据,如果不匹配,则返回nil
。 =~
运算符返回匹配文本的第一个字符,否则返回nil
。
点字符
点字符是正则表达式字符,可与任何单个字符匹配。 请注意,必须有一些字符。 它可能不会被省略。
#!/usr/bin/ruby
p "Seven".match /.even/
p "even".match /.even/
p "eleven".match /.even/
p "proven".match /.even/
在第一个示例中,我们将使用match
方法对字符串应用正则表达式。 match
方法成功返回匹配的数据,否则返回nil
。
p "Seven".match /.even/
"Seven"
是我们称为match
方法的字符串。 该方法的参数是模式。 /.even/
常规模式查找以任意字符开头,后跟"even"
字符的文本。
$ ./dot.rb
#<MatchData "Seven">
nil
#<MatchData "leven">
nil
从输出中,我们可以看到哪些字符串匹配,哪些不匹配。
就像我们上面说过的,如果有一个点字符,那么必须有一个任意字符。 不可省略。 如果我们要搜索可能会省略字符的文本怎么办? 换句话说,我们想要一个既有"Seven"
又有"even"
的模式。 为此,我们可以使用?
重复字符。 ?
重复字符表明前一个字符可能出现 0 次或 1 次。
#!/usr/bin/ruby
p "seven".match /.even/
p "even".match /.even/
p "even".match /.?even/
该脚本使用?
重复字符。
p "even".match /.even/
该行打印nil
,因为正则表达式期望在"even"
字符串之前有一个字符。
p "even".match /.?even/
在这里,我们对正则表达式进行了一些修改。 .?
代表无字符或一个任意字符。 这次有一场比赛。
$ ./dot2.rb
#<MatchData "seven">
nil
#<MatchData "even">
这是示例输出。
Ruby 正则表达式方法
在前两个示例中,我们使用match
方法处理正则表达式。 除了match
以外,其他方法也接受正则表达式作为参数。
#!/usr/bin/ruby
puts "motherboard" =~ /board/
puts "12, 911, 12, 111"[/\d{3}/]
puts "motherboard".gsub /board/, "land"
p "meet big deep nil need".scan /.[e][e]./
p "This is Sparta!".split(/\s/)
该示例显示了一些可用于正则表达式的方法。
puts "motherboard" =~ /board/
=~
是一个运算符,将右侧的正则表达式应用于左侧的字符串。
puts "12, 911, 12, 111"[/\d{3}/]
可以在字符串后的方括号之间放置正则表达式。 该行将打印第一个包含三位数的字符串。
puts "motherboard".gsub /board/, "land"
使用gsub
方法,我们将'board'字符串替换为'land'字符串。
p "meet big deep nil need".scan /.[e][e]./
scan
方法在字符串中查找匹配项。 它查找所有事件,而不仅仅是第一次。 该行将打印所有与模式匹配的字符串。
p "This is Sparta!".split(/\s/)
split
方法使用给定的正则表达式作为分隔符来拆分字符串。 \s
字符类型代表任何空白字符。
$ ./apply.rb
6
911
motherland
["meet", "deep", "need"]
["This", "is", "Sparta!"]
我们看到apply.rb
脚本的输出。
Ruby 特殊变量
使用正则表达式的某些方法会激活一些特殊变量。 它们包含最后一个匹配的字符串,最后一个匹配之前的字符串和最后一个匹配之后的字符串。 这些变量使程序员的工作更加轻松。
#!/usr/bin/ruby
puts "Her name is Jane" =~ /name/
p $`
p $&
p $'
该示例显示了三个特殊变量。
puts "Her name is Jane" =~ /name/
在这一行中,我们有一个简单的正则表达式匹配。 我们在"Her name is Jane"
句子中寻找一个"name"
字符串。 我们使用=~
运算符。 该运算符还设置了三个特殊变量。 该行返回数字 4,这是比赛开始的位置。
p $`
`$``特殊变量包含最后一个匹配项之前的文本。
p $&
$&
具有匹配的文本。
p $'
$'
变量包含最后一个匹配项之后的文本。
$ ./svars.rb
4
"Her "
"name"
" is Jane"
这是示例的输出。
锚点
锚点匹配给定文本内字符的位置。 我们介绍了三个锚定字符。 ^
字符与行的开头匹配。 $
字符与行尾匹配。 \b
字符与单词边界匹配。
#!/usr/bin/ruby
sen1 = "Everywhere I look I see Jane"
sen2 = "Jane is the best thing that happened to me"
p sen1.match /^Jane/
p sen2.match /^Jane/
p sen1.match /Jane$/
p sen2.match /Jane$/
在第一个示例中,我们使用^
和$
锚定字符。
sen1 = "Everywhere I look I see Jane"
sen2 = "Jane is the best thing that happened to me"
我们有两个句子。 "Jane"
一词位于第一个字母的开头和第二个字母的结尾。
p sen1.match /^Jane/
p sen2.match /^Jane/
在这里,我们查看单词"Jane"
是否位于两个句子的开头。
p sen1.match /Jane$/
p sen2.match /Jane$/
在这里,我们在句子的末尾查找文本的匹配项。
$ ./anchors.rb
nil
#<MatchData "Jane">
#<MatchData "Jane">
nil
这些就是结果。
常见的要求是仅包含整个单词的匹配项。 默认情况下,我们会计算所有匹配项,包括匹配较大或复合词的匹配项。 让我们看一个例子来澄清问题。
#!/usr/bin/ruby
text = "The cat also known as the domestic cat is a small,
usually furry, domesticated, carnivorous mammal."
p text.scan /cat/
p $`
p $&
p $'
我们有一句话。 在这句话中,我们寻找一只猫。 使用scan
,我们查找句子中的所有"cat"
字符串,而不仅仅是第一次出现。
text = "The cat also known as the domestic cat is a small,
usually furry, domesticated, carnivorous mammal."
问题在于文本内部有三个"cat"
字符串。 除了匹配提到哺乳动物的"cat"
以外,/cat/
还匹配"domesticated"
一词中的字母 8-10。 在这种情况下,这不是我们想要的。
$ ./boudaries.rb
["cat", "cat", "cat"]
"The cat also known as the domestic cat is a small, \nusually furry, domesti"
"cat"
"ed, carnivorous mammal."
在下一个示例中,将使用\b
定位符消除"domesticated"
上的最后一次匹配。
\b
字符用于为我们要查找的单词设置边界。
#!/usr/bin/ruby
text = "The cat also known as the domestic cat is a small,
usually furry, domesticated, carnivorous mammal."
p text.scan /\bcat\b/
p $`
p $&
p $'
通过包含\b
元字符来改进示例。
p text.scan /\bcat\b/
通过上面的正则表达式,我们将"cat"
字符串作为整个单词来查找。 我们不计算子词。
$ ./boudaries2.rb
["cat", "cat"]
"The cat also known as the domestic "
"cat"
" is a small, \nusually furry, domesticated, carnivorous mammal."
这次有两场比赛。 并且特殊变量可以正确显示最后一次匹配之前和之后的文本。
字符类
我们可以使用方括号将字符组合成字符类。 字符类与括号中指定的任何字符匹配。 /[ab]/
模式表示a
或b
,而/ab/
模式表示a
后跟b
。
#!/usr/bin/ruby
words = %w/ sit MIT fit fat lot pad /
pattern = /[fs]it/
words.each do |word|
if word.match pattern
puts "#{word} matches the pattern"
else
puts "#{word} does not match the pattern"
end
end
我们有六个六个三个字母的单词组成的数组。 我们对具有特定字符集的数组字符串应用正则表达式。
pattern = /[fs]it/
这就是模式。 该模式在数组中查找"fit"
和"sit"
字符串。 我们使用字符集中的"f"
或"s"
。
$ ./classes.rb
sit matches the pattern
MIT does not match the pattern
fit matches the pattern
fat does not match the pattern
lot does not match the pattern
pad does not match the pattern
有两场比赛。
在下一个示例中,我们将进一步探索字符类。
#!/usr/bin/ruby
p "car".match %r{[abc][a][rs]}
p "car".match /[a-r]+/
p "23af 433a 4ga".scan /\b[a-f0-9]+\b/
该示例包含三个带有字符类的正则表达式。
p "car".match %r{[abc][a][rs]}
此行中的正则表达式包含三个字符类。 每个都是一个字符。 [abc]
是a
,b
或c
。 [a]
只是一个。 第三个[rs]
是r
或s
。 与"car"
字符串匹配。
p "car".match /[a-r]+/
我们可以在字符类中使用连字符-字符。 连字符是表示以下字符的元字符:此处的a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q
或r
。 由于字符类仅适用于一个字符,因此我们也使用+
重复字符。 这表示字符集中的前一个字符可以重复一次或多次。 "car"
字符串满足这些条件。
p "23af 433a 4ga".scan /\b[a-f0-9]+\b/
在这一行中,我们有一个包含三个子字符串的字符串。 使用scan
方法,我们检查十六进制数。 我们有两个范围。 第一个[a-f]
代表从 a 到 f 的字符。 第二个字符[0-9]
代表数字 0 到 9。+
指定这些字符可以重复多次。 最后,\b
元字符创建边界,该边界仅接受仅包含这些字符的字符串。
$ ./classes2.rb
#<MatchData "car">
#<MatchData "car">
["23af", "433a"]
This is the example output.
如果字符类的第一个字符是插入号^
,则该类被反转。 它匹配除指定字符以外的任何字符。
#!/usr/bin/ruby
p "ABC".match /[^a-z]{3}/
p "abc".match /[^a-z]{3}/
在示例中,我们在字符类中使用插入符号。
p "ABC".match /[^a-z]{3}/
我们寻找一个包含 3 个字母的字符串。 这些字母可能不是a
到z
的字母。 因为所有三个字符均为大写字符,所以"ABC"
字符串与正则表达式匹配。
p "abc".match /[^a-z]{3}/
此"abc"
字符串不匹配。 所有三个字符都在搜索范围之外。
$ ./caret.rb
#<MatchData "ABC">
nil
这里有示例输出。
量词
标记或组后的量词指定允许该前一个元素出现的频率。
? - 0 or 1 match
* - 0 or more
+ - 1 or more
{n} - exactly n
{n,} - n or more
{,n} - n or less (??)
{n,m} - range n to m
上面是常见量词的列表。
#!/usr/bin/ruby
p "seven dig moon car lot fire".scan /\w{3}/
p "seven dig moon car lot fire".scan /\b\w{3}\b/
在该示例中,我们要选择具有三个字符的单词。 \w
字符是文字字符,\w{3}
表示之前文字字符的三倍。
p "seven dig moon car lot fire".scan /\w{3}/
第一行只是从每个字符串中剪切前三个字符。 这不是我们想要的。
p "seven dig moon car lot fire".scan /\b\w{3}\b/
这是一个改进的搜索。 我们将先前的模式放在\b
边界元字符之间。 现在,搜索将仅找到具有三个字符的单词。
$ ./nchars.rb
["sev", "dig", "moo", "car", "lot", "fir"]
["dig", "car", "lot"]
示例的输出。
{n,m}
是具有n
到m
个字符的字符串的重复结构。
#!/usr/bin/ruby
p "I dig moon lottery it fire".scan /\b\w{2,4}\b/
在上面的示例中,我们选择了具有两个,三个或四个字符的单词。 我们再次使用边界\b
元字符来选择整个单词。
$ ./rchars.rb
["dig", "moon", "it", "fire"]
该示例打印具有 2-4 个字符的单词数组。
在下一个示例中,我们将介绍?
元字符。 后面跟有?
的字符是可选的。 形式上,?
前面的字符可以出现一次或 0 次。
#!/usr/bin/ruby
p "color colour colors colours".scan /colou?rs/
p "color colour colors colours".scan /colou?rs?/
p "color colour colors colours".scan /\bcolor\b|\bcolors\b|\bcolour\b|\bcolours\b/
假设我们有一个文本,我们要在其中查找颜色词。 这个词有两个不同的拼写,英语为"colour"
和美国为"color"
。 我们希望找到这两种情况,而且我们也希望找到它们的复数形式。
p "color colour colors colours".scan /colou?rs/
颜色模式可以同时找到"color"
和"colour"
。 ?
元字符之前的u
字符是可选的。
p "color colour colors colours".scan /colou?rs?/
colou?rs?
模式串使u
和s
字符成为可选字符。 因此,我们找到了所有四种颜色组合。
p "color colour colors colours".scan /\bcolor\b|\bcolors\b|\bcolour\b|\bcolours\b/
可以使用轮换来写相同的请求。
$ ./qmark.rb
["colors", "colours"]
["color", "colour", "colors", "colours"]
["color", "colour", "colors", "colours"]
This is the example output.
在本节的最后一个示例中,我们将显示+
元字符。 它允许将前面的字符重复 1 次或更多次。
#!/usr/bin/ruby
nums = %w/ 234 1 23 53434 234532453464 23455636
324f 34532452343452 343 2324 24221 34$34232/
nums.each do |num|
m = num.match /[0-9]+/
if m.to_s.eql? num
puts num
end
end
在示例中,我们有一个数字数组。 数字可以包含一个或多个数字字符。
nums = %w/ 234 1 23 53434 234532453464 23455636
324f 34532452343452 343 2324 24221 34$34232/
这是一个字符串数组。 其中两个不是数字,因为它们包含非数字字符。 必须将它们排除在外。
nums.each do |num|
m = num.match /[0-9]+/
if m.to_s.eql? num
puts num
end
end
我们遍历数组并将正则表达式应用于每个字符串。 表达式为[0-9]+
,代表0..9
中的任何字符,重复 0 次或多次。 默认情况下,正则表达式也会查找子字符串。 在34$34232
中,引擎将 34 视为数字。 \b
边界在这里不起作用,因为我们没有具体的字符并且引擎不知道在哪里停止寻找。 这就是为什么我们在块中包含if
条件的原因。 仅当匹配项等于原始字符串时,该字符串才被视为数字。
$ ./numbers.rb
234
1
23
53434
234532453464
23455636
34532452343452
343
2324
24221
这些值是数字。
不区分大小写的搜索
我们可以执行不区分大小写的搜索。 正则表达式后面可以有一个选项。 它是单个字符,以某种方式修改了模式串。 如果是不区分大小写的搜索,我们将应用i
选项。
#!/usr/bin/ruby
p "Jane".match /Jane/
p "Jane".match /jane/
p "Jane".match /JANE/
p "Jane".match /jane/i
p "Jane".match /Jane/i
p "Jane".match /JANE/i
该示例显示了区分大小写和不区分大小写的搜索。
p "Jane".match /Jane/
p "Jane".match /jane/
p "Jane".match /JANE/
在这三行中,字符必须与模式完全匹配。 只有第一行给出了匹配项。
p "Jane".match /jane/i
p "Jane".match /Jane/i
p "Jane".match /JANE/i
在这里,我们使用第二个/
字符后面的i
选项。 我们进行不区分大小写的搜索。 所有三行都匹配。
$ ./icase.rb
#<MatchData "Jane">
nil
nil
#<MatchData "Jane">
#<MatchData "Jane">
#<MatchData "Jane">
This is the output of the example.
交替
下一个示例说明了交替运算符|
。 该运算符使您可以创建具有多种选择的正则表达式。
#!/usr/bin/ruby
names = %w/Jane Thomas Robert Lucy Beky
John Peter Andy/
pattern = /Jane|Beky|Robert/
names.each do |name|
if name =~ pattern
puts "#{name} is my friend"
else
puts "#{name} is not my friend"
end
end
名称数组中有 8 个名称。 我们将在该数组中寻找字符串的多个组合。
pattern = /Jane|Beky|Robert/
这是搜索模式。 它说,简,贝基和罗伯特是我的朋友。 如果找到其中任何一个,就找到了我的朋友。
$ ./alternation.rb
Jane is my friend
Thomas is not my friend
Robert is my friend
Lucy is not my friend
Beky is my friend
John is not my friend
Peter is not my friend
Andy is not my friend
在这里,我们看到了脚本的输出。
子模式
我们可以使用括号()
在样式内创建子模式。
#!/usr/bin/ruby
p "bookworm" =~ /book(worm)?$/
p "book" =~ /book(worm)?$/
p "worm" =~ /book(worm)?$/
p "bookstore" =~ /book(worm)?$/
我们有以下正则表达式模式:book(worm)?$
。 (worm)
是子模式。 只有两个字符串可以匹配:"book"
或"bookworm"
。 ?
字符位于子模式之后,这意味着子模式在最终模式中可能会出现 0、1 次。 $
字符在这里用于字符串的精确末尾匹配。 没有它,bookstore
和bookmania
这样的词也将匹配。
#!/usr/bin/ruby
p "book" =~ /book(shelf|worm)?$/
p "bookshelf" =~ /book(shelf|worm)?$/
p "bookworm" =~ /book(shelf|worm)?$/
p "bookstore" =~ /book(shelf|worm)?$/
子模式经常与交替组合在一起以创建多个单词组合。 例如,book(shelf|worm)
匹配"bookshelf"
和"bookworm"
,book(shelf|worm)?
匹配"bookshelf"
,"bookworm"
和"book"
。
$ ./subpatterns2.rb
0
0
0
nil
最后一个子模式不匹配。 请记住,0 并不意味着没有匹配。 对于=~
运算符,它是匹配字符串的第一个字符的索引。
电子邮件示例
在最后一个示例中,我们创建一个用于检查电子邮件地址的正则表达式模式。
#!/usr/bin/ruby
emails = %w/ luke@gmail.com andy@yahoo.com 23214sdj^as
f3444@gmail.com /
pattern = /^[a-zA-Z0-9._-]+@[a-zA-Z0-9-]+\.[a-zA-Z.]{2,5}$/
emails.each do |email|
if email.match pattern
puts "#{email} matches"
else
puts "#{email} does not match"
end
end
请注意,此示例仅提供一种解决方案。 它不一定是最好的。
emails = %w/ luke@gmail.com andy@yahoocom 23214sdj^as
f3444@gmail.com /
这是一系列电子邮件。 其中只有两个有效。
pattern = /^[a-zA-Z0-9._-]+@[a-zA-Z0-9-]+\.[a-zA-Z.]{2,5}$/
这就是模式。 这里的第一个^
和最后一个$
字符可得到精确的模式匹配。 模式前后不允许有字符。 电子邮件分为五个部分。
第一部分是本地部分。 这通常是公司,个人或昵称的名称。 [a-zA-Z0-9._-]+
列出了我们可以在本地部分使用的所有可能字符。 它们可以使用一次或多次。 第二部分是文字@
字符。 第三部分是领域部分。 通常是电子邮件提供商的域名:例如 yahoo 或 gmail。 字符集[a-zA-Z0-9-]+
指定可以在域名中使用的所有字符。 +
量词使
使用一个或多个这些字符。 第四部分是点字符。 它前面带有转义字符\
。 这是因为点字符是一个元字符,并且具有特殊含义。 通过转义,我们得到一个文字点。 最后一部分是顶级域。 模式如下:[a-zA-Z.]{2,5}
顶级域可以包含 2 到 5 个字符,例如sk, net, info, travel
。 还有一个点字符。 这是因为某些顶级域名(例如co.uk
)有两个部分。
$ ./email.rb
luke@gmail.com matches
andy@yahoocom does not match
23214sdj^as does not match
f3444@gmail.com matches
正则表达式将两个字符串标记为有效的电子邮件地址。
在本章中,我们介绍了 Ruby 中的正则表达式。
Ruby 输入&输出
在 Ruby 教程的这一部分中,我们将讨论 Ruby 中的输入&输出操作。 输入是程序从键盘,文件或其他程序读取的任何数据。 输出是程序产生的数据。 输出可能会转到屏幕,文件或其他程序。
输入&输出是一个大话题。 我们提出一些示例,以使您对该主题有一个总体了解。 Ruby 中的几个类具有执行输入&输出操作的方法。 例如Kernel
,IO
,Dir
或File
。
Ruby 输出到控制台
Ruby 有几种方法可以在控制台上打印输出。 这些方法是Kernel
模块的一部分。 Kernel
的方法可用于 Ruby 中的所有对象。
#!/usr/bin/ruby
print "Apple "
print "Apple\n"
puts "Orange"
puts "Orange"
print
和puts
方法在控制台上产生文本输出。 两者之间的区别在于后者添加了新的换行符。
print "Apple "
print "Apple\n"
打印方法将两个连续的"Apple"
字符串打印到终端。 如果要创建新行,则必须显式包含换行符。 换行符为\n
。 在后台,print
方法实际上调用了要打印对象的to_s
方法。
puts "Orange"
puts "Orange"
puts
方法将两个字符串打印到控制台。 每个人都有自己的路由。 该方法自动包括换行符。
$ ./printing.rb
Apple Apple
Orange
Orange
这是printing.rb
脚本文件的输出。
根据 Ruby 文档,print
方法与$stdout.print
等效。 $stdout
是保存标准输出流的全局变量。
#!/usr/bin/ruby
$stdout.print "Ruby language\n"
$stdout.puts "Python language"
我们使用$stdout
变量打印两行。
Ruby 还有另外三种打印输出的方法。
#!/usr/bin/ruby
p "Lemon"
p "Lemon"
printf "There are %d apples\n", 3
putc 'K'
putc 0xA
在示例中,我们介绍了p
,printf
和putc
方法。
p "Lemon"
p
在打印对象时调用inspect
方法。 该方法对于调试很有用。
printf "There are %d apples\n", 3
printf
方法从 C 编程语言中是众所周知的。 它启用字符串格式。
putc 'K'
putc 0xA
putc
方法将一个字符打印到控制台。 第二行打印换行符。 0xA
是换行符的十六进制代码。
$ ./printing3.rb
"Lemon"
"Lemon"
There are 3 apples
K
这是printing3.rb
程序的输出。
使用内核方法将数据打印到控制台是一个快捷方式:一种打印数据的简便方法。 以下示例显示了一种将数据打印到终端的更正式的方法。
ios = IO.new STDOUT.fileno
ios.write "ZetCode\n"
ios.close
在示例中,我们打开一个标准输出流,并将一个字符串写入其中。
ios = IO.new STDOUT.fileno
new
方法返回一个我们可以向其写入数据的流。 该方法采用数字文件描述符。 STDOUT.fileno
为我们提供了标准输出流的文件描述符。 我们也可以简单地写 2。
ios.write "ZetCode\n"
我们向打开的流中写入一个字符串。
ios.close
输入流已关闭。
在 Unix 系统上,标准终端输出连接到名为/dev/tty
的特殊文件。 通过打开并写入,我们将写入控制台。
#!/usr/bin/ruby
fd = IO.sysopen "/dev/tty", "w"
ios = IO.new(fd, "w")
ios.puts "ZetCode"
ios.close
一个小的例子,我们写入/dev/tty
文件。 这仅适用于 Unix。
fd = IO.sysopen "/dev/tty", "w"
sysopen
方法打开给定路径,并返回基础文件描述符号。
ios = IO.new(fd, "w")
文件描述符号用于打开流。
ios.puts "ZetCode"
ios.close
我们向流中写入一个字符串并将其关闭。
Ruby 从控制台读取输入
在本节中,我们将创建一些代码示例,这些示例将处理从控制台读取的内容。
$stdin
是一个全局变量,其中包含标准输入的流。 它可用于从控制台读取输入。
#!/usr/bin/ruby
inp = $stdin.read
puts inp
在上面的代码中,我们使用read
方法从控制台读取输入。
inp = $stdin.read
read
方法从标准输入读取数据,直到到达文件末尾。 通过在 Unix 上按Ctrl + D
以及在 Windows 上按Ctrl + Z
可以产生 EOF。
$ ./reading.rb
Ruby language
Ruby language
当我们启动不带参数的程序时,脚本将从用户读取数据。 读取直到我们按 Ctrl + D
或 Ctrl + Z
为止。
$ echo "ZetCode" | ./reading.rb
ZetCode
$ ./input.rb < stones
Garnet
Topaz
Opal
Amethyst
Ruby
Jasper
Pyrite
Malachite
Quartz
如果我们进行一些重定向,脚本可以从另一个程序或文件中读取数据。
从控制台读取数据的常用方法是使用gets
方法。
#!/usr/bin/ruby
print "Enter your name: "
name = gets
puts "Hello #{name}"
我们使用gets
方法从用户读取一行。
name = gets
gets
方法从标准输入读取一行。 数据被分配给名称变量。
puts "Hello #{name}"
我们已读取的数据将打印到控制台。 我们使用插值将变量包括在字符串中。
$ ./readline.rb
Enter your name: Jan
Hello Jan
样本输出。
在以下两个脚本中,我们将讨论chomp
方法。 这是一个字符串方法,可从字符串末尾删除空格。 在执行输入操作时很有用。 方法名称和用法来自 Perl 语言。
#!/usr/bin/ruby
print "Enter a string: "
inp = gets
puts "The string has #{inp.size} characters"
我们从用户那里读取一个字符串,然后计算输入字符串的长度。
$ ./nochomp.rb
Enter a string: Ruby
The string has 5 characters
消息说该字符串有 5 个字符。 这是因为它也计入换行符。
为了获得正确的答案,我们需要删除换行符。 这是chomp
方法的工作。
#!/usr/bin/ruby
print "Enter a string: "
inp = gets.chomp
puts "The string has #{inp.size} characters"
这次我们使用chomp
方法剪切换行符。
$ ./chomp.rb
Enter a string: Ruby
The string has 4 characters
Ruby 字符串确实有 4 个字符。
Ruby 文件
从正式的 Ruby 文档中,我们了解到IO
类是 Ruby 中所有输入和输出的基础。 File
类是IO
类的唯一子类。 这两个类别紧密相关。
#!/usr/bin/ruby
f = File.open('output.txt', 'w')
f.puts "The Ruby tutorial"
f.close
在第一个示例中,我们打开一个文件并向其中写入一些数据。
f = File.open('output.txt', 'w')
我们以写模式打开文件"output.txt"
。 open
方法返回一个 io 流。
f.puts "The Ruby tutorial"
我们使用上面打开的流来写入一些数据。 puts
方法也可以用于将数据写入文件。
f.close
最后,流关闭。
$ ./simplewrite.rb
$ cat output.txt
The Ruby tutorial
我们执行脚本并显示output.txt
文件的内容。
我们将有一个类似的示例,该示例将展示其他有效的方法。
#!/usr/bin/ruby
File.open('langs', 'w') do |f|
f.puts "Ruby"
f.write "Java\n"
f << "Python\n"
end
如果open
方法之后有一个块,则 Ruby 将打开的流传递到该块。 在该块的末尾,文件将自动关闭。
f.puts "Ruby"
f.write "Java\n"
f << "Python\n"
我们使用三种不同的方法写入文件。
$ ./simplewrite2.rb
$ cat langs
Ruby
Java
Python
我们执行脚本并检查langs
文件的内容。
在第二个示例中,我们显示File
类的一些方法。
#!/usr/bin/ruby
puts File.exists? 'tempfile'
f = File.new 'tempfile', 'w'
puts File.mtime 'tempfile'
puts f.size
File.rename 'tempfile', 'tempfile2'
f.close
该示例创建一个名为tempfile
的新文件,并调用一些方法。
puts File.exists? 'tempfile'
exists?
方法检查具有给定名称的文件是否已经存在。 该行返回false
,因为我们尚未创建文件。
f = File.new 'tempfile', 'w'
文件已创建。
puts File.mtime 'tempfile'
mtime
方法为我们提供了文件的最后修改时间。
puts f.size
我们确定文件大小。 由于尚未写入文件,因此该方法返回 0。
File.rename 'tempfile', 'tempfile2'
最后,我们使用rename
方法重命名文件。
$ ./testfile.rb
false
2011-11-05 16:19:36 +0100
0
这是一个示例输出。
接下来,我们将从磁盘上的文件中读取数据。
#!/usr/bin/ruby
f = File.open("stones")
while line = f.gets do
puts line
end
f.close
这是一个简单的脚本,它将打开一个名为stone
的文件,并将其内容逐行打印到终端。
f = File.open("stones")
我们打开一个stones
文件。 默认模式是读取模式。 stones
文件包含九个有价值的宝石名称,每个名称都位于单独的行上。
while line = f.gets do
puts line
end
gets
方法从 I/O 流中读取一行。 当我们到达文件末尾时,while
块结束。
$ ./readlines2.rb
Garnet
Topaz
Opal
Amethyst
Ruby
Jasper
Pyrite
Malachite
Quartz
这是示例的输出。
下一个示例将从文件中读取数据。
#!/usr/bin/ruby
fname = 'alllines.rb'
File.readlines(fname).each do |line|
puts line
end
该脚本显示了读取文件内容的另一种方法。 该代码示例将在终端上打印其自己的代码。
File.readlines(fname).each do |line|
puts line
end
readlines
从指定文件读取所有行,并以数组形式返回它们。 我们使用each
方法遍历数组并将行打印到终端。
$ ./alllines.rb
#!/usr/bin/ruby
fname = 'alllines.rb'
File.readlines(fname).each do |line|
puts line
end
示例的输出。
Ruby 目录
在本节中,我们使用目录。 我们有一个Dir
类,可以在 Ruby 中使用目录。
#!/usr/bin/ruby
Dir.mkdir "tmp"
puts Dir.exists? "tmp"
puts Dir.pwd
Dir.chdir "tmp"
puts Dir.pwd
Dir.chdir '..'
puts Dir.pwd
Dir.rmdir "tmp"
puts Dir.exists? "tmp"
在脚本中,我们使用Dir
类的四种方法。
Dir.mkdir "tmp"
mkdir
方法创建一个名为tmp
的新目录。
puts Dir.exists? "tmp"
使用exists?
方法,我们检查文件系统中是否存在具有给定名称的目录。
puts Dir.pwd
pwd
方法打印当前工作目录。 这是我们启动脚本的目录。
Dir.chdir '..'
chdir
方法更改到另一个目录。 ..
目录是当前工作目录的父目录。
Dir.rmdir "tmp"
puts Dir.exists? "tmp"
最后,我们使用rmdir
方法删除目录。 这次exists?
方法返回false
。
$ ./dirs.rb
true
/home/vronskij/programming/ruby/io
/home/vronskij/programming/ruby/io/tmp
/home/vronskij/programming/ruby/io
false
这是示例的输出。
在第二个示例中,我们检索目录的所有条目,包括其文件和子目录。
#!/usr/bin/ruby
fls = Dir.entries '.'
puts fls.inspect
entries
方法返回给定目录的所有条目。
fls = Dir.entries '.'
puts fls.inspect
我们得到当前目录的文件和目录数组。 .
字符在此上下文中表示当前工作目录。 inspect
方法为我们提供了更可读的数组表示形式。
$ ./allfiles.rb
["putc.rb", "simplewrite.rb", "readlines2.rb", "fileexists.rb~" ...
在输出中,我们可以看到文件和目录的数组。
第三个示例使用主目录。 计算机中的每个用户都有一个分配给他的唯一目录。 它称为主目录。 在这里,他可以放置文件并创建自己的目录层次结构。
#!/usr/bin/ruby
puts Dir.home
puts Dir.home 'root'
该脚本打印两个主目录。
puts Dir.home
如果未指定用户名,则返回当前用户的主目录。 当前用户是脚本文件的所有者。 有人启动了脚本。
puts Dir.home 'root'
在这里,我们打印特定用户的主目录:在本例中为超级用户。
$ ./homedir.rb
/home/vronskij
/root
这是一个示例输出。
Ruby 执行外部程序
Ruby 有几种执行外部程序的方法。 我们将处理其中一些问题。 在我们的示例中,我们将使用众所周知的 Linux 命令。 Windows 或 Mac 的阅读器可以使用特定于其系统的命令。
#!/usr/bin/ruby
data = system 'ls'
puts data
我们调用ls
命令列出目录内容。
data = system 'ls'
system
命令在子 Shell 中执行外部程序。 该方法属于Kernel
Ruby 模块。
$ ./system.rb
allfiles.rb characters.rb fileexists.rb homedir.rb~ ...
This is a sample output.
我们展示了另外两种在 Ruby 中运行外部程序的方式。
#!/usr/bin/ruby
out = `pwd`
puts out
out = %x[uptime]
puts out
out = %x[ls | grep 'readline']
puts out
要运行外部程序,我们可以使用反引号`
或%x[]
字符。
out = `pwd`
在这里,我们使用反引号执行pwd
命令。 该命令返回当前工作目录。
out = %x[uptime]
在这里,我们获得uptime
命令的输出,该命令指示系统运行了多长时间。
out = %x[ls | grep 'readline']
我们也可以结合使用命令。
$ ./system2.rb
/home/vronskij/programming/ruby/io
22:50:50 up 5:32, 1 user, load average: 0.46, 0.44, 0.45
readline.rb
readline.rb~
readlines2.rb
readlines2.rb~
This is a sample output.
我们可以使用open
方法执行命令。 该方法属于Kernel
模块。 它创建一个连接到给定流,文件或子流程的 IO 对象。 如果要连接到子进程,请使用管道字符|
来启动open
的路径。
#!/usr/bin/ruby
f = open("|ls -l |head -3")
out = f.read
puts out
f.close
puts $?.success?
在示例中,我们打印ls -l | head -3
命令的结果。 这两个命令的组合返回ls -l
命令的前三行。 我们还检查子进程的状态。
f = open("|ls -l |head -3")
我们连接到由这两个命令创建的子流程。
out = f.read
puts out
我们从子流程读取并打印数据。
f.close
我们关闭文件处理器。
puts $?.success?
$?
是一个特殊的 Ruby 变量,它设置为最后执行的子进程的状态。 如果子进程以 OK 结束,则success?
方法返回true
。
$ ./system3.rb
total 148
-rwxr-xr-x 1 vronskij vronskij 57 2011-10-30 23:33 allfiles.rb
-rwxr-xr-x 1 vronskij vronskij 58 2011-10-30 23:33 allfiles.rb~
true
This is a sample output.
Ruby 重定向标准输出
Ruby 具有用于标准输入,标准输出和标准错误输出的预定义全局变量。 $stdout
是标准输出的变量的名称。
#!/usr/bin/ruby
$stdout = File.open "output.log", "a"
puts "Ruby"
puts "Java"
$stdout.close
$stdout = STDOUT
puts "Python"
在上面的示例中,我们将标准输出重定向到output.log
文件。
$stdout = File.open "output.log", "a"
此行创建一个新的标准输出。 现在,标准输出将流到ouput.log
文件。 该文件以附加模式打开。 如果该文件尚不存在,则会创建它。 否则,它将被打开并将数据写入文件的末尾。
puts "Ruby"
puts "Java"
我们打印两个字符串。 字符串将不会像往常一样显示在终端中。 相反,它们将被附加到output.log
文件中。
$stdout.close
处理器已关闭。
$stdout = STDOUT
puts "Python"
我们使用预定义的标准常量STDOUT
重新创建正常的标准输出。 "Python"
字符串被打印到控制台。
在 Ruby 教程的这一部分中,我们使用 Ruby 进行输入和输出操作。