Ruby Koans: test_sometimes_missing_parentheses_are_ambiguous - ruby

I am completing Neo's Ruby Koans (http://rubykoans.com/). In about_methods.rb, the koan instructs to correct the following, currently broken, eval:
# (NOTE: We are Using eval below because the example code is
# considered to be syntactically invalid).
def test_sometimes_missing_parentheses_are_ambiguous
eval "assert_equal (5), my_global_method (2, 3)" # ENABLE CHECK
#
# Ruby doesn't know if you mean:
#
# assert_equal(5, my_global_method(2), 3)
# or
# assert_equal(5, my_global_method(2, 3))
#
# Rewrite the eval string to continue.
#
end
my_global_method is
def my_global_method(a,b)
a + b
end
How do I need to alter the eval to pass this test?
Edit: RubyKoans: broken koan? asked whether this code was broken or not, and while it indicates that the question is working as intended, no answer to the koan is supplied.
The error is as follows:
(eval):1: syntax error, unexpected tINTEGER, expecting keyword_do or '{' or '(' (SyntaxError)
assert_equal 5, my_global_method 2, 3

Change it to:
eval "assert_equal 5, my_global_method(2, 3)"

You want your eval line to look something more like this:
eval "assert_equal 5, my_global_method(2, 3)" # ENABLE CHECK
Your error is telling you that it can't understand the parameters being passed to it, as it is missing parentheses. They put that code in incorrectly to demonstrate the need for parentheses when calling a method with several parameters.

Related

Using splat: SyntaxError: unexpected '\n', expecting '.' or &. or :: or '['

Why does
a = *(1..10)
=> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
but
*(1..10)
SyntaxError: unexpected '\n', expecting '.' or &. or :: or '['
(I'd have thought it would return the same as in the first case, but that it simply wouldn't do any assignment). Curious to know if the reason for this is ruby related, or more specifically related to splat (*)
I don't think of splat as a unary operator to be used on its own /w an expression like that.
It's syntax for assigning values to arguments in a method call:
def foo(a, b)
puts a
puts b
end
foo(*[:a, :b])
For setters, this means:
obj.my_attribute = *(1..10)
is sugar for the method:
obj.my_attribute=(*1..10)
It's also used inside Array literal expressions: [*1..10]
I was a little surprised to find that a = *(1..10) works because it's local variable assignment, but perhaps Ruby treats that the same way.
*(1..10) by itself, however, definitely isn't a method call. You should use (1..10).to_a.

Ruby: How can I kill "warning: `*' interpreted as argument prefix"? [duplicate]

