Ruby method to reverse string input - ruby

I don't get why reversed_string=string[i] + reversed_string puts the last char first. It seems that string[i] would index the first char and not the last. So if the string was "abc" index 0 would be 'a' and not 'c'. Could someone please explain how ruby gets 'c' from index 0? And then, of course, 'b' from index 1? Etc, etc.
Write a method that will take a string as input, and return a new string with the same letters in reverse order.
Difficulty: easy.
def reverse(string)
reversed_string = ""
i = 0
while i < string.length
reversed_string = string[i] + reversed_string
i += 1
end
return reversed_string
end
puts("reverse(\"abc\") == \"cba\": #{reverse("abc") == "cba"}")
puts("reverse(\"a\") == \"a\": #{reverse("a") == "a"}")
puts("reverse(\"\") == \"\": #{reverse("") == ""}")

reversed_string = string[i] + reversed_string
For example, if string is "abc", string[0] is indeed "a", but here it's being put in the beginning of reversed_string, not the end. reversed_string is added up in each iteration as:
"a" + "" #string[0] + "" => "a"
"b" + "a" #string[1] + "a" => "ba"
"c" + "ba" #string[2] + "ba"=> "cba"

Assuming you can't use Ruby Class String's built in Reverse method, you could try the following
def reverse_string(string)
new_string = []
i = string.length-1
while i >= 0
new_string.push(string[i])
i -= 1
end
new_string.join
end
This will create a new string object, but it will reverse the string without using any built-in methods.

As you know, there is a method String#reverse to reverse a string. I understand you are not to use that method, but instead write your own, where the method's argument is the string to be reversed. Others will suggest ways you might do that.
As you are new to Ruby, I thought it might be instructive to show you how you could write a new method for the String class, say, String#my_reverse, that behaves exactly the same as String#reverse. Then for the string "three blind mice", we would have:
"three blind mice".reverse #=> "ecim dnilb eerht"
"three blind mice".my_reverse #=> "ecim dnilb eerht"
To create a method without arguments for the String class, we normally do it like this:
class String
def my_method
...
end
end
We invoke my_method by sending it a receiver that is an instance of the String class. For example, if write:
"three blind mice".my_method
we are sending the method String#my_method to the receiver "three blind mice". Within the definition of the method, the receiver is referred to as self. Here self would be "three blind mice". Similarly, just as the second character (at offset 1) of that string is "three blind mice"[1] #=> "h", self[1] #=> "h". We can check that:
class String
def my_method
puts "I is '#{self}'"
(0...self.size).each { |i| puts self[i] }
end
end
"three blind mice".my_method
would print:
I is 'three blind mice'
t
h
r
e
e
b
...
c
e
The method my_reverse is almost the same:
class String
def my_reverse
sz = self.size
str = ''
(0...sz).each { |i| str << self[sz-1-i] }
str
end
end
"three blind mice".my_reverse
#=> "ecim dnilb eerht"
You can think of self as a variable whose value is the receiver, but unlike a variable, you cannot reassign self to a different object. For example, we can write x = 1; x = 'cat', but we cannot write self = 'cat'. As we have already seen, however, we can change the references self makes to other objects, such as self[1] = 'r'.

Related

How to get the returned value from a method from within the method?

