Does anyone see the error in this simple Ruby function? - ruby

This function is supposed to take a string and return the characters in reverse order.
def reverse(string)
reversedString = "";
i = string.length - 1
while i >= 0
reversedString = reversedString + string[i]
i -= 1
end
puts reversedString
end
however all the tests return false:
puts(
'reverse("abc") == "cba": ' + (reverse("abc") == "cba").to_s
)
puts(
'reverse("a") == "a": ' + (reverse("a") == "a").to_s
)
puts(
'reverse("") == "": ' + (reverse("") == "").to_s
)
Does anyone see what the problem is?

Try to use the default String class reverse method like this:
"Hello World".reverse
"Hello World".reverse!
Check Ruby's String class API at https://ruby-doc.org/core-2.4.0/String.html
If you want to make your custom method, you could use a map like this:
string = String.new
"Hello World".chars.each { | c | string.prepend c }

The problem is your function isn't returning its result, it's printing it. It needs to return reversedString.
As a rule of thumb, functions should return their result. Another function should format and print it.
def reverse(string)
reversedString = "";
i = string.length - 1
while i >= 0
reversedString = reversedString + string[i]
i -= 1
end
return reversedString
end
Note: This was probably an exercise, but Ruby already has String#reverse.
It's good that you're writing tests, but the way you're writing them it's hard to tell what went wrong. Look into a Ruby testing framework like MiniTest.
require "minitest/autorun"
class TestReverse < Minitest::Test
def test_reverse
assert_equal "cba", reverse("abc")
assert_equal "a", reverse("a")
assert_equal "", reverse("")
end
end
That would have told you that your function is returning nil.
1) Failure:
TestReverse#test_reverse [test.rb:16]:
Expected: "cba"
Actual: nil

To make this more Ruby-like yet avoid using the built-in String#reverse method you'd do this:
def reverse(string)
string.chars.reverse.join('')
end
Remember that in Ruby the result of the last operation is automatically the return value of the method. In your case the last operation is puts which always returns nil, eating your value. You want to pass it through.
Try to design methods with a simple mandate, that is, this function should focus on doing one job and one job only: reversing a string. Displaying it is beyond that mandate, so that's a job for another method, like perhaps the caller.
To avoid calling any sort of reverse method at all:
def reverse(string)
result = ''
length = string.length
length.times do |i|
result << string[length - 1 - i]
end
result
end
You can often avoid for almost completely and while frequently if you use things like times or ranges (0..n) to iterate over.

puts prints and returns nil, so the whole method returns nil. If, for debugging reasons , you want to inspect what your method is returning, use p which returns it's argument (reversedString in this case).
def reverse(string)
reversedString = ""
i = string.length - 1
while i >= 0
reversedString = reversedString + string[i]
i -= 1
end
p reversedString # !!!
end
And all 3 tests return true

If I was going to do this, I'd probably take advantage of an array:
ary = 'foo bar baz'.chars
reversed_ary = []
ary.size.times do
reversed_ary << ary.pop
end
reversed_ary.join # => "zab rab oof"
pop removes the last character from the array and returns it, so basically it's walking backwards through ary, nibbling at the end and pushing each character onto the end of reversed_ary, effectively reversing the array.
Alternately it could be done using a string:
ary = 'foo bar baz'.chars
reversed_str = ''
ary.size.times do
reversed_str << ary.pop
end
reversed_str # => "zab rab oof"
or:
reversed_str += ary.pop
I just saw that #tadman did a similar thing with the string. His would run more quickly but this is more readable, at least to my eyes.

Related

Gettting frozen error after iterating with each_char in String class. Any fixes?

