-->

Why are there so many slightly different ways to d

2020-08-12 16:41发布

问题:

I am learning Ruby. My background is C++/Java/C#. Overall, I like the language, but I am a little confused about why there are so many different ways to accomplish the same thing, each with their own slightly different semantics.

Take string creation, for example. I can use '', "", q%, Q%, or just % to create strings. Some forms support interpolation. Other forms allow me to specify the string delimiters.

Why are there five ways to create string literals? Why would I ever use non-interpolated strings? What advantage does the % syntax have over quoted literals?

I know there must be value in the redundency in Ruby, but my untrained eyes are not clearly seeing it. Please enlighten me.

回答1:

Why would I ever use non-interpolated strings?

When you don't want the interpolation, of course. For example, perhaps you're outputting some documentation about string interpolation:

'Use #{x} to interpolate the value of x.'
=> "Use #{x} to interpolate the value of x."

What advantage does the % syntax have over quoted literals?

It lets you write strings more naturally, without the quotes, or when you don't want to escape a lot of things, analogous to C#'s string-literal prefix @.

%{The % syntax make strings look more "natural".}
=> "The % syntax makes strings look more \"natural\"."

%{<basket size="50">}
=> "<basket size=\"50\">"

There are many other %-notations:

%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"

There's also %s (for symbols) and some others.

Why are there five ways to create string literals?

This isn't terribly unusual. Consider C#, for example, which has several different ways to generate strings: new String(); ""; @""; StringBuilder.ToString(), et cetera.



回答2:

I'm not a Ruby expert, but had you ever heard the term "syntactic sugar" ? Basically some programing languages offer different syntax to accomplish the same task. Some people could find one way easier than others due to his previous programing/syntax experience.



回答3:

In most situations, you'll end up using normal string delimiters. The main difference between single and double quotes is that double quotes allow you to interpolate variables.

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 and %Q are useful when you can't use quotes because they are part of the internal string. For example, you might end up writing

html = %Q{this is a <img src="#{img_path}" class="style" /> image tag}

In this case, you can't use double quotes as delimiters unless you want to escape internal attribute delimiters. Also, you can't use single quote because the img_path variable won't be interpolated.



回答4:

The original question is why there are so many slightly different ways of doing things in Ruby.

Sometimes the different things are sensible: quoting is a good case where different behaviour requires different syntax - non/interpolating, alternate quoting characters, etc. - and historical accidence causes synonyms like %x() vs ``, much as in Perl.

The synonym issue - [].size [].length [].count - feels like an attempt to be helpful in a world where the language is too random for IDEs to be able to help: monkey-patching and the weird combination of strict but dynamic typing together make runtime errors an inevitable and frustrating part of the coding, so folks try to reduce the issue by supplying synonyms. Unfortunately, they end up confusing programmers who're accustomed to different methods doing different things.

The 'so similar but not quite' issue, for example ...

 $ 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])'
 $ 

... can only really be viewed as a flaw. Every language has them, but they're usually more arcane than this.

And then there's the plain arbitrary 'use fail instead of raise unless you're just rethrowing an exception' nonsense in the Rubocop coding standards.

There are some nice bits in Ruby, but really - I'd far rather be coding in something better-founded.



回答5:

A lot of ruby's syntax is derived from perl's, like using q to quote a few words into a string. That probably is the main reason for such a big variety.



回答6:

One more reason is a minor performance boost for non-interpolated strings. Using '' vs "" means that Ruby doesn't have to consider what's inside the string at all. So you'll see people using single quotes for array keys or symbols because they're faster. For what it's worth I'll include a little benchmark.

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

yields:

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


回答7:

You would use non-interpolated strings if your string contains a lot of special characters (like backslashes, #{} etc.) and you don't want to escape all of them.

You'd use different delimiters if your string contains a lot of quotes that you'd otherwise have to escape.

You'd use heredocs if your strings has a lot of lines which would make normal string syntax look unwieldy.



回答8:

Ruby borrows constructs and ideas from lots of languages. The two most apparent influences are Smalltalk and Perl.

Depending on your comfort with Smalltalk or Perl you may well choose different constructs to do the same thing.



回答9:

Along the lines of John's answer: In quick hacks, I often end up running a perl or sed one-liner with grep syntax from within my ruby script. Being able to use %[ ] type syntax means that I can simply copy-paste my regexp from the terminal