Ruby: Create a symbol searching method - ruby

So this is supposed to be a method that tells you if there is a + symbol before AND after every letter (a-z). Can't figure out why it always returns false. An example of when it should return true is if the input is +d+. It should return false however if the input was +d+d
def SimpleSymbols(str)
idx = 0
while idx < str.length
if str[idx].ord > 96 && str[idx].ord < 123
return false if str[idx - 1] != "+" || str[idx + 1] != "+"
end
idx += 1
end
return true
end
SimpleSymbols(STDIN.gets)

This returns true for me. But I'm wondering if you're getting bit by a line termination issue? Since gets() keeps the line termination, what do these two lines return in your environment:
SimpleSymbols("+d+d")
and
SimpleSymbols("+d+d\n")
? That might provide a pointer to your problem.

When you start the loop and idx is set to 0, the "str[idx - 1]" will equal -1. Setting the statement to check the last character not the first.
return false if str[idx - 1] != "+" || str[idx + 1] != "+"
So "f++d+" will pass as TRUE while it's actually false.
GL

Related

While loop through a string in Ruby

I am entering "901000" as an argument to the following method, and I expect it to remove the last three zeros and return "901".
def zeros(x)
str = x.to_s
i = str.length-1
while str[i] == "0"
str = str.delete(str[i])
i = i-1
end
return str
end
But it returns "91" instead. I cannot understand why my code does not work. Can someone help please?
At first, str is "901000", i = str.length-1 is 5, and str[i] is "0". Hence,
str = str.delete(str[i])
is equivalent to
str = "901000".delete("0")
That gives you "91".
Then, by i = i-1, i becomes 4, and str[i] (which is equivalent to "91"[4]) becomes nil, and str[i] == 0 (which is equivalent to nil == 0) becomes false. So the loop ends, returning the value str, which is "91".
To do what you want, some simple ways are:
"901000".sub(/0+\z/, "") # => "901"
"901000"[/.*?(?=0+\z)/] # => "901"

Ruby - App Academy Practice Exercise About Condition in While Loop

I'm completing App Academy's practice problems for the first coding challenge and have a question regarding the solution provided for #8 nearby az:
# Write a method that takes a string in and returns true if the letter
# "z" appears within three letters **after** an "a". You may assume
# that the string contains only lowercase letters.
#
# Difficulty: medium.
def nearby_az(string)
idx1 = 0
while idx1 < string.length
if string[idx1] != "a"
idx1 += 1
next
end
idx2 = idx1 + 1
while (idx2 < string.length) && (idx2 <= idx1 + 3)
if string[idx2] == "z"
return true
end
idx2 += 1
end
idx1 += 1
end
return false
end
# These are tests to check that your code is working. After writing
# your solution, they should all print true.
puts("\nTests for #nearby_az")
puts("===============================================")
puts('nearby_az("baz") == true: ' + (nearby_az('baz') == true).to_s)
puts('nearby_az("abz") == true: ' + (nearby_az('abz') == true).to_s)
puts('nearby_az("abcz") == true: ' + (nearby_az('abcz') == true).to_s)
puts('nearby_az("a") == false: ' + (nearby_az('a') == false).to_s)
puts('nearby_az("z") == false: ' + (nearby_az('z') == false).to_s)
puts('nearby_az("za") == false: ' + (nearby_az('za') == false).to_s)
puts("===============================================")
In the second while loop:
while (idx2 < string.length) && (idx2 <= idx1 + 3)
why is the condition (idx2 < string.length) necessary? I tested the code without it and got the same results.
Thank you for your assistance.
why is the condition (idx2 < string.length) necessary?
It is not necessary. It's a guard to prevent meaningless iterations of the loop, when idx2 goes out of bounds of the string.
Addressing characters at position beyond string's length will return nil. nil will never be equal to 'z'. So we might as well just stop when we reach the end. This is what the check is for here, optimization.
In other situations, out-of-bounds access often is a serious offense and leads to all kinds of problems (crashes, usually). So it makes sense to always do this.
I know this does not answer your exact question, and other people have answered it already, but as the usual case with programming, there's a better way. You can easily solve this with regular expression
def nearby_az(string)
!(string =~ /a\w{0,3}z/).nil?
end
The regex will match the pattern a, with 0 to 3 characters after it, then a z. If this matches nothing, the =~ operator will return nil, so nil? method returns true, meaning the string does not have nearby az, so we use the ! to invert the boolean, and this method will return false.
If there is a match, =~ returns the index of the first characters, which is not nil, so nil? return false, and we inverted it as before to return true.
Just thought this may be helpful.

why does this ruby code not return anything?

