1、字符串格式化

  Python

"%s=%s" % (k, v)

  在阅读 Python 字符串格式化的时候,视线先看到字符串的 %s 字样,但是不知道这指的是什么,然后看后面的变量 k,再接着看第二个 %s ,再看后面的 v 视线必须不停地在字符串和变量之间跳动。
  Ruby

"#{k}=#{v}"

  而阅读 Ruby 字符串格式化的时候,看到需要变量的地方,变量就在那里。

  顺便一说

"%s = %s" % [k,v]

  这种风格的代码在 Ruby 里面也能用,Ruby 的理念认为解决问题的方法可以不止一种,选择哪种取决于程序员的喜好。

  2、映射(迭代)

  这在 Python 中叫做列表解析,不过不管叫什么,实际上这是迭代的一种。

[elem*2 for elem in li]

  li 是一个 list。阅读这行代码的时候,先看到 elem*2,但是不知道 elem 是什么。继续看,再次看到 elem ,还是不知道是什么。一直看到 in li,奥,原来 elem 是 li 中的元素,对了,刚才对 elem 做了什么来着?

如果一个元素的解析还不太迷惑,继续看下面这个例子。

["%s=%s" % (k, v) for k, v in params.items()]

  请问我应该先看哪个部分。

  下面是 Ruby 版。

li.map {|elem| elem*2}
params.map {|k, v| "#{k}=#{v}"}

  求 params 的映射(map),其中的元素是原本params里面 k, v 键值对组成的字符串,我不确保没有 Ruby 基础的人会不会习惯这种 block 语法,但是我可以保证阅读代码的时候是从左到右的单一顺序。

  3、DSL(领域语言)

  为了举一个现实中有代表意义、但是又足够简单的例子,我找到了 webpy 和 sinatra,这分别是 Python 和 Ruby 社区热门的简洁风格 web 框架。

  前置的说明是,webpy,甚至是 Python,都不是一个追求 DSL 的社区。而 Ruby 社区则以 DSL 见长,这样比较似乎有失公允。但这里可以比较 DSL 的有无对于代码的可读性有什么帮助。

  webpy 的 hello world

import web

urls = (
'/', 'hello'
)
app = web.application(urls, globals()) class hello:
def GET(self):
return 'Hello, world!' if __name__ == "__main__":
app.run()

  我对 webpy 原本的 helloworld 做了简化,以便和 sinatra 比较。

  坦率地说,webpy 的 hello world 已经够简洁了。相比起 Java EE 和 .net 庞大的 IDE 和那根本不知道拿来做什么的规范,webpy 让我们回归了单纯,简约而不简单。

但是,简约方面,Ruby 的 DSL 文化更是做到了极致,看 sinatra 的例子

require 'sinatra'

get '/' do
"Hello World!"
end

  sinatra 的 DSL 非常简练,甚至让人怀疑它是否是一个玩具。或者可以看下 sinatra 的文档或者用户列表,现在请先暂且相信,它做的事跟 webpy 没什么两样。

  DSL 是语言层面的封装,把复杂性留在库的内部,把接口用 DSL 的形式暴露给程序员。这其实跟类和函数方式的 API 没有什么不同。不过 DSL 会让人忘记自己正在使用什么语言,Rubyists 的说法是:魔法。

  总结

  Python 和 Ruby 虽然同为动态语言时代的佼佼者,不过开发和社区风格有很大的不同。这归根于两个语言诞生时的理念不同:Python 注重规范化,一个问题只有一个方法,缩进的强制约束,便于多人合作;而 Ruby 注重人性化,便于阅读,一个问题有几个方法,过多的魔法需要使用者自己锻炼驾驭能力。

1.puts([obj[, obj2[, ....]]] )

依次将obj和换行符输出到$>。若没有参数的话则只会输出换行符。

若参数是数组,则依次输出数组元素和换行符。若将既非数组又非字符串的对象传递给参数时,将尝试使用使用to_s方法将其化为字符串。若是nil则输出字符串"nil"。

若参数是以换行符结尾时,puts将不再输出换行符。

注意:当obj为string或者array时,puts会对obj里面的转义符号进行转义;如果不是,比如Hash类型,就直接先调用to_s方法,将其转化为字符串,这里是不会对转义进行处理的,所以当我们通过 key-value 对hash进行访问时,如果value为 string或array 中包含转义字符还是会转义的;在做接口测试时,解析json串中,有时中文显示成unicode编码

str = "\u5473\u9053\u4e0d\u9519"
puts str #=> 味道不错 str = ["\u5473\u9053\u4e0d\u9519", "\u5473\u9053\u4e0d\u9519", ["\u5473\u9053\u4e0d\u9519"]]
puts str
#=> 味道不错
#=> 味道不错
#=> 味道不错 str = {a:"\u5473\u9053\u4e0d\u9519"}
puts str #=> {:a=>"\u5473\u9053\u4E0D\u9519"}
puts str[:a] #=> 味道不错

2.print([arg1[, arg2, ...]])

print 基本与puts相同,但输出内容后,不会自动在结尾加上换行符

3.p(obj, [obj2, ...])

以通俗易懂的方式输出obj。等同于以下代码

