你应该学会使用的5个ruby方法
今天看到了这篇文章--Five Ruby Methods You Should Be Using,感觉收获颇丰,先简单翻译一下先。
作者写这篇文章的契机是在Exercism上看到了很多ruby代码可以用更好的方式去重构,因此他分享了一些冷门的但是非常有用的ruby方法。
Object#tap
你是否曾发现在某个对象上调用方法时返回值不是你所预期?你想返回这个对象,但是返回的时候又想对这个对象进行一些修改。比方说,你想给hash对象增加1个key value,这时候你需要调用Hash.[]方法,但是你想返回的是整个hash对象,而不是具体的某个value值,因此你需要显示的返回该对象。
def update_params(params)
params[:foo] = 'bar'
params
end
最后一行的那个params显得有些多余了。
我们可以用Object#tap
方法来优化这个方案。
tap方法用起来非常简单,直接在某个对象上调用tap方法,然后就可以在代码块里yielded这个对象,最后这个对象本身会被返回。下面的代码演示了如何使用tap方法来重构刚才的实现。
def update_params(params)
params.tap {|p| p[:foo] = 'bar' }
end
有很多地方都可以使用到Object#tap
方法,一般的规律是对那些在对象上调用,希望返回对象,但是却没返回该对象本身的方法都适用。
Array#bsearch
我不清楚你的情况,但我经常在数组里去查找数据。ruby的enumerable模块提供了很多简单好用的方法select, reject, find
。不过当数据源很庞大的时候,我开始对这些查找的性能表示忧桑。
如果你正在使用ActiveRecord和非NO SQL的数据库,查询的算法复杂度是经过优化了的。但是有时候你需要从数据库里把所有的数据拉出来进行处理,比方说如果你加密了数据库,那就不能好好的写sql做查询了。
这时候我会冥思苦想以找到一个最小的算法复杂度来筛选数据。如果你不了解算法复杂度,也就是这个O,请阅读Big-O Notation Explained By A Self-Taught Programmer或[Big-O Complexity Cheat Sheet](http://bigocheatsheet.com/)。
一般来说,算法复杂度越低,程序运行的速度就越快。 O(1), O(log n), O(n), O(n log(n)), O(n^2), O(2^n), O(n!)
,在这个例子里,越往右算法复杂度是越高的。所以我们要让我们的算法接近左边的复杂度。
当我们搜索数组的时候,一般第一个想到的方法便是Enumerable#find
,也就是select方法。不过这个方法会搜索整个数组直到找到预期的结果。如果要找的元素在数组的开始部分,那么搜索的效率倒不会太低,但如果是在数据的末尾,那么搜索时间将是很可观的。find方法的算法复杂度是O(n)。
更好的办法是使用(Array#bsearch)[http://www.ruby-doc.org/core-2.1.5/Array.html#method-i-bsearch]方法。该方法的算法复杂度是O(log n)。你可以查看Building A Binary Search这篇文章来该算法的原理。
下面的代码显示了搜索50000000个数字时不同算法之间的性能差异。
require 'benchmark'
data = (0..50_000_000)
Benchmark.bm do |x|
x.report(:find) { data.find {|number| number > 40_000_000 } }
x.report(:bsearch) { data.bsearch {|number| number > 40_000_000 } }
end
user system total real
find 3.020000 0.010000 3.030000 (3.028417)
bsearch 0.000000 0.000000 0.000000 (0.000006)
如你所见,bsearch
要快的多。不过要注意的是bsearch要求搜索的数组是排序过的。尽管这个限制bsearch的使用场景,bsearch在显示生活中确实是有用武之地的。比如通过created_at
字段来查找从数据库中取出的数据。
Enumerable#flat_map
考虑这种情况,你有个blog应用,你希望找到上个月有过评论的所有作者,你可以会这样做:
module CommentFinder
def self.find_for_users(user_ids)
users = User.where(id: user_ids)
user.posts.map do |post|
post.comments.map |comment|
comment.author.username
end
end
end
end
得到的结果看起来会是这样的
[[['Ben', 'Sam', 'David'], ['Keith']], [[], [nil]], [['Chris'], []]]
不过你想得到的是所有作者,这时候你大概会使用flatten
方法。
module CommentFinder
def self.find_for_users(user_ids)
users = User.where(id: user_ids)
user.posts.map { |post|
post.comments.map { |comment|
comment.author.username
}.flatten
}.flatten
end
end
另一个选择是使用flat_map
方法。
module CommentFinder
def self.find_for_users(user_ids)
users = User.where(id: user_ids)
user.posts.flat_map { |post|
post.comments.flat_map { |comment|
comment.author.username
}
}
end
end
这跟使用flatten方法没什么太大的不同,不过看起来会优雅一点,毕竟不需要反复调用flatten了。
Array.new with a Block
想当年我在一个技术训练营,我们的导师Jeff Casimir同志(Turing School的创始人)让我们在一小时内写个Battleship游戏。这是极好的进行面向对象编程的练习,我们需要Rules,Players, Games和Boards类。
创建代表Board的数据结构是一件非常有意思的事情。经过几次迭代我发现下面的方法是初始化8x8格子的最好方式:
class Board
def board
@board ||= Array.new(8) { Array.new(8) { '0' } }
end
end
上面的代码是什么意思?当我们调用Array.new
并传入了参数length,1个长度为length的数组将会被创建。
Array.new(8)
#=> [nil, nil, nil, nil, nil, nil, nil, nil]
当你传入一个block,这时候block的返回值会被当成是数组的每个元素。
Array.new(8) { 'O' }
#=> ['O', 'O', 'O', 'O', 'O', 'O', 'O', 'O']
因此,当你向block传入1个具有8个元素的数组时,你会得到8x8个元素的嵌套数组了。
用Array#new加block的方式可以创建很多有趣和任意嵌套层级的数组。
<=>
这个方法就很常见了。简单来说这方法是判断左值和右值的关系的。如果左值大于右值返回1,相等返回0,否则返回-1。
实际上Enumerable#sort, Enumerable#max
方法都是基于<=>的。另外如果你定义了<=>,然后再include Comparable,你将免费得到<=, <, >=, >以及between方法。
这是作者的在现实生活中所用到的例子:
def fix_minutes
until (0...60).member? minutes
@hours -= 60 <=> minutes
@minutes += 60 * (60 <=> minutes)
end
@hours %= 24
self
end
这个方法不是很好理解,大概的意思就是如果minutes超过60的话,小时数+1,等于60小时数不变,否则-1。
讨论
会的方法越多写出来的代码可能会更有表现力,边写代码边改进,另外多读rubydoc。
你应该学会使用的5个ruby方法的更多相关文章
- Ruby 方法
Ruby 方法 Ruby 方法与其他编程语言中的函数类似.Ruby 方法用于捆绑一个或多个重复的语句到一个单元中. 方法名应以小写字母开头.如果您以大写字母作为方法名的开头,Ruby 可能会把它当作常 ...
- Ruby方法
Ruby 方法 Ruby 方法与其他编程语言中的函数类似.Ruby 方法用于捆绑一个或多个重复的语句到一个单元中. 方法名应以小写字母开头.如果您以大写字母作为方法名的开头,Ruby 可能会把它当作常 ...
- Python学习笔记4-如何快速的学会一个Python的模块、方法、关键字
想要快速的学会一个Python的模块和方法,两个函数必须要知道,那就是dir()和help() dir():能够快速的以集合的型式列出该模块下的所有内容(类.常量.方法)例: #--encoding: ...
- 雷林鹏分享:Ruby 方法
Ruby 方法 Ruby 方法与其他编程语言中的函数类似.Ruby 方法用于捆绑一个或多个重复的语句到一个单元中. 方法名应以小写字母开头.如果您以大写字母作为方法名的开头,Ruby 可能会把它当作常 ...
- ruby 方法重载
class MyClass def sayHello return "hello from MyClass" end def sayGoodbye return "Goo ...
- Ruby方法参数默认值的一个小技巧在Rails中的应用
我们需要生成一个gravatar格式的html.image标示,于是写了如下方法: def gravatar_for(user) gravatar_id = Digest::MD5::hexdiges ...
- ruby方法名之命名方式
上一篇博文 ruby传参之引用类型 里边定义了一个方法名 modify_my_object!,这个方法名是以 ! 结尾,在 ruby 的对象里边是用于表达修改本身的意思. 比如 String#gsub ...
- cocoapods:安装/更新Ruby环境教程
简介 有时候在安装cocoapods时会产生如下错误 ERROR: Error installing cocoapods: activesupport requires Ruby version &g ...
- Ruby基本语法规则
1.Ruby常用数据类型 Numbers, Strings, Booleans my_num = 25 my_boollean = true (or false) my_string = " ...
随机推荐
- asp.net正则表达式类的定义
using System; using System.Collections; using System.Reflection; using System.Reflection.Emit; using ...
- PAT 1065 1066 1067 1068
pat 1065 A+B and C 主要是注意一下加法溢出的情况,不要试图使用double,因为它的精度是15~16 ...
- 【树莓派】树莓派新版系统SSH连接被拒绝问题处理
安装好新的版本树莓派(NOOBS_v2_4_0.zip)之后,直连显示器并接上网线,可以看到已经获取到动态IP地址了. 但是,此时使用xshell远程连接时,却一直连接不成功: [d:\~]$ Con ...
- bootstrap——强大的网页设计元素模板
本文介绍一个网页设计工具——bootstrap,它包含了很多华丽的按钮及排版,我们可以在网页设计中直接使用它,尤其是加入我们只是想简单的使用一下的话,将会是一个不错的选择,下面是几张examples, ...
- Cocon90.Db调用方法
Cocon90.DB 使用说明 开源库:https://github.com/Cocon90/Cocon90.Db Sqlite位置:https://www.nuget.org/packages/Co ...
- JAVA i++于++i的区别
大家看一下下面一个程序: 一.问题说明 Test.java public class Test { public static void main(String[] args) { int i = 1 ...
- Java的PriorityQueue
转载请注明原文地址:http://www.cnblogs.com/ygj0930/p/6538654.html 优先队列实质上就是数据结构中的最小堆,而堆从概念图来看类似于一棵二叉树,从具体实现来说 ...
- C++ 函数返回局部变量的std::move()的适用场景(转)
作者:神奇先生链接:https://www.zhihu.com/question/57048704/answer/151446405来源:知乎著作权归作者所有.商业转载请联系作者获得授权,非商业转载请 ...
- GoldenGate 12c 新特性 Credential Store and USERIDALIAS
GoldenGate 12C的Credential Store and USERIDALIAS新特性有点类似存储钱夹,提高了配置的易用性和安全性. --生成credentialstore文件 GGSC ...
- 〖Linux〗转换Socks Proxy为Http Proxy
使用工具,privoxy,官网: http://www.privoxy.org/ socks5 proxy设定方法: autossh -CgNfD 0.0.0.0:1080 vps-lxb sock ...