What general tips can you give for golfing in Ruby?
I'm looking for ideas that can be applied to code golf problems in general that are specific to Ruby. (For example, "Remove comments" would not be an answer.)
Please post one tip per answer.
-
\$\begingroup\$ Someone needs to write a language called Rub, which uses a single Unicode character for every Ruby token, kinda like Jelly and Pyth :) \$\endgroup\$Mark Thomas– Mark Thomas2017年08月30日 12:28:57 +00:00Commented Aug 30, 2017 at 12:28
53 Answers 53
- The numbers 100 to 126 can be written as
?d
to?~
in 1.8. - On a similar note if you need a single-character string in 1.9 ?x is shorter than "x".
- If you need to print a string without appending a newline,
$><<"string"
is shorter thanprint"string"
. - If you need to read multiple lines of input
$<.map{|l|...}
is shorter thanwhile l=gets;...;end
. Also you can use$<.read
to read it all at once. - If you're supposed to read from a file,
$<
andgets
will read from a file instead of stdin if the filename is inARGV
. So the golfiest way to reimplementcat
would be:$><<$<.read
.
-
1\$\begingroup\$ ?x yields the ascii code in general, so you can realistically get all the printables to digits in two characters. 1.9 is different, 'a'.ord yields the ascii number, but is four bytes longer than the decimal version. \$\endgroup\$Hiato– Hiato2011年02月03日 08:03:17 +00:00Commented Feb 3, 2011 at 8:03
-
8\$\begingroup\$ An even golfier way to implement
cat
is to leave the ruby file completely empty (0 bytes) and insist that it should be run from the command line with the-p
flag. \$\endgroup\$daniero– daniero2013年07月17日 20:01:02 +00:00Commented Jul 17, 2013 at 20:01 -
1\$\begingroup\$ or, from @daniero's own answer,
puts *$<
\$\endgroup\$Not that Charles– Not that Charles2014年07月02日 19:15:07 +00:00Commented Jul 2, 2014 at 19:15 -
1\$\begingroup\$ So in 1.8, all I have to do is go ?~ and it will return 126? \$\endgroup\$Simply Beautiful Art– Simply Beautiful Art2017年05月13日 13:09:30 +00:00Commented May 13, 2017 at 13:09
-
9\$\begingroup\$ You can go beyond 126 using thinks like
☺
or♫
, or if you are crazy enough:?﷽.ord=65021
\$\endgroup\$Simply Beautiful Art– Simply Beautiful Art2017年05月13日 15:45:55 +00:00Commented May 13, 2017 at 15:45
Use the splat operator to get the tail and head of an array:
head, *tail = [1,2,3]
head => 1
tail => [2,3]
This also works the other way:
*head, tail = [1,2,3]
head => [1,2]
tail => 3
Use the *
method with a string on an array to join elements:
[1,2,3]*?,
=> "1,2,3"
- Use
abort
to terminate the program and print a string to STDERR - shorter thanputs
followed byexit
- If you read a line with
gets
, you can then use~/$/
to find its length (this doesn't count a trailing newline if it exists) - Use
[]
to check if a string contains another:'foo'['f'] #=> 'f'
- Use
tr
instead ofgsub
for character-wise substitutions:'01011'.tr('01','AB') #=> 'ABABB'
- If you need to remove trailing newlines, use
chop
instead ofchomp
-
\$\begingroup\$ Please explain how to use
~/$/
\$\endgroup\$Mathieu CAROFF– Mathieu CAROFF2018年11月25日 06:47:21 +00:00Commented Nov 25, 2018 at 6:47 -
\$\begingroup\$ @MathieuCAROFF every time you call
gets
, its result is stored in the$_
variable./regex/ ~= string
returns the index of the first match. Calling~
on a regex is equivalent to/regex/ ~= $_
. So it would be something likes=gets;l= ~/$/
\$\endgroup\$Cyoce– Cyoce2019年02月08日 03:18:03 +00:00Commented Feb 8, 2019 at 3:18
End your end
.
Try to remove end
from your code.
Don't use def...end
to define functions. Make a lambda with the new -> operator in Ruby 1.9. (The -> operator is a "stabby lambda", or "dash rocket".) This saves 5 characters per function.
# 28 characters
def c n
/(\d)1円/=~n.to_s
end
# 23 characters, saves 5
c=->n{/(\d)1円/=~n.to_s}
Method calls are c n
or c(n)
. Lambda calls are c[n]
. Changing each c n
to c[n]
costs 1 character, so if you can use c n
more than 5 times, then keep the method.
All methods that take do...end
blocks can take {...}
blocks instead. This saves 3 to 5 characters. If the precedence of {...}
is too high, then use parentheses to fix it.
# 48 characters
(?a..?m).zip (1..5).cycle do|a|puts a.join','end
# WRONG: passes block to cycle, not zip
(?a..?m).zip (1..5).cycle{|a|puts a.join','}
# 45 characters, saves 3
(?a..?m).zip((1..5).cycle){|a|puts a.join','}
Replace if...else...end
with the ternary operator ?:
. If a branch has two or more statements, wrap them in parentheses.
# 67 characters
if a<b
puts'statement 1'
puts'statement 2'else
puts'statement 3'end
# 62 characters, saves 5
a<b ?(puts'statement 1'
puts'statement 2'):(puts'statement 3')
You probably don't have while
or until
loops, but if you do, then write them in modifier form.
(a+=1
b-=1)while a<b
-
2\$\begingroup\$ Are the parentheses around
puts'statement 3'
necessary? \$\endgroup\$Cyoce– Cyoce2016年12月02日 02:38:06 +00:00Commented Dec 2, 2016 at 2:38
Addition to w0lf
When working with arrays,
.compact
can be replaced with-[nil]
to save 2 chars.
Combined with above -> you can make it even shorter with -[p]
to save another 2 chars.
Use operator methods instead of parentheses
Let's say you want to express a*(b+c)
. Because of precedence, a*b+c
won't work (obviously). Ruby's cool way of having operators as methods comes to the rescue! You can use a.*b+c
to make the precedence of *
lower than that of +
.
a*(b+c) # too long
a*b+c # wrong
a.*b+c # 1 byte saved!
This can also work with the !
and ~
operators (things like unary +
or unary -
don't work because their methods are -@
and +@
, saving ()
but adding .@
)
(~x).to_s # too long
~x.to_s # error
x.~.to_s # 1 byte saved!
Don't use the true
and false
keywords.
Use:
!p
fortrue
(thanks, histocrat!)!0
forfalse
. If all you need is a falsy value, then you can simply usep
(which returnsnil
).
to save some chars.
-
1\$\begingroup\$ Unless you actually need
true
(i.e. if a truthy value is enough, like in an if condition), you don't even need!!
. \$\endgroup\$Martin Ender– Martin Ender2014年08月06日 06:31:37 +00:00Commented Aug 6, 2014 at 6:31
When you are using string interpolation, (as you should pr Martin Büttner's post), you don't need the curly brackets if your object has a sigil ($
, @
) in front of it. Useful for magical variables like $_
, $&
, 1ドル
etc:
puts "this program has read #$. lines of input"
So also if you need to print a variable more than you use it otherwise, you may save some bytes.
a=42; puts "here is a: #{a}"; puts "here is a again: #{a}"
$b=43; puts "here is b: #$b"; puts "here is b again: #$b"
-
\$\begingroup\$ Isn't
puts "here is b: "+b
more shorter? \$\endgroup\$vrintle– vrintle2020年12月16日 07:53:56 +00:00Commented Dec 16, 2020 at 7:53 -
1\$\begingroup\$ @vrintle In this particular example, yes. But for instance
"here is b:#{b}, here is a:#{a}!!"
is shorter than"here is b:"+b+", here is a:"+a+"!!"
. The linked post also provides more info \$\endgroup\$daniero– daniero2020年12月16日 22:14:41 +00:00Commented Dec 16, 2020 at 22:14
New features in Ruby 2.3 and 2.4
It's good to stay abreast of new language features that will help your golf game. There are a few great ones in the latest Rubies.
Ruby 2.3
The safe navigation operator: &.
When you call a method that might return nil
but you want to chain additional method calls if it's not, you waste bytes handling the nil
case:
arr = ["zero", "one", "two"]
x = arr[5].size
# => NoMethodError: undefined method `size' for nil:NilClass
x = arr[5].size rescue 0
# => 0
The "safe navigation operator" stops the chain of method calls if one returns nil
and returns nil
for the whole expression:
x = arr[5]&.size || 0
# => 0
Array#dig
& Hash#dig
Deep access to nested elements, with a nice short name:
o = { foo: [{ bar: ["baz", "qux"] }] }
o.dig(:foo, 0, :bar, 1) # => "qux"
Returns nil
if it hits a dead end:
o.dig(:foo, 99, :bar, 1) # => nil
Enumerable#grep_v
The inverse of Enumerable#grep
—returns all elements that don't match the given argument (compared with ===
). Like grep
, if a block is given its result is returned instead.
(1..10).grep_v 2..5 # => [1, 6, 7, 8, 9, 10]
(1..10).grep_v(2..5){|v|v*2} # => [2, 12, 14, 16, 18, 20]
Hash#to_proc
Returns a Proc that yields the value for the given key, which can be pretty handy:
h = { N: 0, E: 1, S: 2, W: 3 }
%i[N N E S E S W].map(&h)
# => [0, 0, 1, 2, 1, 2, 3]
Ruby 2.4
Ruby 2.4 isn't out yet, but it will be soon and has some great little features. (When it's released I'll update this post with some links to the docs.) I learned about most of these in this great blog post.
Enumerable#sum
No more arr.reduce(:+)
. You can now just do arr.sum
. It takes an optional initial value argument, which defaults to 0 for Numeric elements ([].sum == 0
). For other types you'll need to provide an initial value. It also accepts a block that will be applied to each element before addition:
[[1, 10], [2, 20], [3, 30]].sum {|a,b| a + b }
# => 66
Integer#digits
This returns an array of a number's digits in least-to-greatest significance order:
123.digits # => [3, 2, 1]
Compared to, say, 123.to_s.chars.map(&:to_i).reverse
, this is pretty nice.
As a bonus, it takes an optional radix argument:
a = 0x7b.digits(16) # => [11, 7]
a.map{|d|"%x"%d} # => ["b", "7"]
Comparable#clamp
Does what it says on the tin:
v = 15
v.clamp(10, 20) # => 15
v.clamp(0, 10) # => 10
v.clamp(20, 30) # => 20
Since it's in Comparable you can use it with any class that includes Comparable, e.g.:
?~.clamp(?A, ?Z) # => "Z"
String#unpack1
A 2-byte savings over .unpack(...)[0]
:
"👻💩".unpack(?U) # => [128123]
"👻💩".unpack(?U)[0] # => 128123
"👻💩".unpack1(?U) # => 128123
Precision argument for Numeric#ceil
, floor
, and truncate
Math::E.ceil(1) # => 2.8
Math::E.floor(1) # => 2.7
(-Math::E).truncate(1) # => -2.7
Multiple assignment in conditionals
This raises an error in earlier versions of Ruby, but is allowed in 2.4.
(a,b=1,2) ? "yes" : "no" # => "yes"
(a,b=nil) ? "yes" : "no" # => "no"
-
\$\begingroup\$ Golf
Math::E.ceil(1)
toMath::E.ceil 1
, and likewise forfloor
andtruncate
. \$\endgroup\$Simply Beautiful Art– Simply Beautiful Art2017年11月20日 23:38:48 +00:00Commented Nov 20, 2017 at 23:38 -
1\$\begingroup\$ @SimplyBeautifulArt I expect that someone golfing in Ruby will be able to make that leap themselves. \$\endgroup\$Jordan– Jordan2017年11月20日 23:50:49 +00:00Commented Nov 20, 2017 at 23:50
-
1\$\begingroup\$ For
Enumerable#sum
,.flatten.sum
is 2 bytes shorter than.sum{|a,b|a+b}
\$\endgroup\$Asone Tuhid– Asone Tuhid2018年01月24日 10:45:33 +00:00Commented Jan 24, 2018 at 10:45 -
\$\begingroup\$
(-Math::E).truncate(1)
is equivalent to-Math::E.truncate(1)
which is 1 byte shorter \$\endgroup\$Asone Tuhid– Asone Tuhid2018年03月01日 12:21:24 +00:00Commented Mar 1, 2018 at 12:21 -
2\$\begingroup\$
&.
can be used with subscripting like thisa&.[]i
(1 byte shorter thana&.at i
). Although, if brackets are required,a||a[i]
is 1 byte is shorter thana&.[](i)
ora&.at(i)
\$\endgroup\$Asone Tuhid– Asone Tuhid2018年03月01日 12:24:27 +00:00Commented Mar 1, 2018 at 12:24
If you need to find if a particular element e
is inside a range r
, you can use
r===e
instead of the longer:
r.cover?(e) # only works if `r.exclude_end?` is false
or
r.member?(e)
or
r.include?(e)
-
4\$\begingroup\$ Isn’t
r===e
even shorter? \$\endgroup\$akuhn– akuhn2012年06月01日 21:10:55 +00:00Commented Jun 1, 2012 at 21:10 -
\$\begingroup\$ @akuhn Yes, it is. Much Shorter. Thanks for pointing that out, it helped me shorten my code by 10 chars, which is huge: codegolf.stackexchange.com/a/6125/3527 \$\endgroup\$Cristian Lupascu– Cristian Lupascu2012年06月01日 21:20:33 +00:00Commented Jun 1, 2012 at 21:20
-
1\$\begingroup\$ You’re welcome. Everything that can be used in a switch statement has
===
implemented. \$\endgroup\$akuhn– akuhn2012年06月02日 12:49:34 +00:00Commented Jun 2, 2012 at 12:49
Build arrays using a=i,*a
to get them in reverse order. You don't even need to initialize a
, and if you do it doesn't have to be an array.
Avoid length
in if a.length<n
length
is 6 bytes, a bit costly in code golf. in many situations, you can instead check if the array has anything at a given point. if you grab past the last index you will get nil
, a falsey value.
So you can Change:
if a.length<5
to if !a[4]
for -5 bytes
or
if a.length>5
to if a[5]
for -6 bytes
or
if a.length<n
to if !a[n-1]
for -3 bytes
or
if a.length>n
to if a[n]
for -6 bytes
Note: will only work with an array of all truthy values. having nil
or false
within the array may cause problems.
-
6\$\begingroup\$ I always use
size
... But this is definitely better. BTW, works forString
too. \$\endgroup\$manatwork– manatwork2015年12月07日 17:06:36 +00:00Commented Dec 7, 2015 at 17:06 -
1\$\begingroup\$
if !a[n-1]
can be shortened toif !a[-n]
\$\endgroup\$G B– G B2025年01月22日 10:22:43 +00:00Commented Jan 22 at 10:22
New features in Ruby 2.7 (experimental)
Ruby 2.7 is in prerelease (as of 17 Jun 2019) and has some features that look great for golfing. Note that some of them might not make it into the final 2.7 release.
All changes in Ruby 2.7-preview1: https://github.com/ruby/ruby/blob/v2_7_0_preview1/NEWS
Numbered block parameters
This is my favorite. It lets you finally drop the |a,b|
in a block:
Edit: The syntax was changed from @1
to _1
.
%w[a b c].zip(1..) { puts _1 * _2 }
# => a
# bb
# ccc
(削除) Method reference operator: .:
(削除ここまで)
.:
(削除ここまで)Edit: This was unfortunately removed before release.
.:
is syntactic sugar for the .method
method, e.g.:
(1..5).map(&1r.:/)
# => [(1/1), (1/2), (1/3), (1/4), (1/5)]
Pattern matching
I'm not sure how much use this will see in golf, but it's a great feature for which I only have a contrived example:
def div(*a)
case a
in [0, 0] then nil
in [x, 0] if x > 0 then Float::INFINITY
in [x, 0] then -Float::INFINITY
in [x, y] then x.fdiv(y)
end
end
div(-3, 0) # => -Infinity
The pattern matching syntax has lots of features. For a complete list, check out this presentation: https://speakerdeck.com/k_tsj/pattern-matching-new-feature-in-ruby-2-dot-7
This is also the feature most likely to change before 2.7 is finished; it even prints a warning when you try to use it, which you should heed:
warning: Pattern matching is experimental, and the behavior may change in future versions of Ruby!
Beginless Range: ..3
Analogous to the endless Range introduced in 2.6, it may or may not have much use in golfing:
%w[a v o c a d o].grep(..?m)
# => ["a", "c", "a", "d"]
Enumerable#tally
to count like elements
This could be useful in golfing:
%w[a v o c a d o].tally
# => {"a"=>2, "v"=>1, "o"=>2, "c"=>1, "d"=>1}
Enumerable#filter_map
to filter
+map
in one
(1..20).filter_map {|i| 10 * i if i.even? }
# => [20, 40, 60, 80, 100]
If the block returns nil
or false
the element will be omitted from the result.
Integer#[]
takes a second argument or range:
You've long been able to get a specific bit from an integer with with subscript notation:
n = 77 # (binary 01001101)
n[3] # => 1
Now you can get the value of a range of bits by a second length argument or a range.
n = 0b01001101
n[2, 4] # => 3 (0011)
n[2..5] # => 3
Note that bits are indexed from least- to most-significant (right to left).
-
\$\begingroup\$ The range in the
Enumerable#filter_map
example should be(1..10)
, I think? \$\endgroup\$Dingus– Dingus2020年05月01日 01:46:19 +00:00Commented May 1, 2020 at 1:46 -
2\$\begingroup\$ The method reference operator didn't make it in, as I sadly discovered just now trying to use it to do a restricted source challenge. \$\endgroup\$histocrat– histocrat2020年06月06日 17:32:34 +00:00Commented Jun 6, 2020 at 17:32
-
7\$\begingroup\$
@1
and@2
are apparently now_1
and_2
. \$\endgroup\$Eric Duminil– Eric Duminil2020年10月12日 15:05:46 +00:00Commented Oct 12, 2020 at 15:05 -
2\$\begingroup\$ Unfortunatelly
.:
didn't make it through preview and was reverted in 2.7 release bugs.ruby-lang.org/issues/16275 \$\endgroup\$Mike– Mike2022年12月11日 20:37:49 +00:00Commented Dec 11, 2022 at 20:37
$_
is last read line.
print
- if no argument given print content of$_
~/regexp/
- short for$_=~/regexp/
In Ruby 1.8, you have four methods in Kernel
that operate on $_
:
chop
chomp
sub
gsub
In Ruby 1.9, these four methods exist only if your script uses -n
or -p
.
If you want to print some variable often then use trace_var(:var_name){|a|p a}
-
2\$\begingroup\$ These are only available when you run Ruby with the
-p
or-n
option. Reference. \$\endgroup\$Darren Stone– Darren Stone2013年12月27日 19:12:17 +00:00Commented Dec 27, 2013 at 19:12 -
1\$\begingroup\$ It seems that
trace_var
only works with global $variables \$\endgroup\$daniero– daniero2016年03月23日 08:56:33 +00:00Commented Mar 23, 2016 at 8:56 -
1\$\begingroup\$ Another trick that works with
-n
or-p
is using a regexp literal as a boolean: the regexp is implicitly matched against$_
. See this answer of mine for an example. \$\endgroup\$Dingus– Dingus2020年09月04日 00:30:07 +00:00Commented Sep 4, 2020 at 0:30 -
\$\begingroup\$ Unlike the
String
methods with the same names, the listedKernel
methods modify$_
in place. Generally this behaviour is what is wanted, but worth noting the difference. \$\endgroup\$Dingus– Dingus2020年11月30日 11:50:24 +00:00Commented Nov 30, 2020 at 11:50
Use string interpolation!
To replace
to_s
. If you need parentheses around whatever you want to turn into a string,to_s
is two bytes longer than string interpolation:(n+10**i).to_s "#{n+10**i}"
To replace concatenation. If you concatenate something surrounded by two other strings, interpolation can save you one byte:
"foo"+c+"bar" "foo#{c}bar"
Also works if the middle thing is itself concatenated, if you just move the concatenation inside the interpolation (instead of using multiple interpolations):
"foo"+c+d+e+"bar" "foo#{c+d+e}bar"
Don't use #each. You can loop over all elements just fine with #map. So instead of
ARGV.each{|x|puts x}
you can do the same in less bytes.
ARGV.map{|x|puts x}
Of course, in this case puts $*
would be even shorter.
There are literals for rational and complex numbers:
puts 3/11r == Rational(3,11)
puts 3.3r == Rational(66,20)
puts 1-1.i == Complex(1,-1)
=> true
true
true
You can use most bytes within strings. "\x01"
(6 bytes) can be shortened to ""
(3 bytes). If you only need this one byte, this can be shortened even further to ?
(2 bytes).
By the same token, you can get newlines shorter like this:
(0..10).to_a.join'
'
=> "0\n1\n2\n3\n4\n5\n6\n7\n8\n9\n10"
You can use ?\n
and ?\t
as well, which is one byte shorter than "\n"
and "\t"
. For obfuscation, there also ?\s, a space.
Use constants instead of passing arguments around, even if you need to change them. The interpreter will give warnings to stderr, but who cares. If you need to define more variables related to each other, you can chain them like this:
A=C+B=7+C=9
=> A=17, B=16, C=9
This is shorter than C=9;B=16;A=17
or C=0;B=C+7;A=C+B
.
If you need an infinite loop, use loop{...}
. Loops of unknown length may be shorter with other loops:
loop{break if'
'==f(gets)}
while'
'!=f(gets);end
Some more gsub/regexp tricks. Use the special '1円'
escape characters instead of a block:
"golf=great short=awesome".gsub(/(\w+)=(\w+)/,'(1円~>2円)')
"golf=great short=awesome".gsub(/(\w+)=(\w+)/){"(#{1ドル}~>#{2ドル})")
And the special variables 1ドル
etc. if you need to perform operations. Keep in mind they are defined not only inside the block:
"A code-golf challenge." =~ /(\w+)-(\w+)/
p [1,ドル2,ドル$`,$']
=> ["code", "golf", "A ", " challenge."]
Get rid of spaces, newlines, and parentheses. You can omit quite a bit in ruby. If in doubt, always try if it works without, and keep in mind this might break some editor syntax highlighting...
x+=1if$*<<A==????::??==??
-
\$\begingroup\$ "Please post one tip per answer." Also
?\n
is nice, but not really shorter than actually putting a newline character inside quotes. (same for tab) \$\endgroup\$Martin Ender– Martin Ender2015年05月07日 23:29:28 +00:00Commented May 7, 2015 at 23:29 -
\$\begingroup\$ And
puts$*
is even shorter. \$\endgroup\$Cyoce– Cyoce2017年11月07日 05:20:01 +00:00Commented Nov 7, 2017 at 5:20 -
\$\begingroup\$ I know you were trying to prove a point but I'm pretty sure that last example is the same as
x+=1;$*<<A
\$\endgroup\$Asone Tuhid– Asone Tuhid2018年03月01日 14:03:37 +00:00Commented Mar 1, 2018 at 14:03
If you ever need to get a number from ARGV
, get
, or something similar to do something that many times, instead of calling to_i
on it, you can just use ?1.upto x{do something x times}
where x is a string.
So using ?1.upto(a){}
instead of x.to_i.times{}
will save you 2 characters.
You can also re-write things like p 1 while 1
or p 1 if 1
as p 1while 1
or p 1if 1
That example isn't very useful, but it could be used for other things.
Also, if you need to assign the first element of an array to a variable, a,=c
will save two characters as opposed to a=c[0]
Save some bytes when removing repeated elements of an array
a.uniq # before
a|[] # after
^^
If you will be using an empty array []
in a variable, you can save even more bytes:
a.uniq;b=[] # before
a|b=[] # after
^^^^^
-
9\$\begingroup\$ For the first case,
a&a
is 1 byte shorter \$\endgroup\$Asone Tuhid– Asone Tuhid2018年03月07日 12:59:59 +00:00Commented Mar 7, 2018 at 12:59
Scientific notation can often be used to shave off a char or two:
x=1000
#versus
x=1e3
-
11\$\begingroup\$ Note: This will return a Float value (1000.0) instead of an Integer, which may cause inaccurate results with large numbers. \$\endgroup\$Dogbert– Dogbert2011年02月25日 11:02:40 +00:00Commented Feb 25, 2011 at 11:02
-
4\$\begingroup\$ Ah, nice
1e2
is better than100.0
when a percentage is needed. \$\endgroup\$Phrogz– Phrogz2011年02月26日 06:36:55 +00:00Commented Feb 26, 2011 at 6:36 -
1\$\begingroup\$ Similar to this principle,
1.0*
is 1 char shorter than.to_f
\$\endgroup\$Unihedron– Unihedron2017年12月16日 13:44:38 +00:00Commented Dec 16, 2017 at 13:44 -
\$\begingroup\$ To my surprise, this trick works with
Integer#upto
andInteger#downto
, e.g.1.upto(1000)
can become1.upto(1e3)
. (Both forms iterate over integers.) \$\endgroup\$Dingus– Dingus2020年05月20日 01:06:48 +00:00Commented May 20, 2020 at 1:06
Kernel#p is a fun method.
Use p var
instead of puts var
. This works perfectly with integers and floats, but not with all types. It prints quotation marks around strings, which is probably not what you want.
Used with a single argument, p
returns the argument after printing it.
Used with multiple arguments, p
returns the arguments in an array.
Use p
(with no arguments) instead of nil
.
-
12\$\begingroup\$ Unfortunately
p 'some string'
prints"some string"
and not justsome string
which is often criticised by others. \$\endgroup\$Patrick Oscity– Patrick Oscity2013年06月01日 21:11:12 +00:00Commented Jun 1, 2013 at 21:11 -
2\$\begingroup\$ Basically
p s
is the same asputs s.inspect
, but it returnss
\$\endgroup\$Cyoce– Cyoce2016年11月03日 23:37:59 +00:00Commented Nov 3, 2016 at 23:37
Yet another way to use the splat operator: if you want to assign a single array literal, a *
on the left-hand side is shorter than brackets on the right-hand side:
a=[0]
*a=0
With multiple values you don't even need the splat operator (thanks to histocrat for correcting me on that):
a=[1,2]
a=1,2
-
\$\begingroup\$ The latter case doesn't actually need the splat. \$\endgroup\$histocrat– histocrat2015年08月15日 22:53:07 +00:00Commented Aug 15, 2015 at 22:53
-
\$\begingroup\$ @histocrat Oh wow, I thought the second value would just be discarded in that case. \$\endgroup\$Martin Ender– Martin Ender2015年08月15日 22:53:45 +00:00Commented Aug 15, 2015 at 22:53
-
1\$\begingroup\$ I can't believe I haven't known these in all the time I've spent golfing in Ruby. \$\endgroup\$Doorknob– Doorknob2016年01月02日 03:07:22 +00:00Commented Jan 2, 2016 at 3:07
To join an array, instead of this
[...].join
do this
[...]*''
which saves 2 bytes. To join with a separator use
[...]*?,
Save a byte when printing a word with symbols
This is a bit situational, but every byte counts!
puts"thing" # before
puts:thing # after
^
Avoid Array#repeated_permutation
and Array#repeated_combination
Credit to @AsoneTuhid who golfed the code for repeated permutations of length \$\ge5\$.
Some of Ruby's built-in methods have unfortunately long names. Never use Array#repeated_permutation
or Array#repeated_combination
; save bytes as follows.
Repeated permutations
Assume a
is an array. To get repeated permutations of length \$L = n + 1\$ of the elements of a
:
a.product(a,a,a) # L <= 4; number of arguments = n
a.product(*[a]*n) # L >= 5
Depending on context, the parentheses may not be required. Both of the above yield an array. To iterate over the permutations, simply call with a block.
Repeated combinations
For repeated combinations of length \$L\$, use one of
a.send(a.methods[42],L) # enumerator
[*a.send(a.methods[42],L)] # array
The index that yields :repeated_combination
depends on the Ruby version and possibly the OS (42
is correct for Ruby 2.5.5 on Linux, which is the version on TIO at the time of writing). The default indexing may also be disrupted if any libraries are loaded. The correct index in any case can always be found using [].methods.index(:repeated_combination)
.
In general, calling a method by index using Object#send
and Object#methods
, as demonstrated above for repeated combinations, is shorter than a direct method call when the number of bytes in the method name and the index of the method in the methods
array satisfy:
+-------+-------+ | Bytes | Index | +-------+-------+ | 19+ | 0-9 | | 20+ | 10-99 | | 21+ | 100+ | +-------+-------+ Subtract 1 from byte count if parentheses not needed for send.
-
\$\begingroup\$ 17 bytes \$\endgroup\$Asone Tuhid– Asone Tuhid2020年06月02日 22:17:15 +00:00Commented Jun 2, 2020 at 22:17
-
\$\begingroup\$ @AsoneTuhid Very nice. Seems so obvious now that I see it. Thanks! \$\endgroup\$Dingus– Dingus2020年06月03日 00:20:30 +00:00Commented Jun 3, 2020 at 0:20
Use Goruby instead of Ruby, which is something like an abbreviated version of Ruby. You can install it with rvm via
rvm install goruby
Goruby allows you to write most of your code as you would be writing Ruby, but has additional abbreviations built in. To find out the shortest available abbreviation for something, you can use the helper method shortest_abbreviation
, for example:
shortest_abbreviation :puts
#=> "pts"
Array.new.shortest_abbreviation :map
#=> "m"
String.new.shortest_abbreviation :capitalize
#=> "cp"
Array.new.shortest_abbreviation :join
#=> "j"
Also very handy is the alias say
for puts
which itself can be abbreviated with s
. So instead of
puts [*?a..?z].map(&:capitalize).join
you can now write
s [*?a..?z].m(&:cp).j
to print the alphabet in capitals (which is not avery good example). This blog post explains more stuff and some of the inner workings if you are interested in further reading.
PS: don't miss out on the h
method ;-)
-
2\$\begingroup\$ More than 2 years later and I finally figured out what this answer reminds me of... \$\endgroup\$undergroundmonorail– undergroundmonorail2015年05月22日 13:53:19 +00:00Commented May 22, 2015 at 13:53
I just attempted a TDD code-golf challenge i.e. Write shortest code to make specs pass. The specs were something like
describe PigLatin do
describe '.translate' do
it 'translates "cat" to "atcay"' do
expect(PigLatin.translate('cat')).to eq('atcay')
end
# And similar examples for .translate
end
end
For the sake of code-golf, one need not create a module or class.
Instead of
module PigLatin def self.translate s;'some code'end;end
one can do
def(PigLatin=p).translate s;'some code'end
Saves 13 characters!
-
9\$\begingroup\$ Ha, very thorough. Not only did you add the necessary behavior to
PigLatin
, but also to@pig_latin
,$pig_latin
, and'pig'['latin']
. \$\endgroup\$histocrat– histocrat2014年02月27日 15:23:19 +00:00Commented Feb 27, 2014 at 15:23 -
\$\begingroup\$ @histocrat: Now I get it. It's because
translate
has been defined onnil
. \$\endgroup\$Eric Duminil– Eric Duminil2017年03月20日 13:48:54 +00:00Commented Mar 20, 2017 at 13:48
When a challenge requires that you output multiple lines, you don't have to loop through your results in order to print each line of e.g. an array. The puts
method will flatten an array and print each element on a separate line.
> a = %w(testing one two three)
> puts a
testing
one
two
three
Combining the splat operator with #p
you can make it even shorter:
p *a
The splat operator (technically the *@
method, I think) also casts your non-array enumerables to arrays:
> p a.lazy.map{|x|x*2}
#<Enumerator::Lazy: #<Enumerator::Lazy: [1, 2, 3]>:map>
vs
> p *a.lazy.map{|x|x*2}
2
4
6
-
1\$\begingroup\$
*@
is not a method, splat is syntactic sugar \$\endgroup\$Asone Tuhid– Asone Tuhid2018年01月24日 10:55:14 +00:00Commented Jan 24, 2018 at 10:55
Subscripting Numbers!
I just discovered this yesterday. n[i]
returns n
's bit at the i
-th position.
Example:
irb(main):001:0> n = 0b11010010
=> 210
irb(main):002:0> n[0]
=> 0
irb(main):003:0> n[1]
=> 1
irb(main):004:0> n[2]
=> 0
irb(main):005:0> n[3]
=> 0
irb(main):006:0> n[4]
=> 1
irb(main):007:0> n[5]
=> 0
-
\$\begingroup\$ And now you can use more arguments such as
n[0..3]
\$\endgroup\$Simply Beautiful Art– Simply Beautiful Art2019年07月30日 00:23:20 +00:00Commented Jul 30, 2019 at 0:23
On looping
while...end
If you need to break
out of the loop, while condition;code;end
will probably be shorter than loop{code;condition||break}
.
The ;
before end
is not always required, eg. while condition;p("text")end
until c;...;end
is equivalent to while !c;...;end
and 1 byte shorter.
Note that in most cases code while condition
and code until condition
are significantly shorter as they don't require the end
keyword and can often drop semicolons. Also, i+=1 while true
is equivalent to i+=1while true
and 1 byte shorter.
redo
When run, the redo
command jumps back to the beginning of the block it's in.
When using redo
in a lambda, you will have to move any setup variables to the arguments to avoid them being reset at every iteration (see examples).
recursion
Recursion can be shorter is some cases. For instance, if you're working on an array element by element, something like f=->s,*t{p s;t[0]&&f[*t]}
can be shorter than the alternatives depending on the stuff
.
Note that per the current consensus, if you're calling your function by name, you need to include the assignment (f=
) in the byte count making all recursive lambdas 2 bytes longer by default.
eval
If you need to run some code n
times, you can use eval"code;"*n
.
This will concatenate code;
n
times and run the whole thing.
Note that in most cases you need to include a ;
after your code.
Examples
A lambda to print all numbers from 1
to a
inclusive:
->n{i=0;loop{p i+=1;i<n||break}} # 32 bytes
f=->n,i=1{i>n||p(i)&&f[n,i+1]} # 30 bytes
->n,i=0{p(i+=1)<n&&redo} # 24 bytes
->n{i=0;p i+=1while i<n} # 24 bytes
->n{i=0;eval"p i+=1;"*n} # 24 bytes
->n{n.times{|i|p i+1}} # 22 bytes # thanks to @benj2240
->n{n.times{p _1+1}} # 20 bytes # thanks to @AgentIvan
In this case, since the end-point is defined (n
), the n.times
loop is the shortest.
The redo
loop works because i+=1
modifies i and returns its new value and p(x)
returns x
(this is not true of print
and puts
).
Given a function g
and a number n
, find the first number strictly larger than n
for which g[n]
is truthy
->g,n{loop{g[n+=1]&&break};n} # 29 bytes
f=->g,n{g[n+=1]?n:f[g,n]} # 25 bytes
->g,n{1until g[n+=1];n} # 23 bytes
->g,n{(n+1..).find &g} # 22 bytes
->g,n{g[n+=1]?n:redo} # 21 bytes
In this case, with an unknown end-point, redo
is the best option.
The (n+1..Inf)
loop is equivalent to simply loop
ing indefinitely but more verbose.
A 1
(or anything else) is required before the until
keyword to complete the syntax, using a number allows you to drop a space.
The eval
method is not viable in this case because there is neither a defined end-point nor an upper bound.
Update: with the new open ranges (n+1..Inf)
can be written simply as (n+1..)
, also .find{|x|g[x]}
is equivalent to .find &g
where g
is converted to a block.
TL;DR check out redo
, it can very often shave off a couple of bytes
-
1\$\begingroup\$
.times
can often be even shorter for fixed loops, eg->n{n.times{|i|p i+1}}
for 22 bytes \$\endgroup\$benj2240– benj22402018年03月16日 15:07:37 +00:00Commented Mar 16, 2018 at 15:07