# Character Counter
class String
def count_char
#lcase_count ,#upcase_count, #num_count, #spl_char_count = [0, 0 ,0 ,0]
each_char { |char|
if ('a'..'z').cover?(char)
#lcase_count += 1
elsif ('A'..'Z').cover?(char)
#upcase_count += 1
elsif ('0'..'9').cover?(char)
#num_count += 1
else
#spl_char_count += 1
end
}
return #lcase_count,#upcase_count,#num_count,#spl_char_count
end
end
input = ARGV[0]
if ARGV.empty?
puts 'Please provide an input'
exit
end
puts 'Lowercase characters = %d' % [input.count_char[0]]
puts 'Uppercase characters = %d' % [input.count_char[1]]
puts 'Numeric characters = %d' % [input.count_char[2]]
puts 'Special characters = %d' % [input.count_char[3]]
Traceback (most recent call last):
1: from new.rb:25:in <main>'
new.rb:3:incount_char': can't modify frozen String (FrozenError)
I think as far, i didnt modify string not sure why getting FrozenError
You are monkeypatching the String class and at the same time introduce new instance variables to String, which already is a terrible design decision, because - unless you are the author of the String class -, you don't know whether or not these variables exist already. Then, in your code, you modify the variables by incrementing them. Since ARGV is an array of frozen strings, you get the error.
Using instance variables here is absolutely unnecessary. Just use normal local variables.
It’s impossible to tell what exactly is wrong with your code, it looks like one of the instance variables you use is initialized as string or likewise. Introducing instance variables in foreign classes is not a good practice in general, also you do abuse each for reducing. Here is an idiomatic ruby code for your task:
class String
def count_char
each_char.with_object(
{lcase_count: 0, upcase_count: 0, num_count: 0, spl_char_count: 0}
) do |char, acc|
case char
when 'a'..'z' then acc[:lcase_count] += 1
when 'A'..'Z' then acc[:upcase_count] += 1
when '0'..'9' then acc[:num_count] += 1
else acc[:spl_char_count] += 1
end
end
end
end
Please note, that this code deals with a simple latin alphabet only. Better approach would be to match regular expressions, like:
lcase_count = scan(/\P{Lower}/).count
upcase_count = scan(/\P{Upper}/).count
...
You can try following,
class String
def count_char
chars = { lcase_count: 0 ,upcase_count: 0, num_count: 0, spl_char_count: 0 }
each_char do |char|
if ('a'..'z').cover?(char)
chars[:lcase_count] += 1
elsif ('A'..'Z').cover?(char)
chars[:upcase_count] += 1
elsif ('0'..'9').cover?(char)
chars[:num_count] += 1
else
chars[:spl_char_count] += 1
end
end
return chars
end
end
str = 'Asdssd'
# => "Asdssd"
str.count_char
# => {:lcase_count=>5, :upcase_count=>1, :num_count=>0, :spl_char_count=>0}
str.count_char[:upcase_count]
# => 1
I couldn't find a document regarding ARGV being a frozen string.
But it seems to be that is the case.
You can use dup to fix your error.
input = ARGV[0].dup

Method to reverse string only if it has less than four letters

I need to write a ruby method that reverses a string only if it has less than four characters.
# Write a method that reverses
# a string ONLY if it's shorter than
# 4 letters.
# Otherwise, the string is
# returned as-is.
# (Hint: strings have
# a built-in .length method!)
# conditional_reverse("yo")
# => "oy"
# conditional_reverse("hello")
# => "hello"
Here is the code I came up with.
def conditional_reverse(string)
good = string.length
if good < 4
puts string.reverse
else
puts string
end
puts conditional_reverse("cat")
end
When I run it in repl I get the following response
:conditional_reverse
I have no idea what i'm doing wrong.
just put puts conditional_reverse("cat") out side our def
def conditional_reverse(string)
good = string.length
if good < 4
puts string.reverse
else
puts string
end
end
conditional_reverse("cat")
You are callind your method in its definition. Avoid it if you are not writing a recursive method.
def conditional_reverse(s)
s.length < 4 ? s.reverse : s
end
The answer provided by #Ursus is perfect, but in case you want to go with your way the change you have to do is this;
def conditional_reverse(string)
good = string.length
if good < 4
puts string.reverse
else
puts string
end
end
puts conditional_reverse("cat")
What the others said, plus...
You get that response from irb because in recent versions of Ruby, a method definition returns the method name as a symbol.
Also, your problem specifies that the string should be reversed, not that the string should be output reversed; so you should remove the puts calls and just manipulate the string.
For the benefit of your readers, I recommend being specific with your names. good = string.length could be changed to needs_reversing = string.length < 4, for example.

Reversing a Ruby String, without .reverse method

