我正在学习红宝石。我的背景是 C++/Java/C#。总的来说,我喜欢这种语言,但我有点困惑,为什么有这么多不同的方法来完成同一件事,每一种都有自己略微不同的语义。
以创建字符串为例。我可以使用 ''、""、q%、Q% 或仅使用 % 来创建字符串。某些形式支持插值。其他形式允许我指定字符串分隔符。
为什么有五种方法可以创建字符串文字?为什么我会使用非插值字符串?% 语法比带引号的文字有什么优势?
我知道 Ruby 中的冗余肯定是有价值的,但我未经训练的眼睛并不能清楚地看到它。请赐教。
为什么我会使用非插值字符串?
当然,当您不想要插值时。例如,也许您正在输出一些关于字符串插值的文档:
'Use #{x} to interpolate the value of x.'
=> "Use #{x} to interpolate the value of x."
% 语法比带引号的文字有什么优势?
它使您可以更自然地编写字符串,无需引号,或者当您不想转义很多东西时,类似于 C# 的 string-literal prefix @
。
%{The % syntax make strings look more "natural".}
=> "The % syntax makes strings look more \"natural\"."
%{<basket size="50">}
=> "<basket size=\"50\">"
还有许多其他的 % 符号:
%w{apple banana #{1}cucumber} # [w]hitespace-separated array, no interpolation
=> ["apple", "banana", "\#{1}cucumber"]
%W{apple banana #{1}cucumber} # [W]hitespace-separated array with interpolation
=> ["apple", "banana", "1cucumber"]
# [r]egular expression (finds all unary primes)
%r{^1?$|^(11+?)\1+$}
=> /^1?$|^(11+?)\1+$/
(1..30).to_a.select{ |i| ("1" * i) !~ %r{^1?$|^(11+?)\1+$} }
=> [2, 3, 5, 7, 11, 13, 17, 19, 23, 29]
%x{ruby --version} # [s]hell command
=> "ruby 1.9.1p129 (2009-05-12 revision 23412) [x86_64-linux]\n"
还有%s
(用于符号)和其他一些。
为什么有五种方法可以创建字符串文字?
这并不是很不寻常。例如,考虑 C#,它有几种不同的方式来生成字符串new String()
:""
; @""
; StringBuilder.ToString()
,等等。
我不是 Ruby 专家,但你听说过“语法糖”这个词吗?基本上,一些编程语言提供不同的语法来完成相同的任务。由于他以前的编程/语法经验,有些人可能会比其他人更容易找到一种方法。
最初的问题是为什么在 Ruby 中有这么多略有不同的做事方式。
有时不同的事情是明智的:引用是一个很好的情况,其中不同的行为需要不同的语法 - 非/插值,交替引用字符等 - 历史偶然性会导致同义词,如 %x() vs ``,就像在 Perl 中一样。
同义词问题 - [].size [].length [].count - 感觉就像是试图在语言过于随机以至于 IDE 无法提供帮助的世界中有所帮助:猴子补丁和严格的奇怪组合但是动态类型一起使运行时错误成为编码中不可避免且令人沮丧的部分,因此人们试图通过提供同义词来减少问题。不幸的是,它们最终使习惯于使用不同方法做不同事情的程序员感到困惑。
例如,“如此相似但不完全”的问题......
$ ruby -le 'e=[]; e << (*[:A, :B])'
-e:1: syntax error, unexpected ')', expecting :: or '[' or '.'
$ ruby -le 'e=[]; e << *[:A, :B]'
-e:1: syntax error, unexpected *
$ ruby -le 'e=[]; e.push(*[:A, :B])'
$
......只能真正被视为一个缺陷。每种语言都有它们,但它们通常比这更神秘。
然后在 Rubocop 编码标准中出现了简单的任意“使用失败而不是 raise,除非你只是重新抛出异常”的废话。
Ruby 中有一些不错的部分,但实际上——我宁愿编写更有根据的代码。
在大多数情况下,您最终会使用普通的字符串分隔符。单引号和双引号之间的主要区别在于双引号允许您插入变量。
puts 'this is a string'
# => this is a string
puts "this is a string"
# => this is a string
v = "string"
puts 'this is a #{v}'
# => this is a #{v}
puts "this is a #{v}"
# => this is a string
%q
并且%Q
当您不能使用引号时很有用,因为它们是内部字符串的一部分。例如,你最终可能会写
html = %Q{this is a <img src="#{img_path}" class="style" /> image tag}
在这种情况下,您不能使用双引号作为分隔符,除非您想转义内部属性分隔符。此外,您不能使用单引号,因为img_path
不会对变量进行插值。
很多 ruby 的语法都是从 perl 派生的,比如q
用来将几个单词引用到一个字符串中。这可能是品种如此之多的主要原因。
另一个原因是非插值字符串的性能提升很小。使用 '' vs "" 意味着 Ruby 根本不需要考虑字符串中的内容。因此,您会看到人们使用单引号作为数组键或符号,因为它们更快。对于它的价值,我将包括一个小基准。
require 'benchmark'
Benchmark.bmbm(10) do |x|
x.report("single-quote") do
for z in 0..1000000
zf = 'hello'
end
end
x.report("double-quote") do
for z in 0..1000000
zf = "hello"
end
end
x.report("symbol") do
for z in 0..1000000
zf = :hello
end
end
end
产量:
Rehearsal ------------------------------------------------
single-quote 0.610000 0.000000 0.610000 ( 0.620387)
double-quote 0.630000 0.000000 0.630000 ( 0.627018)
symbol 0.270000 0.000000 0.270000 ( 0.309873)
--------------------------------------- total: 1.580000sec
#{}
如果您的字符串包含很多特殊字符(如反斜杠等)并且您不想全部转义,您将使用非插值字符串。
如果您的字符串包含许多您必须转义的引号,您将使用不同的分隔符。
如果你的字符串有很多行会使普通的字符串语法看起来很笨拙,你会使用 heredocs。
Ruby 借鉴了许多语言的结构和思想。两个最明显的影响是 Smalltalk 和 Perl。
根据您对 Smalltalk 或 Perl 的熟悉程度,您可能会选择不同的结构来做同样的事情。
按照约翰的回答:在快速破解中,我经常在我的 ruby 脚本中使用 grep 语法运行 perl 或 sed 单行程序。能够使用%[ ]
类型语法意味着我可以简单地从终端复制粘贴我的正则表达式