This question already has answers here:
Why does white-space affect ruby function calls?
(2 answers)
Closed 6 years ago.
How can I remove the "warning: `*' interpreted as argument prefix" from the following code?
hash = {"a" => 1,
"b" => 2,
"s" => 3,}
if "string".start_with? *hash.keys then
puts "ok"
else
puts "ng"
end
When I run the code above, I get:
$ ruby -w /tmp/a.rb
/tmp/a.rb:5: warning: `*' interpreted as argument prefix
ok
What is the best way to fix this warning?
I've tried to put parenthesis around hash like this:
hash = {"a" => 1,
"b" => 2,
"s" => 3,}
if "string".start_with? (*hash.keys) then
puts "ok"
else
puts "ng"
end
then you get:
$ ruby -w /tmp/a.rb
/tmp/a.rb:5: syntax error, unexpected *
if "string".start_with? (*hash.keys) then
^
/tmp/a.rb:5: syntax error, unexpected ')', expecting '='
if "string".start_with? (*hash.keys) then
^
/tmp/a.rb:7: syntax error, unexpected keyword_else, expecting end-of-input
And this is the problem described in Why does white-space affect ruby function calls?, and clearly not the way to fix the warning I'm trying to fix.
My ruby version is:
$ ruby --version
ruby 2.3.3p222 (2016-11-21) [x86_64-linux-gnu]
If you're going to use method-calling-parentheses then you must avoid putting a space between the method name and the opening parentheses:
if "string".start_with?(*hash.keys)
puts "ok"
else
puts "ng"
end
Also, then is rather archaic so we'll pretend that was never there. If there is a space between the method name and the opening parentheses then your parentheses are interpreted as expression-grouping-parentheses and that's where your syntax error comes from.
Once you add the method-calling-parentheses you remove any possible hint of ambiguity as to what your * is supposed to mean and the warning should go away.
BTW, the warning you're getting in this case is rather, um, silly. On second thought, the warning isn't so silly because Ruby can be whitespace sensitive in surprising ways. This:
o.m *x
can be interpreted as:
o.m(*x)
or as:
o.m() * x
but these:
o.m * x
o.m*x
o.m* x
can be interpreted in the same ways. Of course, all three of those are interpreted as o.m() * x and only o.m *x is seen as o.m(*x). Sane whitespace usage would suggest that o.m *x is obviously a splat whereas o.m * x is obviously a multiplication but a couple days on SO should convince you that whitespace usage is hardly sane or consistent.
That said, -w's output in the Real World tends to be so voluminous and noisy that -w is nearly useless.

Why won't Ruby return an array without "return"?

def foo
1,2
end
causes syntax error "unexpected ',', expecting keyword_end"
I'd think this is valid Ruby. What's wrong?
You're not returning an array.
You should have this:
def foo
[1, 2]
end
Ruby isn't expecting a comma (,) because it isn't valid syntax. Integers in a simple array should be surrounded by brackets as well as delineated by a comma.
If you used explicit return it will work.
def foo
return 1,2
end
But that wouldn't work with implicit return. To make it work with implicit return you need to give it [1, 2].

Passing hash to p causes syntax error

Why do I get this?
p {a:3}
# => syntax error, unexpected tINTEGER, expecting tSTRING_CONTENT or tSTRING_DBEG or tSTRING_DVAR or tSTRING_END
# => p {a:3}
^
Ruby has a few oddities in its parsing engine. One is that certain things require parentheses around them.
For instance, this should work.
p({a:3})
Or this
hash = { a: 3 }
p hash
As the other answer pointed out. The reason for this is that the interpreter processes as below.
# Input
p { a: 3 }
# What the interpreter sees
p do
a: 3
end
The Kernel#p doesn't support blocks, so you must use the parentheses.

Ruby Regexp Interpolation / Character Class / Global Variable Syntax Clash?

Why does this error occur?
Regexp.new("[#$]")
# => SyntaxError: (irb):1: syntax error, unexpected $undefined
# => Regexp.new("[#$]")
# ^
# (irb):1: unterminated string meets end of file
# from ~/.rvm/rubies/ruby-1.9.3-p194/bin/irb:1:in `<main>'
This should describe the subset of strings consisting of either a single $ or #, literally. And, AFAIU Ruby's Regexp engine, # and $ don't need to be escaped inside a character class even though they're usually metacharacters.
I would guess from the error message that Ruby is trying to interpolate $ when it's hitting # within double-quotes, but...why? Ordering is important. The $ and # characters have multiple overloaded behaviors, so I'm at a loss about what's triggering this.
PS, FYI:
/[#$]/
# => SyntaxError: (irb):1: syntax error, unexpected $undefined
/[$#]/
# => /[$#]/
Regexp.new '[$#]'
# => /[$#]/
Regexp.new '[#$]'
# => /[#$]/
Regexp.new "[#$]"
# => SyntaxError: (irb):1: syntax error, unexpected $undefined
The problem is not $, but #, as #... is usually used for variable expansion in double quoted strings. Like "#{x}".
But the thing is you can also expand global variables directly using #$global, and that explains your problem:
$global = "hello"
"#$global"
=> "hello"
So the solution is to escape either # or $, as this will break the string interpolation state machine out of it's effort to interpret the construct as an interpolation:
puts "\#$global"
=> #$global
puts "#\$global"
=> #$global
EDIT
And just to make it really clear :) The problem is not the Regexp, but you are trying to expand a global variable named $] when you type "#$]":
puts "#$]"
SyntaxError: (irb):22: syntax error, unexpected $undefined
To fix it you need to escape something:
puts "\#$]"
=> #$]

Resources