I am working on this coding challenge, and I have found that I am stuck. I thought it was possible to call the .string method on an argument that was passed in, but now I'm not sure. Everything I've found in the Ruby documentation suggests otherwise. I'd really like to figure this out without looking at the solution. Can someone help give me a push in the right direction?
# Write a method that will take a string as input, and return a new
# string with the same letters in reverse order.
# Don't use String's reverse method; that would be too simple.
# Difficulty: easy.
def reverse(string)
string_array = []
string.split()
string_array.push(string)
string_array.sort! { |x,y| y <=> x}
end
# These are tests to check that your code is working. After writing
# your solution, they should all print true.
puts(
'reverse("abc") == "cba": ' + (reverse("abc") == "cba").to_s
)
puts(
'reverse("a") == "a": ' + (reverse("a") == "a").to_s
)
puts(
'reverse("") == "": ' + (reverse("") == "").to_s
)
This is the simplest one line solution, for reversing a string without using #reverse, that I have come across -
"string".chars.reduce { |x, y| y + x } # => "gnirts"
Additionally, I have never heard of the #string method, I think you might try #to_s.
Easiest way to reverse a string
s = "chetan barawkar"
b = s.length - 1
while b >= 0
print s[b]
b=b-1
end
You need to stop the search for alternative or clever methods, such as altering things so you can .sort them. It is over-thinking the problem, or in some ways avoiding thinking about the core problem you have been asked to solve.
What this test is trying to get you you to do, is understand the internals of a String, and maybe get an appreciation of how String#reverse might be implemented using the most basic string operations.
One of the most basic String operations is to get a specific character from the string. You can get the first character by calling string[0], and in general you can get the nth character (zero-indexed) by calling string[n].
In addition you can combine or build longer strings by adding them together, e.g. if you had a="hell" and b="o", then c = a + b would store "hello" in the variable c.
Using this knowledge, find a way to loop through the original string and use that to build the reverse string, one character at a time. You may also need to look up how to get the length of a string (another basic string method, which you will find in any language's string library), and how to loop through numbers in sequence.
You're on the right track converting it to an array.
def reverse(str)
str.chars.sort_by.with_index { |_, i| -i }.join
end
Here is a solution I used to reverse a string without using .reverse method :
#string = "abcde"
#l = #string.length
#string_reversed = ""
i = #l-1
while i >=0 do
#string_reversed << #string[i]
i = i-1
end
return #string_reversed
Lol, I am going through the same challenge. It may not be the elegant solution, but it works and easy to understand:
puts("Write is a string that you want to print in reverse")
#taking a string from the user
string = gets.to_s #getting input and converting into string
def reverse(string)
i = 0
abc = [] # creating empty array
while i < string.length
abc.unshift(string[i]) #populating empty array in reverse
i = i + 1
end
return abc.join
end
puts ("In reverse: " + reverse(string))
Thought i'd contribute my rookie version.
def string_reverse(string)
new_array = []
formatted_string = string.chars
new_array << formatted_string.pop until formatted_string.empty?
new_array.join
end
def reverse_str(string)
# split a string to create an array
string_arr = string.split('')
result_arr = []
i = string_arr.length - 1
# run the loop in reverse
while i >=0
result_arr.push(string_arr[i])
i -= 1
end
# join the reverse array and return as a string
result_arr.join
end

why are my returns messing up my results?

The objective of this piece of code (it's part of a larger code) is to determine if a year has duplicate numbers in it. Here is my code:
def no_repeat?(year)
year = year.to_s
string = ''
year.each_char{|i| string << year[i] unless string.include?(year[i])}
year.length == string.length ? (return false) : (return true)
end
puts no_repeat?(1993)
It ALWAYS returns true, and I can't see why that is happening. I have tried expanding the ternary into a full if statement...still returns true. I have tried writing this whole method out as a while loop (with two indexes that compare one index to the other)
def no_repeat?(year)
year = year.to_s
i = 0
while i < year.length
i2 = i + 1
while i2 < year.length
if year[i] == year[i2]
return false
else
return true
end
i2 += 1
end
i += 1
end
...still returns true. I have tested each thing independently and they all work fine until I put the returns in. What is it about the returns? I need fresh eyes on it.
The way you've structured the ternary is incorrect. Since your method is attempting to ensure nothing is repeated, it should return true when the == is true. A ternary itself is intended to return a value, not really to execute an expression like (return false) inside its result. That works, but is unconventional to being practically non-existent.
The ternary should look like
return year.length == string.length ? true : false
Which can of course be simplified because the == expression already returns a boolean.
return year.length == string.length
Next, your use of year[i] isn't quite right. String#each_char is assigning the character value into i, so you can use i directly. It appears that the way you've used it actually does work, but that's not how the iterator variable i is meant to be used.
This makes your method into:
def no_repeat?(year)
year = year.to_s
string = ''
# i represents the character in this iteration
# so you may just directly reference i here
year.each_char{|i| string << i unless string.include?(i)}
# If the lengths match, return true because there's no repeating character
return year.length == string.length
# You could omit the 'return' keyword too which is preferred by convention
# since Ruby returns the last expression's value implicitly
# year.length == string.length
end
You have the true and false statements flipped. Otherwise the code works.
This works:
def no_repeat?(year)
year = year.to_s
string = ''
year.each_char{|i| string << year[i] unless string.include?(year[i])}
year.length == string.length ? (return true) : (return false)
end
no_repeat?(1993) # => false
no_repeat?(2015) # => true
However there are many ways that you should improve this code. The return keyword is rarely used in Ruby. In fact, it is completely superfluous in your example. These two methods are equivalent:
# with `return`
def no_repeat?(year)
year = year.to_s
string = ''
year.each_char{|i| string << year[i] unless string.include?(year[i])}
year.length == string.length ? (return true) : (return false)
end
# without `return`
def no_repeat?(year)
year = year.to_s
string = ''
year.each_char{|i| string << year[i] unless string.include?(year[i])}
year.length == string.length
end
Second, using a negative ("no") in a method name makes code hard to follow. I suggest flipping the logic and calling the method repeat? or even better repeat_chars?.
Finally, there are more concise ways to express the logic you have written using built-in Ruby methods. Here is one alternative implementation (I'm sure other Rubyists can chime in with even more elegant solutions):
def repeat_chars?(year)
year = year.to_s
year.length != year.chars.uniq.length
end

How does Ruby's #count method deal with nil values?

I'm doing Ruby exercises for the Odin Project (programming newcomer), and we're tasked with recreating Ruby's #count method. Given an array like:
nil_list = [false, false, nil]
Observations:
nil_list.count == 3, its length.
nil_list.count(nil) == 1, the number of times nil is present in the list.
nil_list given a block behaves as expected.
When I try to recreate it, here's what I come up with:
module Enumerable
def my_count (find = nil)
result = 0
for i in self
if block_given?
result += 1 if yield(i)
elsif find != nil
result += 1 if i == find
else return self.length
end
end
return result
end
end
The problem here is that this doesn't actually count nils if we enter nil in as an argument, since this is the same (according to my code) as there not being an argument.
ie, nil_list.my_count(nil) == 3 instead of 1.
While typing this question I had a slightly different idea:
module Enumerable
def my_count (find = "")
result = 0
for i in self
if block_given?
result += 1 if yield(i)
elsif find != ""
result += 1 if i == find
else return self.length
end
end
return result
end
end
So this fixes the problem I was having with searches for nil, but now nil_list.count("") == 0 whereas nil_list.my_count("") == 3. Same issue, just relocated to "" which I assume doesn't ever get used.
At this point I'm just curious: how does the actual count method prevent this issue from happening?
You can write def my_count(*args) and check then length of args. I'd write:
module Enumerable
def my_count(*args)
case
when args.size > 1
raise ArgumentError
when args.size == 1
value = args.first
reduce(0) { |acc, x| value == x ? acc + 1 : acc }
when block_given?
reduce(0) { |acc, x| yield(x) ? acc + 1 : acc }
else
reduce(0) { |acc, x| acc + 1 }
end
end
end
The ugly truth is: in most Ruby implementations, Enumerable#count isn't actually written in Ruby. In MRI, YARV and MRuby, it's written in C, in JRuby and XRuby, it's written in Java, in IronRuby and Ruby.NET, it's written in C#, in MacRuby, it's written in Objective-C, in MagLev, it's written in Smalltalk, in Topaz, it's written in RPython, in Cardinal, it's written in PIR or PASM, and so on. And it not only is not written in Ruby, it's also got privileged access to the internals of the execution engine, in particular, it can access the arguments that were passed, which you cannot do from Ruby.
Such overloaded methods appear all over the core library and standard library, but they can't easily be written in Ruby. The implementers cheat by either writing them in languages that do support overloading (e.g. C# or Java), or they give them privileged access to the internals of the execution engine.
The standard workaround in Ruby is to (ab)use the fact that the default value of an optional parameter is just a normal Ruby expression and that local variables in a default value expression are visible inside the method body:
def my_count(find = (find_passed = false; nil))
if find_passed # find was passed
# do something
else
# do something else
end
end
A second possibility is to use some unforgeable unique token as the default value:
undefined = Object.new
define_method(:my_count) do |find = undefined|
if undefined.equal?(find) # find was not passed
# do something
else
# do something else
end
end

Resources