Something in my computer-science-student background feels unsettled when I can use a language for years and still learn new tricks (or often, that I've forgotten that I've learned) about common functionality such as string interpolation...but I do love a lot of Ruby's tricks.
if path =~ %r{^/assets/mobile/img}
...
end
I knew about `%r` but hadn't realized that it escapes forward-slashes while leaving other regex symbols intact. But with the previous explanation of `%q`, in which arbitrary delimiters can be used, it makes sense...curly braces simply replace `/` as the delimiter, and `/` simply retains its non-specialness in regex...saves me a lot of writing `Regex.escape("/path/to")`.
Another regex notation that I recently learned (elsewhere) and now love to abuse:
This explanation of how %r works here is incorrect:
> I knew about `%r` but hadn't realized that it escapes forward-slashes while leaving other regex symbols intact.
The correct explanation can also be found in the next sentence of your comment:
> But with the previous explanation of `%q`, in which arbitrary delimiters can be used, it makes sense...curly braces simply replace `/` as the delimiter, and `/` simply retains its non-specialness in regex.
I think growing a string with << is an expensive operation. Since once the allocated memory is full, it will cost O(n) to copy the entire string to a new location. If we are carrying out m insertions, this could take O(mn).
The way I have been building strings is to store the m strings in an Array and then join() it at the end (which should be only O(n)). The m small objects need to be allocated in both cases, the only difference being that we hold on to those and join them later on.
Am I missing something? I have never measured the performance on either of the these operations so I could be off the mark here.
For the cases I have benchmarked concatenation with << has been faster than join on an array.
Here's a simple benchmark:
require "benchmark"
n = 100_000
Benchmark.bm(4) do |x|
x.report("<<") do
n.times do
"aaaaa " << "bbbbbb " << "ccccc " << "ddddd " << "eeeee " << "fffff"
end
end
x.report("join") do
n.times do
["aaaaa", "bbbbbb", "ccccc", "ddddd", "eeeee", "fffff"].join(" ")
end
end
end
The results I get for this are:
user system total real
<< 0.140000 0.000000 0.140000 ( 0.143750)
join 0.230000 0.000000 0.230000 ( 0.228035)
I'd be interested to see if there were any use cases where the relative performance was reversed.
require "benchmark"
n = 100_000
A = "foo " * 20
B = "bar " * 20
C = "baz " * 20
D = "foobar " * 20
STRINGS = [A, B, C, D]
Benchmark.bm(4) do |x|
x.report("<<") do
n.times do
"" << A << B << C << D
end
end
x.report("join") do
n.times do
STRINGS.join
end
end
end
user system total real
<< 0.090000 0.010000 0.100000 ( 0.102411)
join 0.070000 0.010000 0.080000 ( 0.071142)
These are some good tips. If you are into the Ruby language and want more, I recommend checking out Ruby Tapas http://www.rubytapas.com/ . I have no affiliation with it, just happy customer.
Here's an example that shows x ||= y behaves like x || x = y, rather than x = x || y
class Foo
def foo=(object)
puts "foo= called with #{object.inspect}"
@foo = object
end
def foo
puts "foo called"
@foo
end
end
puts "x || x = y"
a = Foo.new
a.foo || a.foo = 1
a.foo || a.foo = 2
puts "\nx = x || y"
b = Foo.new
b.foo = b.foo || 1
b.foo = b.foo || 2
puts "\nx ||= y"
c = Foo.new
c.foo ||= 1
c.foo ||= 2
This outputs:
x || x = y
foo called
foo= called with 1
foo called
x = x || y
foo called
foo= called with 1
foo called
foo= called with 1
x ||= y
foo called
foo= called with 1
foo called
and you can see that the output for ||= matches the output for x || x = y
I didn't mean that it is "x = x || y", I meant "it's completely ad hoc".
For example, if you try "x || x = y" you will get an exception if x is not defined, but "x ||= y" will work fine. The same is true of constants "X || X = 1" will explode while "X ||= 1" will work fine.
Another regex notation that I recently learned (elsewhere) and now love to abuse:
as opposed to: