The question says to write a function that takes a string of braces, and determines if the order of the braces is valid. It should return true if the string is valid, and false if it's invalid.
All input strings will be nonempty, and will only consist of parentheses, brackets and curly braces: ()[]{}.
The tests should give these:
"(){}[]" => True
"([{}])" => True
"(}" => False
"[(])" => False
"[({})](]" => False
The code I've written is:
def validBraces(braces)
revBraces = braces.reverse
arr = []
i = -1
loop do
i += 1
if braces[i] == "(" && revBraces[i] == ")"
arr << 1
else
arr << 0
end
if braces[i] == ")" && revBraces[i] == "("
arr << 1
else
arr << 0
end
if braces[i] == "{" && revBraces[i] == "}"
arr << 1
else
arr << 0
end
if braces[i] == "}" && revBraces[i] == "{"
arr << 1
else
arr << 0
end
if braces[i] == "[" && revBraces[i] == "]"
arr << 1
else
arr << 0
end
if braces[i] == "]" && revBraces[i] == "["
arr << 1
else
arr << 0
end
break if i <= braces.length
end
if arr.include? 0
puts false
else
puts true
end
end
I cant tell where I've gone wrong and why it doesn't work.
I would use a stack to solve this problem. Add found opening braces to the stack and for closing braces compare them with the brace from the top of the stack.
def validBraces(braces)
matches = { '(' => ')', '{' => '}', '[' => ']' }
stack = []
braces.chars.each do |char|
case char
when *matches.keys
stack.push(char)
when *matches.values
return false if matches[stack.pop] != char
else
raise ArgumentError, "Unexpected char `#{char}`"
end
end
stack.empty?
end
validBraces("(){}[]") #=> true
validBraces("([{}])") #=> true
validBraces("(}") #=> false
validBraces("[(])") #=> false
validBraces("[({})](]") #=> false
validBraces("[A]") #=> Unexpected char `A` (ArgumentError)
Or following OOP:
class Balancer
MATCHES = { '(' => ')', '{' => '}', '[' => ']' }
OPENING = MATCHES.keys
CLOSING = MATCHES.values
def initialize(string)
#chars = string.chars
end
def valid?
stack = []
chars.each do |char|
case char
when *OPENING
stack.push(char)
when *CLOSING
return false if MATCHES[stack.pop] != char
else
raise ArgumentError, "Unexpected char `#{char}`"
end
end
stack.empty?
end
private
attr_reader :chars
end
Balancer.new("(){}[]").valid? #=> true
Balancer.new("([{}])").valid? #=> true
Balancer.new("(}").valid? #=> false
Balancer.new("[(])").valid? #=> false
Balancer.new("[({})](]").valid? #=> false
Balancer.new("[A]").valid? #=> Unexpected char `A` (ArgumentError)
The logics is wrong.
You take braces and reverseBraces. And you don't think of cases where the braces are nested. But even when not nested the way you go through the string is not right. Better have a dictionary of "(", "{", "[", ")", "}", "]" and count their frequency (make the closing brackets' counts negative) and see whether they cancel themselves out by building a sum - it should be 0.
def freqs(l)
h = Hash.new(0)
l.each do |e|
h[e] += 1
end
return h
end
def balanced(braces)
fr = freqs(braces.split(""))
return fr["("] - fr[")"] == 0 && fr["{"] - fr["}"] == 0 && fr["["] - fr["]"] == 0
end
One way to do that is to successively remove strings '()', '[]' and '{}' until either the string is empty, in which case the string is found to be balanced, or at some iteration no string '()', '[]' or '{}' is found, in which case the string is found to be unbalanced.
RGX = /\(\)|\[\]|{}/
def valid_braces(str)
s = str.dup
until s.empty?
return false if s.gsub!(RGX, '').nil?
end
true
end
valid_braces '(){}[]' #=> true
valid_braces '([{}])' #=> true
valid_braces '([[{[]}]{}]())' #=> true
valid_braces '(}' #=> false
valid_braces '[(])' #=> false
valid_braces '[({})](]' #=> false
valid_braces('[A]') #=> false
The regular expression reads, "match '()' or '[]' or '{}'". Note that String#gsub! returns nil if no substitutions are performed.
I can insert a puts statement to illustrate what is happening at each iteration.
def valid_braces(str)
s = str.dup
until s.empty?
puts s
return false if s.gsub!(RGX, '').nil?
end
true
end
valid_braces '([[{[]}]{}]())'
([[{[]}]{}]()) full string
([[{}]]) '[]', '{}' and '()' removed
([[]]) '{}' removed
([]) '[]' removed
() '[]' removed
'()' removed
#=> true as `str` is empty
This is probably not as efficient as using a stack, as #spickermann has done. I would implement that a little differently.
CLOSE_TO_OPEN = { ')'=>'(', ']'=>'[', '}'=>'{' }
OPENS = CLOSE_TO_OPEN.values
#=> ["(", "[", "{"]
def valid_braces(str)
stack = []
str.each_char do |c|
if OPENS.include?(c)
stack << c
else
return false unless CLOSE_TO_OPEN[c] == stack.pop
end
end
return stack.empty?
end
valid_braces '(){}[]' #=> true
valid_braces '([{}])' #=> true
valid_braces '([[{[]}]{}]())' #=> true
valid_braces '(}' #=> false
valid_braces '[(])' #=> false
valid_braces '[({})](]' #=> false
valid_braces('[A]') #=> false
We can add puts statements to see what is happening.
def valid_braces(str)
stack = []
str.each_char do |c|
print "stack = #{stack}, c = '#{c}', "
if OPENS.include?(c)
stack << c
puts "pushed '#{c}'"
else
return false unless CLOSE_TO_OPEN[c] == stack.pop
puts "popped '#{CLOSE_TO_OPEN[c]}'"
end
end
puts "stack at end = #{stack}"
return stack.empty?
end
valid_braces '([[{[]}]{}]())'
stack = [], c = '(', pushed '('
stack = ["("], c = '[', pushed '['
stack = ["(", "["], c = '[', pushed '['
stack = ["(", "[", "["], c = '{', pushed '{'
stack = ["(", "[", "[", "{"], c = '[', pushed '['
stack = ["(", "[", "[", "{", "["], c = ']', popped '['
stack = ["(", "[", "[", "{"], c = '}', popped '{'
stack = ["(", "[", "["], c = ']', popped '['
stack = ["(", "["], c = '{', pushed '{'
stack = ["(", "[", "{"], c = '}', popped '{'
stack = ["(", "["], c = ']', popped '['
stack = ["("], c = '(', pushed '('
stack = ["(", "("], c = ')', popped '('
stack = ["("], c = ')', popped '('
stack at end = []
#=> true
Related
def encrypt(string)
alphabet = ("a".."b").to_a
result = ""
idx = 0
while idx < string.length
character = string[idx]
if character == " "
result += " "
else
n = alphabet.index(character)
n_plus = (n + 1) % alphabet.length
result += alphabet[n_plus]
end
idx += 1
end
return result
end
puts encrypt("abc")
puts encrypt("xyz")
I'm trying to get "abc" to print out "bcd" and "xyz" to print "yza". I want to advance the letter forward by 1. Can someone point me to the right direction?
All I had to do was change your alphabet array to go from a to z, not a to b, and it works fine.
def encrypt(string)
alphabet = ("a".."z").to_a
result = ""
idx = 0
while idx < string.length
character = string[idx]
if character == " "
result += " "
else
n = alphabet.index(character)
n_plus = (n + 1) % alphabet.length
result += alphabet[n_plus]
end
idx += 1
end
return result
end
puts encrypt("abc")
puts encrypt("xyz")
Another way to solve the issue, that I think is simpler, personally, is to use String#tr:
ALPHA = ('a'..'z').to_a.join #=> "abcdefghijklmnopqrstuvwxyz"
BMQIB = ('a'..'z').to_a.rotate(1).join #=> "bcdefghijklmnopqrstuvwxyza"
def encrypt(str)
str.tr(ALPHA,BMQIB)
end
def decrypt(str)
str.tr(BMQIB,ALPHA)
end
encrypt('pizza') #=> "qjaab"
decrypt('qjaab') #=> "pizza"
Alternatively if you don't want to take up that memory storing the alphabet you could use character codings and then just use arithmetic operations on them to shift the letters:
def encrypt(string)
result = ""
idx = 0
while idx < string.length
result += (string[idx].ord == 32 ? (string[idx].chr) : (string[idx].ord+1).chr)
idx += 1
end
result
end
Other strange thing about ruby is that you do not need to explicitly return something at the end of the method body. It just returns the last thing by default. This is considered good style amongst ruby folks.
Your question has been answered, so here are a couple of more Ruby-like ways of doing that.
Use String#gsub with a hash
CODE_MAP = ('a'..'z').each_with_object({}) { |c,h| h[c] = c < 'z' ? c.next : 'a' }
#=> {"a"=>"b", "b"=>"c",..., "y"=>"z", "z"=>"a"}
DECODE_MAP = CODE_MAP.invert
#=> {"b"=>"a", "c"=>"b",..., "z"=>"y", "a"=>"z"}
def encrypt(word)
word.gsub(/./, CODE_MAP)
end
def decrypt(word)
word.gsub(/./, DECODE_MAP)
end
encrypt('pizza')
#=> "qjaab"
decrypt('qjaab')
#=> "pizza"
Use String#gsub with Array#rotate
LETTERS = ('a'..'z').to_a
#=> ["a", "b", ..., "z"]
def encrypt(word)
word.gsub(/./) { |c| LETTERS.rotate[LETTERS.index(c)] }
end
def decrypt(word)
word.gsub(/./) { |c| LETTERS.rotate(-1)[LETTERS.index(c)] }
end
encrypt('pizza')
#=> "qjaab"
decrypt('qjaab')
#=> "pizza"
I need to turn a string that contains (among other letters) a sequence of 3 letters consisting of a non-vowel, an "o" and the same non-vowel again
into
a string that contains (other letters and) only that non-vowel.
like
"kok" #=> "k"
"mom" #=> "m"
"lol" #=> "l"
"kokmomloljk" #=> "kmljk"
I would like my code to be as compact as possible and only use string methods.
str.each_char { | i | if i == /[^aeiou]/ and i == str[i.index + 2] and str[i.index + 1] == "o"
str = str.delete(str.slice(str[i.index + 1], 2))
end
}
The output is the unchanged string. Thank you in advance.
R = /
([^aeiou]) # match a consonant in capture group 1
o # match an 'o'
\1 # match the contents of capture group 1
/x # free-spacing regex definition mode
def my_method(str)
str.gsub(R,'\1')
end
my_method "my dog kok has fleas"
#=> "my dog k has fleas"
my_method "much momentum"
#=> "much mentum"
my_method "'plolly' is not a word"
#=> "'plly' is not a word"
my_method "abkokcmomloljkde"
#=> "abkcmljkde"
my_method "bub"
#=> "bub"
I was wondering if you could do this in a more functional non-destructieve way with map and, yes you can, but not more compact than the other answers:
str = "iskakmomlol"
VOWEL = /[aeiou]/
RESONANT = /[^aeiou]/
str.chars.map.with_index { |c, i|
prevb, prev, nxt, scnd = str[i - 2], str[i - 1], str[i + 1], str[i + 2]
if i > str.length - 1 || i == 0 then c
elsif c =~ RESONANT && nxt =~ VOWEL && c == scnd then c
elsif c =~ VOWEL && prev =~ RESONANT && nxt =~ RESONANT
elsif c =~ RESONANT && prevb == c && prev =~ VOWEL
else c
end
}.compact.join # "iskmljk"
Actually, this can be shorter:
R = /([^aeiou])[aeiou]\1/
str.chars.map.with_index { |c, i|
c unless str[i-1..i+1][R] || str[i-2..i][R]
}.compact.join # "iskmljk"
i figured since the "o" is a fix character in the sequence to be accepted, i can just go through with a count var and see if the characters next to it are equal to each other and non-vowels. also i found that slice can be also passed two parameters so that it slices starting at an offset and stopping after the given length.
index = 0
while index < str.length
index = index + 1
if str[index] == "o" and str[index-1] == str[index+1] and str[index-1] != /[^aeiou]/
str.slice!(index, 2)
I'm trying to evaluate whether every space value is equal to either "X" or "O". Can I use each to do that? Is there a better way?
if #spaces.each {|x| x=="O" || x=="X"}
#winner = true
puts "It's a tie!"
break
end
ETA: all? doesn't seem to be working, either. I got this error referring to the line with the block:
tictac.rb:47: syntax error, unexpected '|', expecting '}'
{|x| x=="O" || x=="X"}
^
tictac.rb:47: syntax error, unexpected '}', expecting keyword_end
Here is the entire TicTacToe I'm working on:
class Board
def initialize
#spaces = [1, 2, 3, 4, 5, 6, 7, 8, 9]
self.print_board
#winner = false
#turn = "X"
end
def print_board
puts
puts " " + #spaces[0].to_s + " " + #spaces[1].to_s + " " + #spaces[2].to_s
puts " " + #spaces[3].to_s + " " + #spaces[4].to_s + " " + #spaces[5].to_s
puts " " + #spaces[6].to_s + " " + #spaces[7].to_s + " " + #spaces[8].to_s
puts
end
def mark(turn, move)
space = #spaces.index(move)
#spaces[space] = turn
self.print_board
end
def play
while #winner == false
puts "where would you like to put your #{#turn}?"
move = gets.chomp.to_i
self.mark(#turn, move)
if
#spaces[0] == #turn && #spaces[1] == #turn && #spaces[2] == #turn ||
#spaces[3] == #turn && #spaces[4] == #turn && #spaces[5] == #turn ||
#spaces[6] == #turn && #spaces[7] == #turn && #spaces[8] == #turn ||
#spaces[0] == #turn && #spaces[3] == #turn && #spaces[6] == #turn ||
#spaces[1] == #turn && #spaces[4] == #turn && #spaces[7] == #turn ||
#spaces[2] == #turn && #spaces[5] == #turn && #spaces[8] == #turn ||
#spaces[0] == #turn && #spaces[4] == #turn && #spaces[8] == #turn ||
#spaces[2] == #turn && #spaces[4] == #turn && #spaces[6] == #turn
#winner = true
puts "#{#turn} is the winner!"
break
elsif #spaces.all?
{|x| x=="O" || x=="X"}
#winner = true
puts "It's a tie!"
break
else
#turn == "X"? #turn = "O" : #turn = "X"
end
end
end
end
game = Board.new
game.play
I marked an answer that worked, and I guess all? was better than my each, but I'm still curious why changing it to all? didn't seem to work.
#spaces.all? { |x| x=="O" || x=="X" }
You cannot put a newline between a method call and its block. That makes Ruby think you're calling the method without a block and defining a Hash.
All elements of #spaces will be "O" or "X" if
(#spaces - ["O", "X"]).empty?
is true.
#spaces = ["O", "X", "X", "O"]
(#spaces - ["O", "X"]).empty?
#=> true
#spaces = ["O", "X", "X", "O", "cat"]
(#spaces - ["O", "X"]).empty?
#=> false
Chain Array#Uniq and Array#Size
Refactor your check into a method which takes an array of box entries (e.g. "X" or "O"), or a variable holding such an array. The method will return true if all letters in the array are the same, because there will only be one unique character. For example:
def check_spaces letters
true if letters.uniq.size == 1
end
check_spaces %w[X X X] #=> true
check_spaces %w[O O O] #=> true
check_spaces %w[O X O] #=> nil
If you do this, you can even assign the result of #check_spaces directly to #winner, like so:
#winner = check_spaces %w[X X X]
#=> true
#winner = check_spaces %w[X O X]
#=> nil
This is the code I am trying to run which counts the frequency of chars in the array passed to it, and this is the error I am getting frequency_string': undefined methodeach' for "a":String (NoMethodError). I'm not entirely sure what is wrong because this is code that should work and was given to me. Very new to Ruby, and have spent hours trying to solve this on my own. Will take any help I can get.
def frequency_string(chars)
frequencies = {}
result = ""
chars.each do |char|
if frequencies.has_key? char then
frequencies = 1
else
frequencies[char] += 1
end
end
frequencies.each do |char, freq|
result << char << freq
end
return result
end
data1 = ['a', 'b', 'c', 'c', 'd', 'a', '#', 'b', '#', 'a']
puts "Data 1: " + data1.join(' ')
puts
puts frequency_string(data1)
You have two problems:
the line frequencies = 1 should be frequencies[char] = 1. As it stands, you are converting a hash to the number 1.
you have frequency[char] = 1 and frequency[char] = 1 reversed.
Here is your corrected code:
def frequency_string(chars)
frequencies = {}
result = ""
chars.each do |char|
if frequencies.has_key? char then
frequencies[char] += 1
else
frequencies[char] = 1
end
end
frequencies.each do |char, freq|
result << char << freq.to_s
end
result
end
data1 = ['a', 'b', 'c', 'c', 'd', 'a', '#', 'b', '#', 'a']
puts "Data 1: " + data1.join(' ')
puts
puts frequency_string(data1)
#=> a3b2c2d1#2
As you gain experience with Ruby you will find that the language allows you to write operations such as this one with very little code. Here's one way:
def frequency_string(chars)
chars.each_with_object({}) { |char,hash|
hash[char] = (hash[char] ||= 0) + 1 }.to_a.flatten.join
end
puts frequency_string(data1)
#=> a3b2c2d1#2
This may look complex, but it won't be long before you find this to be more natural and straightforward than what you have written. You don't have to worry about this now; I just wanted to give you a taste of what you can look forward to.
chars.each do |char|
if frequencies.has_key? char then
frequencies = 1
else
frequencies[char] += 1
end
end
should be:
chars.each do |char|
if frequencies.has_key? char
frequencies[char] += 1
else
frequencies[char] = 1
end
end
or
chars.each { |char| frequencies.has_key?(char) ? frequencies[char] += 1 : frequencies[char] = 1 }
You also can make it like this:
group_by usage
irb(main):038:0> data1.group_by { |e| e }
=> {"a"=>["a", "a", "a"], "b"=>["b", "b"], "c"=>["c", "c"], "d"=>["d"], "#"=>["#", "#"]}
irb(main):041:0> data1.group_by { |e| data1.count(e) }
=> {3=>["a", "a", "a"], 2=>["b", "c", "c", "#", "b", "#"], 1=>["d"]} # it can be an optional if it can meet your demands
after improvement:
irb(main):053:0> result = ''
=> ""
irb(main):054:0> data1.group_by { |e| e }.each { |e| result += e[0] + e[1].count.to_s }
=> {"a"=>["a", "a", "a"], "b"=>["b", "b"], "c"=>["c", "c"], "d"=>["d"], "#"=>["#", "#"]}
irb(main):055:0> result
=> "a3b2c2d1#2"
or use inject
irb(main):040:0> data1.group_by { |e| e }.inject('') {|r, e| r + e[0] + e[1].count.to_s}
=> "a3b2c2d1#2"
I needed to write a program that converts normal speech into pig latin. I wrote
def translate(*string)
word = []
word_string = []
s = []
i = 0
a = nil
#enters input from user into individual arrays spots
#removes spaces
string[0].scan(/\w+/).each { |x| word << x }
#goes through each word and does operation
while i < word.count
word[i].scan(/./) { |x| word_string << x }
#checks if starts with vowel
if word_string[0].include?("aeiou" || "AEIOU")
word_string = word_string << "ay"
s[i] << word_string.join('')
#checks if starts with Qu or qu
elsif word_string[0] + word_string[1] == "qu" || word_string[0] + word_string[1] == "Qu"
word_string.delete_at(0) && word_string.delete_at(1)
word_string << "quay"
s[i] = word_string.join('')
#checks if starts with 3 consonants
unless (word_string[0] + word_string[1] + word_string[2]).include?("aeiou")
a = word_string[0] + word_string[1] + word_string[2]
word_string.delete_at(0) && word_string.delete_at(1) && word_string.delete_at(2)
word_string << (a + "ay")
s[i] = word_string.join('')
a = nil
#checks if starts with 2 consonants
unless (word_string[0] + word_string[1]).include?("aeiou")
a = word_string[0] + word_string[1]
word_string.delete_at(0) && word_string.delete_at(1)
word_string << (a + "ay")
s[i] = word_string.join('')
a = nil
#check if starts with 1 consonants
else
a = word_string[0]
word_string.delete_at(0)
word_string << (a + "ay")
s[i] = word_string.join('')
a = nil
end
i += 1
end
s.join(" ")
end
It returned to me a error saying
pig_latin.rb:58: syntax error, unexpected $end, expecting kEND
I looked into the error, it means that I either missed a end somewhere or I have one too many, but I am unable to find it. I have a def end, a while end and a if end so the issue isn't there. I thought it might be somewhere in the first few linkes where I wrote the scans to sort the text originally but it doesn't seem like its there either. I need another pair of eyes to take a look, I can't find it. Also if there would be a better way to write this, please let me know.
This is more how the code should look, if it was written in more of a Ruby way:
def translate(string)
pig_latin = []
words = string.split(/\W+/)
words.each do |word|
case word
when /^[aeiou]/i
pig_latin << (word + "ay")
when /^qu/i
word << word[0,2] << 'ay'
pig_latin << word[2 .. -1]
when /^[^aeiou]{3}/i
word << word[0,3] << 'ay'
pig_latin << word[3..-1]
when /^[^aeiou]{2}/i
word << word[0, 2] << 'ay'
pig_latin << word[2 .. -1]
else
word << word[0] << 'ay'
pig_latin << word[1 .. -1]
end
end
pig_latin.join(' ')
end
puts translate('the rain in spain stays mainly on the plain')
=> ethay ainray inay ainspay aysstay ainlymay onay ethay ainplay
I'd have checked for consonants differently.
How it works is left to the reader to figure out. If this was a homework assignment, take the time to figure out how it works, because knowing what it does is important. Copying other people's work... well, this is on the internet now so anyone can search and find it, so don't plagiarize.