I've been practicing some algorithms with ruby for a while, and I'm wondering if it is possible to catch the returned value from within the method.
the code below is to reverse a string without any kind of reverse method and with few local variables...
def rev(a)
i = -1
a.split("").each do |el|
el[0] = a[i]
i = i + (-1)
end.join
end
Note that the result of the 'each' method is not being assigned to any variable. So, 'each' evaluates to an array with a reversed sequence of characters. At the 'end' (literally) I've just 'called' the method 'join' to glue everything together. The idea is to 'catch' the returned value from all this process and check if is true or false that the reversed string is a palindrome.
If the reversed string is equal to the original one then the word is a palindrome. Ex. "abba", "sexes", "radar"...
for example:
def rev(a)
i = -1
a.split("").each do |el|
el[0] = a[i]
i = i + (-1)
end.join
# catch here the returned value from the code above
# and check if its a palindrome or not. (true or false)
end
Thank you guys! I will be very grateful if anyone could help me figure out this!
Just add == a to see if your reversal matches the original string:
def rev(a)
i = -1
a.split("").each do |el|
el[0] = a[i]
i = i + (-1)
end.join == a
end
puts rev("racecar") # => true
puts rev("racecars") # => false
An easier way to check palindromes (rev could be better named palindrome?) is a == a.reverse since .reverse is essentially what your split/each/join does.
If you want back all the information, you can return an array with both the values:
def rev(a)
i = -1
rev = a.split("").each do |el|
el[0] = a[i]
i = i + (-1)
end.join
[rev, rev == a] # or
# return rev, rev == a
end
p rev("abra") #=> ["arba", false]
p rev("abba") #=> ["abba", true]
You can also return a hash:
{ reverse: rev, palindrome: rev == a}
to get
#=> {:reverse=>"arba", :palindrome=>false}
#=> {:reverse=>"abba", :palindrome=>true}
Here are a couple of other ways you could reverse a string.
#1
def esrever(str)
s = str.dup
(str.size/2).times { |i| s[i], s[-1-i] = s[-1-i], s[i] }
s
end
esrever("abcdefg")
#=> "gfedcba"
esrever("racecar")
#=> "racecar"
This uses parallel assignment (sometimes called multiple assignment).
#2
def esrever(str)
a = str.chars
''.tap { |s| str.size.times { s << a.pop } }
end
esrever("abcdefg")
#=> "gfedcba"
esrever("racecar")
#=> "racecar"
I've used Object#tap merely to avoid creating a local variable initialized to an empty string and then having to make that variable the last line of the method.
With both methods a string str is a palindrome if and only if str == esrever(str).

How to return string with character at given index removed?