print obj.inspect, "\n", obj2.inspect, "\n", ...
p "\u5473\u9053\u4e0d\u9519" #=> "\u5473\u9053\u4E0D\u9519"
p不会识别双引号内的转义符,并自动换行

所以p 和 puts主要是用途的不同,p 是作为 debug 输出,而 puts 作为对象的字符串表示输出

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。

ruby klb.rb irb的更多相关文章

  1. [ruby on rails] 深入(2) ruby基本语法

    1. 调试&注释&打印输出 1.1 调试 ruby属于解释型语言,即脚本,在linux上,脚本的执行无非三种: 1. 用解释器运行脚本 解释器 脚本文件 即:ruby  脚本文件 2. ...

  2. Ruby安装

    Windows下安装ruby 先安装ruby吧 点击安装,额,咳咳什么情况,好了 人是有国籍的,但知识无国界的 是这个意思吧,选择安装语言   选择安装目录 顺便勾选上添加到环境变量吧   安装完成 ...

  3. ruby语言是什么东西

    1.简介    Ruby是日本的Yukihiro Matsumoto写的,简单易学的面向对象的脚本语言,像perl一样,有丰富的文字处理.系统管理等丰富 功能,但是ruby要简单,容易理解和扩充.跟p ...

  4. mac平台下面ruby环境搭建

    一.安装xcode 先安装 [Xcode](http://developer.apple.com/xcode/) 开发工具,它将帮你安装好 Unix 环境需要的开发包 二.安装 RVM curl -L ...

  5. 【ruby】ruby基础知识

    Install Ruby(安装) For windows you can download Ruby from http://rubyforge.org/frs/?group_id=167 for L ...

  6. ruby学习总结01

    1.ruby的两种运行方式:ruby方式咋(在命令行中输入ruby xxx.rb)和irb方式(在命令行中输入 irb) 注意:可以在命令后添加 -E UTF-8 指定编码格式 例:ruby -E U ...

  7. 【转】教你Ruby快速入门

    转自:http://developer.51cto.com/art/200703/41243.htm 介绍 这是一个短小的Ruby入门,完全读完只需20分钟.这里假设读者已经安装了Ruby,如果你没有 ...

  8. ruby编程语言-学习笔记1

    安装完 ruby ri irb ruby-devel 1. 先来个简单的,写个helloworld  给新手们 (terminal中,# 代表root权限,$ 代表用户权限, 前面的就不写了.) # ...

  9. ruby 安装 运行

    Ruby基础 一 简介 1.Ruby在windows平台下的安装 (1)下载地址:http://rubyinstaller.org/downloads/ (2)安装过程 这里我们选择安装路径为 D:\ ...

随机推荐

  1. Linux下使用GDAL进行开发(automake使用)

    首先写三个源代码文件,分别是GDALTest.cpp.Fun.cpp和Fun.h,将这三个存放在一个叫GDALTest的文件夹中,然后打开终端,切换到该目录,如下图所示(注:这个图是最后截图的,所以文 ...

  2. 演练Ext JS 4.2自定义主题

    本文将根据API文档中关于主题的介绍做的一次演练,以便熟悉自定义主题的过程. 练习环境: Sencha Cmd v4.0.1.45 Ruby 1.9.3-p392 firefox 26 首先,使用以下 ...

  3. AndFix使用感想

    AndFix已经使用了一段时间了,但是到AndFix上看了一下,最近2个月都没有更新代码了,有141个issues和3个pull request没人处理,其实AndFix的Contributors就俩 ...

  4. linux下用gtk+写比赛赌博GUI小游戏

    游戏界面全部由gtk的GUI完成,没有使用openGL之类的高端货. 游戏玩法就是8位选手比赛跑步,你可以在赛前赌哪位选手会赢,如果输了cash会被扣除,反之cash会增加. 无聊写了3个选项:小数时 ...

  5. linux内核算法---hex_to_bin分享

    这是我从内核抠出来的一段代码,用处就是传入一个字符,即可以用printf语句%d以十进制数的格式输出,同时也可以以%p地址的形式输出. 代码如下: #include <stdio.h> # ...

  6. iOS9 ReplayKit录制视频

    猴子原创,欢迎转载.转载请注明: 转载自Cocos2Der-CSDN,谢谢! 原文地址: http://blog.csdn.net/cocos2der/article/details/50260873 ...

  7. 如何设置静态IP

    首先在CMD命令行ipconfig查看临时分配的IP地址: 然后打开我的"网络"--->"本地连接"--->IPv4--->属性 电信DNS劫 ...

  8. oo第二次博客作业

    多线程协同与同步控制总结 第五次作业-多线程电梯 本次作业是我第一次接触多线程,建立了请求模拟器.调度器和电梯运行三种线程.请求模拟器负责在输入后识别有效请求:调度器在扫描有效请求后将新的请求加入请求 ...

  9. ios中block访问外部变量的一些注意点

    Block类型是一个C级别的语法和运行机制.它与标准的C函数类似,不同之处在于,它除了有可执行代码以外,它还包含了与堆.栈内存绑定的变量.因此,Block对象包含着一组状态数据,这些数据在程序执行时用 ...

  10. rotate image(旋转数组)

    You are given an n x n 2D matrix representing an image. Rotate the image by 90 degrees (clockwise). ...