I'm trying to take a number and return a string with dashes around any odd numbers. Also, the string should not begin or end with a dash.
I've written the following but it does not return anything:
def dasherize_number(num)
string = num.to_s
i = 0
while i<string.length
if (string[i].to_i % 2) != 0
string[i] = '-' + string[i] + '-'
end
i += 1
end
if string[0] == '-'
string.pop(1)
end
if (string.length - 1) == '-'
string.pop(1)
end
string
end
It appears to be looping infinitely if I understand correctly; the console shows no output and doesn't allow me to do anything else unless I refresh. I've reviewed the code by each character, but I can't figure where it goes wrong.
There were a lot of logical issues in your code.
here's something that might just work for you
def dasherize_number(num)
string = num.to_s
str_len = string.length
i = 0
while i < str_len
next if string[i] == '-'
if (string[i].to_i % 2) != 0
string[i] = '-' + string[i] + '-'
str_len = string.length
i += 3
else
i += 1
end
end
if string[0] == '-'
string = string[1..-1]
end
if (string[string.length - 1]) == '-'
string = string[0..-2]
end
string.gsub('--', '-')
end
Explaination
Firstly, you had this condition in your while loop i < string.length
Which wouldn't work, because the length of the string keeps changing. So i've used a variable to store the value and update the variable if the string is updated.
If the string is updated, we can be sure that we can skip the next two indexes.
eg: number inputed -> 122
then after first iteration the string would be -1-22
so we don't want to run the same condition for the next index because, that would be 1 again, hence the infinite loop. (Hope you get the idea)
pop wouldn't work on string, just because we can access characters using indexes like for arrays, we can't use pop for strings.
To make sure there are no consecutive dashes, i've used gsub to replace them with single dash.
The problem seems to be in this part of the code:
while i<string.length
if (string[i].to_i % 2) != 0
string[i] = '-' + string[i] + '-'
end
i += 1
end
If your string contains odd number it increases its length by 2 more chars (2x-), but incrementing it by 1 (i+=1).
Assign initial string length to a var and check its length in the while loop.
string_length = string.length
while i < string_length
if ((string[i].to_i % 2) != 0)
string[i] = '-' + string[i] + '-'
end
i += 1
end

Ruby longest palindrome - why does a slight modification in a while loop break my code?

I'm trying to solve this Ruby problem and I can't figure out why having a minor while loop difference renders one test false: longest_palindrome("abba") outputs "bb" instead of "abba", which is false. I can only solve it with for and while loops, so please no advanced methods. It's easier to highlight the difference in the code block (first one is the working solution, second is mine. Also assume the palindrome? method is already defined):
def longest_palindrome(string)
best_palindrome = nil
idx1 = 0
***while idx1 < string.length
length = 1
while (idx1 + length) <= string.length
substring = string.slice(idx1, length)***
if palindrome?(substring) && (best_palindrome == nil || substring.length > best_palindrome.length)
best_palindrome = substring
end
length += 1
end
idx1 += 1
end
return best_palindrome
end
def longest_palindrome(string)
longest = nil
i = 0
***while i < string.length
i2 = 1
while i2 < string.length***
if palindrome?(string.slice(i, i2)) == true && (longest == nil || string.slice(i, i2).length > longest.length)
longest = string.slice(i, i2)
end
i2 += 1
end
i += 1
end
return longest
end
This part of your code...
while i2 < string.length
... means you're never checking the maximum possible length.
"abba".slice(0,4) is the entire string, but you only ever go up to "abba".slice(0,3) which is "abb".
So you never test the entire string.
Change the line to...
while i2 <= string.length
...and it should be ok.

comparison of string with nil failed

Im trying to write a loop that counts the number of capitals in a word. When I run this code I get an error stating that nil can't be compared to a string. I don't understand this because I am comparing a a string to a string?
passwort = gets.chomp
i = 0
capitals = 0
while(i < (passw0ort.length) + 1) do
if('A' <= passwort[i] && passwort[i] <= 'Z')
capitals = capitals + 1
else
end
i = i + 1
end
Greetings Patrick
The reason why you are getting nil is that you are counting more elements than there are.
i<(passw0ort.length)+1 should be i<(passw0ort.length), because .length returns a number bigger than zero, if the string is not empty, while arrays are zero-indexed. The last time the loop runs, you try to access an element after the string, which does not exist.
So on your last loop iteration, you are comparing a string to nil, which results in this error.
Just use scan method of String class to filter out only capital letters
capitals = passwort.scan(/[A-Z]/).count
Your loop is too "long":
passwort = "test"
i = 0
while i < passwort.length + 1
puts "passwort[#{i}] == #{passwort[i].inspect}"
i = i + 1
end
Output:
passwort[0] == "t"
passwort[1] == "e"
passwort[2] == "s"
passwort[3] == "t"
passwort[4] == nil # <- this is your nil error
You can fix this by changing i < passwort.length + 1 to i < passwort.length.
However, instead of using temporary variables and while loops, you can use String#chars to get the string's characters as an array and Array#count to count the ones satisfying your condition:
passwort = "123fooBAR"
passwort.chars.count { |char| 'A' <= char && char <= 'Z' }
#=> 3
Or simply String#count:
passwort.count("A-Z") #=> 3
From the docs:
The sequence c1-c2 means all characters between c1 and c2.

Resources