I have a string, e.g.
string = 'foo-bar'
and I want a new string which doesn't contain the character at index 3 (the - in the example string), so the results would be "foobar". The former string must not be changed.
This is what I have so far:
new_string = string.dup
new_string.slice!(3)
new_string
#=> "foobar"
string
#=> "foo-bar"
or as a "one-liner":
new_string = string.dup.tap { |s| s.slice!(3) }
#=> "foobar"
But having to dup and maybe tap looks quite cumbersome. Is there a more concise way?
I'm not aware of such a method. You can write it yourself using slice though.
at = 3
string = "abc-def"
def delete_at(string, at)
string.slice(0, at) + string.slice(at + 1, string.length)
end
x = delete_at(string, at) #> "abcdef"
You could also monkey patch it to String class (which I don't like though)
Seems Kernel#sprintfcan solve this fairly simply:
str = "foo-bar"
sprintf("%3.3s%s",str,str[4..-1])
#=> "foobar"
Or simply
sprintf("%s%s",str[0..2],str[4..-1])
#=> "foobar"
Additionally Enumerable methods could help but seem a bit overkill e.g.
str.each_char.with_index.reduce("") do |memo,(s,i)|
i == 3 ? memo : memo << s
end
#=> "foobar"

having trouble with .next method making a casear cipher

I'm not worried about what happens if my key will go past Z right now, or capital letters. All I want is my outcome to be something like. text=abc key=2 and it print "cde". Where am I going wrong?
puts "What would you like to cipher?"
text = gets.chomp
puts " what number key would you like?"
key = gets.chomp.to_i
def casear_cipher(text,key)
ciphered_text = []
text.chars.each do |letter|
ciphered_text = letter
ciphered_text = ciphered_text.next
end
end
puts casear_cipher(text,key)
You're not using the key yet, so it will always just do abc -> bcd. If you're really not concerned about "Z" going to "AA", you can try this:
def cipher(text, key)
text.chars.map { |c| (c.ord + key).chr }.join
end
Since 'Z'.next => 'AA' and 'z'.next #=> 'aa', we can use [-1] to select the last letter.
In the code below we perform next! on each character n times using the times method. next! modifies the character whereas next does not.
def casear_cipher(text, n)
text.chars.map do |c| n.times { c.next! }
c[-1]
end.join
end
p casear_cipher('abc',2) #=> "cde"
p casear_cipher('xyz',2) #=> "zab"
p casear_cipher('ZEBRA',2) #=> "BGDTC"
More information about these methods can be found at http://www.ruby-doc.org/core-2.4.1/

Trying to find vowels of a string using Ruby while loops

def count_vowels(string)
vowels = ["a", "e", "i", "o", "u"]
i = 0
j = 0
count = 0
while i < string.length do
while j < vowels.length do
if string[i] == vowels[j]
count += 1
break
end
j += 1
end
i += 1
end
puts count
end
I'm having trouble spotting where this goes wrong. If this program encounters a consonant, it stops. Also, how would the same problem be solved using the ".each" method?
The problem is that you never reset j to zero.
The first time your outer while loop runs, which is to compare the first character of string to each vowel, j is incremented from 0 (for "a") to 4 (for "u"). The second time the outer loop runs, however, j is already 4, which means it then gets incremented to 5, 6, 7 and on and on. vowels[5], vowels[6], etc. all evaluate to nil, so characters after the first are never counted as vowels.
If you move the j = 0 line inside the outer while loop, your method works correctly.
Your second question, about .each, shows that you're already thinking along the right lines. while is rarely seen in Ruby and .each would definitely be an improvement. As it turns out, you can't call .each on a String (because the String class doesn't include Enumerable), so you have to turn it into an Array of characters first with the String#chars method. With that, your code would look like this:
def count_vowels(string)
chars = string.chars
vowels = ["a", "e", "i", "o", "u"]
count = 0
chars.each do |char|
vowels.each do |vowel|
if char == vowel
count += 1
break
end
end
end
puts count
end
In Ruby, though, we have much better ways to do this sort of thing. One that fits particularly well here is Array#count. It takes a block and evaluates it for each item in the array, then returns the number of items for which the block returned true. Using it we could write a method like this:
def count_vowels(string)
chars = string.chars
vowels = ["a", "e", "i", "o", "u"]
count = chars.count do |char|
is_vowel = false
vowels.each do |vowel|
if char == vowel
is_vowel = true
break
end
end
is_vowel
end
puts count
end
That's not much shorter, though. Another great method we can use is Enumerable#any?. It evaluates the given block for each item in the array and returns true upon finding any item for which the block returns true. Using it makes our code super short, but still readable:
def count_vowels(string)
chars = string.chars
vowels = %w[ a e i o u ]
count = chars.count do |char|
vowels.any? {|vowel| char == vowel }
end
puts count
end
(Here you'll see I threw in another common Ruby idiom, the "percent literal" notation for creating an array: %w[ a e i o u ]. It's a common way to create an array of strings without all of those quotation marks and commas. You can read more about it here.)
Another way to do the same thing would be to use Enumerable#include?, which returns true if the array contains the given item:
def count_vowels(string)
vowels = %w[ a e i o u ]
puts string.chars.count {|char| vowels.include?(char) }
end
...but as it turns out, String has an include? method, too, so we can do this instead:
def count_vowels(string)
puts string.chars.count {|char| "aeiou".include?(char) }
end
Not bad! But I've saved the best for last. Ruby has a great method called String#count:
def count_vowels(string)
puts string.count("aeiou")
end

Why is the splat/unary operator changing the assigned value a when p is called before *a = ""?

To give a little context around how I understand the problem.
Using splat collect on a string sends :to_a or :to_ary to the String
class String
def method_missing method, *args, &block
p method #=> :to_ary
p args #=> []
p block #=> nil
end
end
*b = "b"
So I was thinking that redefining the :to_ary method would be what I'm after.
class String
def to_ary
["to_a"]
end
end
p *a = "a" #=> "a"
p a #=> "a"
*b = "b"
p b #=> ["to_a"]
Now this confuses me to no end.
Printing the result from the *a = "a" changes the value assigned to a?
To demonstrate further
class String
def to_ary
[self.upcase!]
end
end
p *a = "a" #=> "a"
p a #=> "a"
*b = "b"
p b #=> ["B"]
Very interesting question! Ruby takes this expression:
p *a = "a"
and translates it to something like this:
temp = (a = "a")
p *temp
So the first thing that happens is that a gets assigned to "a", and then the result of the assignment expression which is "a" gets splatted and sent to p. Since p's default behaviour when sent multiple arguments is just to iterate over and print each one, you only see "a" appear.
In short, it follows a "assign then splat" order of evaluation. So a gets assigned to "a" before the string gets splatted.
When you don't have a function call however, it is interpreted as something like this:
# *a = "a" gets interpreted as:
temp = "a"
a = *temp
This follows a "splat then assign" order of evaluation. So a gets assigned after the string gets splatted.
You can see what's being received by a function by going like this:
def foo *args
puts args.inspect
end
foo *a = "a" # outputs ["a"]
a # outputs "a"
Hope this clears up what's going on!
In short (thanks to Mark Reed):
p *a = "a" # interpreted as: p(*(a = "a"))
*a = "a" # interpreted as: a = *("a")

Resources