楼主: jieforest

Ruby开发技巧

[复制链接]
论坛徽章:
277
马上加薪
日期:2014-02-19 11:55:14马上有对象
日期:2014-02-19 11:55:14马上有钱
日期:2014-02-19 11:55:14马上有房
日期:2014-02-19 11:55:14马上有车
日期:2014-02-19 11:55:14马上有车
日期:2014-02-18 16:41:112014年新春福章
日期:2014-02-18 16:41:11版主9段
日期:2012-11-25 02:21:03ITPUB年度最佳版主
日期:2014-02-19 10:05:27现任管理团队成员
日期:2011-05-07 01:45:08
11#
 楼主| 发表于 2013-11-3 00:51 | 只看该作者
Arrays will be automatically destructured on the right hand side of an assignment, this works particularly nicely when you have a method returning an array.
  1. family, port, host, address = socket.peeraddr
复制代码
I’m also partial to this trick for grabbing just the first element of an array.
  1. family, = socket.peeraddr
复制代码

使用道具 举报

回复
论坛徽章:
277
马上加薪
日期:2014-02-19 11:55:14马上有对象
日期:2014-02-19 11:55:14马上有钱
日期:2014-02-19 11:55:14马上有房
日期:2014-02-19 11:55:14马上有车
日期:2014-02-19 11:55:14马上有车
日期:2014-02-18 16:41:112014年新春福章
日期:2014-02-18 16:41:11版主9段
日期:2012-11-25 02:21:03ITPUB年度最佳版主
日期:2014-02-19 10:05:27现任管理团队成员
日期:2011-05-07 01:45:08
12#
 楼主| 发表于 2013-11-3 00:51 | 只看该作者
However, the same result can be achieved more clearly with #first.
  1. family = socket.peeraddr.first
复制代码
You can take advantage of this for Hashes using the #values_at method, which returns an array.
  1. first, last = params.values_at(:first_name, :last_name)
复制代码

使用道具 举报

回复
论坛徽章:
277
马上加薪
日期:2014-02-19 11:55:14马上有对象
日期:2014-02-19 11:55:14马上有钱
日期:2014-02-19 11:55:14马上有房
日期:2014-02-19 11:55:14马上有车
日期:2014-02-19 11:55:14马上有车
日期:2014-02-18 16:41:112014年新春福章
日期:2014-02-18 16:41:11版主9段
日期:2012-11-25 02:21:03ITPUB年度最佳版主
日期:2014-02-19 10:05:27现任管理团队成员
日期:2011-05-07 01:45:08
13#
 楼主| 发表于 2013-11-3 00:52 | 只看该作者
This implicit destructuring also happens in block arguments.
  1. names = ["Arthur", "Ford", "Trillian"]
  2. ids = [42, 43, 44]
  3. id_names = ids.zip(names)   #=> [[42, "Arthur"], [43, "Ford"], [44, "Trillian"]]
  4. id_names.each do |id, name|
  5.   puts "user #{id} is #{name}"
  6. end
复制代码
Even cooler is you can force further destructuring with parentheses.
  1. id_names = [[42, ["Arthur", "Dent"]], [43, ["Ford", "Prefect"]], [44, ["Tricia", "McMillan"]]]
  2. id_names.each do |id, (first_name, last_name)|
  3.   puts "#{id}\t#{last_name}, #{first_name[0]}."
  4. end
复制代码

使用道具 举报

回复
论坛徽章:
277
马上加薪
日期:2014-02-19 11:55:14马上有对象
日期:2014-02-19 11:55:14马上有钱
日期:2014-02-19 11:55:14马上有房
日期:2014-02-19 11:55:14马上有车
日期:2014-02-19 11:55:14马上有车
日期:2014-02-18 16:41:112014年新春福章
日期:2014-02-18 16:41:11版主9段
日期:2012-11-25 02:21:03ITPUB年度最佳版主
日期:2014-02-19 10:05:27现任管理团队成员
日期:2011-05-07 01:45:08
14#
 楼主| 发表于 2013-11-3 00:52 | 只看该作者
and this works in method parameters!
  1. def euclidean_distance((ax, ay), (bx, by))
  2.   Math.sqrt((ax - bx)**2 + (ay - by)**2)
  3. end

  4. euclidean_distance([1, 5], [4, 2])   #=> 4.242640687119285
复制代码
Ruby has two methods you can implement for your own classes so that you can take advantage of this. The first to allow an object to be explicitly destructured with * is #to_a.
  1. class Point
  2.   attr_accessor :x, :y
  3.   def initialize(x, y)
  4.     @x, @y = x, y
  5.   end

  6.   def to_a
  7.     [x, y]
  8.   end
  9. end

  10. point = Point.new(6, 3)
  11. x, y = *point
  12. x   #=> 6
  13. y   #=> 3
复制代码

使用道具 举报

回复
论坛徽章:
277
马上加薪
日期:2014-02-19 11:55:14马上有对象
日期:2014-02-19 11:55:14马上有钱
日期:2014-02-19 11:55:14马上有房
日期:2014-02-19 11:55:14马上有车
日期:2014-02-19 11:55:14马上有车
日期:2014-02-18 16:41:112014年新春福章
日期:2014-02-18 16:41:11版主9段
日期:2012-11-25 02:21:03ITPUB年度最佳版主
日期:2014-02-19 10:05:27现任管理团队成员
日期:2011-05-07 01:45:08
15#
 楼主| 发表于 2013-11-3 00:53 | 只看该作者
The second, for implicit destructuring, is #to_ary. You’ll want to be more selective in implementing this one, as it can lead to your objects suddenly behaving like arrays in places you weren’t expecting.
  1. class Point
  2.   attr_accessor :x, :y
  3.   def initialize(x, y)
  4.     @x, @y = x, y
  5.   end

  6.   def to_ary
  7.     [x, y]
  8.   end
  9. end

  10. point = Point.new(6, 3)
  11. x, y = point
  12. x   #=> 6
  13. y   #=> 3

  14. points = [Point.new(1, 5), Point.new(4, 2)]
  15. points.each do |x, y|
  16.   ...
  17. end

  18. # using our distance method from an earlier example
  19. euclidean_distance(Point.new(1, 5), Point.new(4, 2))    #=> 4.242640687119285
复制代码

使用道具 举报

回复
论坛徽章:
277
马上加薪
日期:2014-02-19 11:55:14马上有对象
日期:2014-02-19 11:55:14马上有钱
日期:2014-02-19 11:55:14马上有房
日期:2014-02-19 11:55:14马上有车
日期:2014-02-19 11:55:14马上有车
日期:2014-02-18 16:41:112014年新春福章
日期:2014-02-18 16:41:11版主9段
日期:2012-11-25 02:21:03ITPUB年度最佳版主
日期:2014-02-19 10:05:27现任管理团队成员
日期:2011-05-07 01:45:08
16#
 楼主| 发表于 2013-11-5 09:53 | 只看该作者
There’s one final use for the splat (*); when overriding methods in a subclass and calling super. If it turns out you don’t need the arguments to the method for whatever additional behaviour you’re adding, you can accept all arguments with a bare splat, then a bare super passes all arguments to super.
  1. class LoggedReader < Reader
  2.   def initialize(*)
  3.     super
  4.     @logger = Logger.new(STDOUT)
  5.   end

  6.   def read(*)
  7.     result = super
  8.     @logger.info(result)
  9.     result
  10.   end
  11. end
复制代码

使用道具 举报

回复
论坛徽章:
277
马上加薪
日期:2014-02-19 11:55:14马上有对象
日期:2014-02-19 11:55:14马上有钱
日期:2014-02-19 11:55:14马上有房
日期:2014-02-19 11:55:14马上有车
日期:2014-02-19 11:55:14马上有车
日期:2014-02-18 16:41:112014年新春福章
日期:2014-02-18 16:41:11版主9段
日期:2012-11-25 02:21:03ITPUB年度最佳版主
日期:2014-02-19 10:05:27现任管理团队成员
日期:2011-05-07 01:45:08
17#
 楼主| 发表于 2013-11-5 09:53 | 只看该作者
Conversion methods

We’ve just seen there are methods for implicit and explicit conversion of an object to an array. These can also be thought of as non-strict and strict conversion methods, and there are a few for other types.

The non-strict methods you probably know well, #to_a, #to_i, #to_s, as of Ruby 2.0 #to_h, and a handful of others. These are available on a wide range of classes, including nil.
  1. {:foo => 1, :bar => 2}.to_a   #=> [[:foo, 1], [:bar, 2]]
  2. nil.to_a                      #=> []
  3. "3".to_i                      #=> 3
  4. "foo".to_i                    #=> 0
  5. nil.to_i                      #=> 0
  6. [1,2,3].to_s                  #=> "[1, 2, 3]"
  7. nil.to_s                      #=> ""
  8. nil.to_h                      #=> {}
复制代码

使用道具 举报

回复
论坛徽章:
277
马上加薪
日期:2014-02-19 11:55:14马上有对象
日期:2014-02-19 11:55:14马上有钱
日期:2014-02-19 11:55:14马上有房
日期:2014-02-19 11:55:14马上有车
日期:2014-02-19 11:55:14马上有车
日期:2014-02-18 16:41:112014年新春福章
日期:2014-02-18 16:41:11版主9段
日期:2012-11-25 02:21:03ITPUB年度最佳版主
日期:2014-02-19 10:05:27现任管理团队成员
日期:2011-05-07 01:45:08
18#
 楼主| 发表于 2013-11-5 09:53 | 只看该作者
These non-strict conversions can sometimes trip you up, allowing invalid data to pass further down a system than you’d want, returning incorrect results, or producing unexpected errors.
  1. USERS = ["Arthur", "Ford", "Trillian"]

  2. def user(id)
  3.   USERS[id.to_i]
  4. end

  5. user(nil)   #=> "Arthur" # oops! should have returned an error
复制代码
Ruby provides some more strict conversion methods for a few types, #to_ary, #to_int, and #to_str. These are only implemented on classes where the strict conversion makes sense, e.g. #to_int is only available on numeric classes.
  1. def user(id)
  2.   USERS[id.to_int]
  3. end

  4. user(nil)   #=> NoMethodError: undefined method ‘to_int’ for nil:NilClass
复制代码

使用道具 举报

回复
论坛徽章:
277
马上加薪
日期:2014-02-19 11:55:14马上有对象
日期:2014-02-19 11:55:14马上有钱
日期:2014-02-19 11:55:14马上有房
日期:2014-02-19 11:55:14马上有车
日期:2014-02-19 11:55:14马上有车
日期:2014-02-18 16:41:112014年新春福章
日期:2014-02-18 16:41:11版主9段
日期:2012-11-25 02:21:03ITPUB年度最佳版主
日期:2014-02-19 10:05:27现任管理团队成员
日期:2011-05-07 01:45:08
19#
 楼主| 发表于 2013-11-5 09:54 | 只看该作者
This makes our example a little better, we’re getting an error and it’s coming from the right place, but the error doesn’t really convey our full intent. Plus the original was probably written that way to allow strings to be used, and as String doesn’t implement #to_int we’ve broken that.

But Ruby has another set of conversion methods, and these are a little more intelligent. The most useful of these areArray() and Integer() (along with the other numeric conversion methods like Float()). String() and Hash() are less useful as they just delegate to #to_s and #to_h.
  1. def user(id)
  2.   USERS[Integer(id)]
  3. end

  4. user(nil)   #=> TypeError: can't convert nil into Integer
复制代码
Now our example raises an error that really shows our intentions, plus we can use strings again. This is where the Integer() method really shines
  1. "1".to_i         #=> 1
  2. Integer("1")     #=> 1

  3. "foo".to_i       #=> 0
  4. Integer("foo")   #=> ArgumentError: invalid value for Integer(): "foo"
复制代码

使用道具 举报

回复
论坛徽章:
277
马上加薪
日期:2014-02-19 11:55:14马上有对象
日期:2014-02-19 11:55:14马上有钱
日期:2014-02-19 11:55:14马上有房
日期:2014-02-19 11:55:14马上有车
日期:2014-02-19 11:55:14马上有车
日期:2014-02-18 16:41:112014年新春福章
日期:2014-02-18 16:41:11版主9段
日期:2012-11-25 02:21:03ITPUB年度最佳版主
日期:2014-02-19 10:05:27现任管理团队成员
日期:2011-05-07 01:45:08
20#
 楼主| 发表于 2013-11-5 09:55 | 只看该作者
Array() is also really useful, as when it can’t convert its argument to an array it will put the argument in an array.
  1. Array([1, 2])   #=> [1, 2]
  2. Array(1)        #=> [1]
复制代码
Occasionally you want to write a method that takes a single item, or an array of items, and Array() lets you do this without messing about checking types.
  1. def project_cost(hours, developer)
  2.   developers = Array(developer)
  3.   avg_rate = developers.inject(0) {|acc, d| acc + d.rate} / developers.length
  4.   hours * avg_rate
  5. end
复制代码

使用道具 举报

回复

您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

TOP技术积分榜 社区积分榜 徽章 团队 统计 知识索引树 积分竞拍 文本模式 帮助
  ITPUB首页 | ITPUB论坛 | 数据库技术 | 企业信息化 | 开发技术 | 微软技术 | 软件工程与项目管理 | IBM技术园地 | 行业纵向讨论 | IT招聘 | IT文档
  ChinaUnix | ChinaUnix博客 | ChinaUnix论坛
CopyRight 1999-2011 itpub.net All Right Reserved. 北京盛拓优讯信息技术有限公司版权所有 联系我们 未成年人举报专区 
京ICP备16024965号-8  北京市公安局海淀分局网监中心备案编号:11010802021510 广播电视节目制作经营许可证:编号(京)字第1149号
  
快速回复 返回顶部 返回列表