I am writing something in Ruby that needs to compare versions in order to determine whether or not something needs to be updated.
But when I run current_version <=> desired_version and at least one of the versions is frozen, I get:
4: from .../ruby/2.6.0/rubygems/version.rb:344:in `<=>'
3: from .../ruby/2.6.0/rubygems/version.rb:371:in `canonical_segments'
2: from .../ruby/2.6.0/rubygems/version.rb:393:in `_split_segments'
1: from .../ruby/2.6.0/rubygems/version.rb:387:in `_segments'
FrozenError (can't modify frozen Gem::Version)
According to the docs, the source code is this:
def <=>(other)
return unless Gem::Version === other
return 0 if #version == other._version || canonical_segments == other.canonical_segments
lhsegments = _segments
rhsegments = other._segments
lhsize = lhsegments.size
rhsize = rhsegments.size
limit = (lhsize > rhsize ? lhsize : rhsize) - 1
i = 0
while i <= limit
lhs, rhs = lhsegments[i] || 0, rhsegments[i] || 0
i += 1
next if lhs == rhs
return -1 if String === lhs && Numeric === rhs
return 1 if Numeric === lhs && String === rhs
return lhs <=> rhs
end
return 0
end
I don't see why this code would be mutating the state of the Gem. Is there something that I'm missing?
The error tells you where: The <=> method calls canonical_segments, which calls _split_segments, which calls _segments. So that's where the mutation must be happening; not directly in the method you've copied in the post.
More specifically, here's the offending source code:
def canonical_segments
#canonical_segments ||=
_split_segments.map! do |segments|
segments.reverse_each.drop_while {|s| s == 0 }.reverse
end.reduce(&:concat)
end
protected
def _version
#version
end
def _segments
# segments is lazy so it can pick up version values that come from
# old marshaled versions, which don't go through marshal_load.
# since this version object is cached in ##all, its #segments should be frozen
#segments ||= #version.scan(/[0-9]+|[a-z]+/i).map do |s|
/^\d+$/ =~ s ? s.to_i : s
end.freeze
end
def _split_segments
string_start = _segments.index {|s| s.is_a?(String) }
string_segments = segments
numeric_segments = string_segments.slice!(0, string_start || string_segments.size)
return numeric_segments, string_segments
end
I am formatting a phone number, and there can be no single digit after a dash, i.e. 123-555-5555 or 12-34, but not 123-4. There can also be any alpha characters in the answer.
Here is my answer.
class CodeTestException < Exception; end
# driver method
def phone_format(s)
string = s.to_s.gsub(/[^0-9]/, '') # force input to string
string_length = string.length
# ensure that the return type stays consistent and make sure that there isn't
# one digit by itself as specified by the API
return string unless string_length > 2
format_phone_string(string, string_length)
end
private
def format_phone_string(string, string_length)
formatted_string = ""
early_dash = string_length % 3 == 1
skip_dash = false
# start index at 1 for easier comprehension
i = 0
string.each_char do |char|
formatted_string << char
break if i == string_length
i += 1
formatted_string << '-' && skip_dash = true if early_dash && (i == string_length - 2)
formatted_string << '-' if i % 3 == 0 && !skip_dash
end
formatted_string
end
raise CodeTestException unless phone_format("(+1) 888 33x19") == "188-833-19"
raise CodeTestException unless phone_format("555 123 1234") == "555-123-12-34"
raise CodeTestException unless phone_format("(+1)") == '1'
raise CodeTestException unless phone_format(nil) == ""
raise CodeTestException unless phone_format("") == ""
I was told that the answer was not recursion, or scan. I believe O(n) is the best time that I can make. Some other solutions that I found was a higher multiple of linear time. Can anyone beat my solution?
I need help on Writing 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. here's what I have:
def nearby_az(string)
string.downcase!
i = 0
while i < string.length
if (string[i] == "a" && string[i] == "z")
true
else
false
end
end
end
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)
A regular expression would be the best for this. Try
def nearby_az(string)
(string =~ /a.{0,2}z/) != nil
end
EDIT:
as per the "if statement required requirement" :)
def nearby_az(string)
if (string =~ /a.{0,2}z/) != nil
return true
end
return false
end
The way this code works is it searches the input string for an "a". After that, the period indicates that you can have any character. After that you have {0,2} which is a modifier of the period indicating you can have 0 to 2 of any character. After this you must have a "z", and this fulfills your must have a z within 3 characters of an "a".
I've saved this regex to regex101 here so you can try various inputs as well as change the regular expression around to understand it better.
To fix you code you need to:
increment i at the end of the loop.
search the z letter within the 3 next letters
return true when condition is met
return false when getting out of the loop
Here is what it should look like:
def nearby_az(string)
string.downcase!
i = 0
while i < string.length do
return true if string[i] == "a" && string[i+1,3].include?(?z)
i+=1
end
return false
end
This method is supposed to take a string and detect if the brackets '(' '{' '[' in the string are closing properly with the corresponding (opposite) brackets.
First, is there a more elegant, compact way to write this bit without using all the "or"s (||):
split_array.each do |i|
if (i == "{" || i == "(" || i == "[")
left.push(i)
else (i == "}" || i == ")" || i == "]")
right.push(i)
end
end
My second question is, is this code terrible (see below)? It seems I should be able to write this in way fewer lines, but logically, I haven't come up with another solution (yet.)
The code works for most tests, but it returns false for this test (see all driver tests at bottom): p valid_string?("[ ( text ) {} ]") == true
Any critique would be greatly appreciated!
(also, if there is a better section to post this, please let me know)
Thanks!
def valid_string?(string)
opposites = { "[" => "]", "{" => "}", "(" => ")", "]" => "[", "}" => "{", ")" => "(" }
left = Array.new
right = Array.new
return_val = true
split_array = string.split(//)
split_array.delete_if { |e| e.match(/\s/) }
split_array.each do |i|
if (i == "{" || i == "(" || i == "[")
left.push(i)
else (i == "}" || i == ")" || i == "]")
right.push(i)
end
end
# p left
# p right
left.each_index do |i|
if left[i] != opposites[right[i]]
return_val = false
end
end
return_val
end
p valid_string?("[ ] } ]") == false
p valid_string?("[ ]") == true
p valid_string?("[ ") == false
p valid_string?("[ ( text ) {} ]") == true
p valid_string?("[ ( text { ) } ]") == false
p valid_string?("[ (] {}") == false
p valid_string?("[ ( ) ") == false
-------Updated: After trying some different approaches, my refactor is this:-----------
def valid_string?(str)
mirrored = { "[" => "]", "{" => "}", "(" => ")" }
open_brackets = Array.new
split_str_array = str.split("")
split_str_array.each do |bracket|
if bracket.match(/[\[|\{|\(]/) then open_brackets.push(bracket)
elsif bracket.match(/[\]|\}|\)]/)
return false if mirrored[open_brackets.pop] != bracket
end
end
open_brackets.empty?
end
My approach is as below :
def valid_string?(string)
open_paren = ['[','{','(']
close_paren = [']','}',')']
open_close_hash = {"]"=>"[", "}"=>"{", ")"=>"("}
stack = []
regex = Regexp.union(close_paren+open_paren)
string.scan(regex).each do |char|
if open_paren.include? char
stack.push(char)
elsif close_paren.include? char
pop_val = stack.pop
return false if pop_val != open_close_hash[char]
end
end
open_paren.none? { |paren| stack.include? paren }
end
valid_string?("[ ] } ]") # => false
valid_string?("[ ]") # => true
valid_string?("[ ") # => false
valid_string?("[ (] {}") # => false
valid_string?("[ ( ) ") # => false
valid_string?("[ ( text { ) } ]") # => false
valid_string?("[ ( text ) {} ]") # => true
Algorithm :
Declare a character stack S.
Now traverse the expression string exp.
If the current character is a starting bracket (‘(‘ or ‘{‘ or ‘[') then push it to stack.
If the current character is a closing bracket (')' or '}' or ']') then pop from stack and if the popped character is the matching starting bracket then fine else parenthesis are not balanced.
After complete traversal, if there is some starting bracket left in
stack then “not balanced”
The shortest regex solution is probably:
def valid_string? orig
str = orig.dup
re = /\([^\[\](){}]*\)|\[[^\[\](){}]*\]|\{[^\[\](){}]*\}/
str[re] = '' while str[re]
!str[/[\[\](){}]/]
end
How about:
class Brackets
def self.paired?(s)
stack = []
brackets = { '{' => '}', '[' => ']', '(' => ')' }
s.each_char do |char|
if brackets.key?(char)
stack.push(char)
elsif brackets.values.include?(char)
return false if brackets.key(char) != stack.pop
end
end
stack.empty?
end
end
Brackets.paired?("[ ] } ]") # => false
Brackets.paired?("[ ]") # => true
Brackets.paired?("[ ") # => false
Brackets.paired?("[ (] {}") # => false
Brackets.paired?("[ ( ) ") # => false
Brackets.paired?("[ ( text { ) } ]") # => false
Brackets.paired?("[ ( text ) {} ]") # => true
You can try this approach:
def balanced_brackets?(string)
# your code here
stack = []
opening_bracket = ['{','[', '(']
closing_bracket = ['}', ']', ')']
string.chars.each do |char|
if opening_bracket.include?(char)
stack << char
elsif closing_bracket.include?(char)
value = stack.pop
return false if opening_bracket.index(value) != closing_bracket.index(char)
end
end
stack.empty?
end
if you want to understand pseudo code try this link from coursera (starts at 0:56).
Another way:
s = str.gsub(/[^\{\}\[\]\(\)]/, '')
while s.gsub!(/\{\}|\[\]|\(\)/, ''); end
s.empty?
Ex 1
str = "(a ()bb [cc{cb (vv) x} c ]ss) "
s = str.gsub(/[^\{\}\[\]\(\)]/, '') #=> "(()[{()}])"
while s.gsub!(/\{\}|\[\]|\(\)/, '') do; end
s => "([{}])" => "([])" => "()" => "" gsub!() => nil
s.empty? #=> true
Ex 2
str = "(a ()bb [cc{cb (vv) x] c }ss) "
s = str.gsub(/[^\{\}\[\]\(\)]/, '') #=> "(()[{()]})"
while s.gsub!(/\{\}|\[\]|\(\)/, '') do; end
s => "([{]})" gsub!() => nil
s.empty? #=> false
This should provide the same functionality
def valid_string?(string)
#assume validity
#valid = true
#empty array will be populated inside the loop
#open_characters = []
#set up a hash to translate the open character to a closing character
translate_open_closed = {"{" => "}","["=>"]","("=>")"}
#create an array from the string loop through each item
string.split('').each do |e|
#adding it to the open_characters array if it is an opening character
#open_characters << e if e=~ /[\[\{\(]/
#if it is a closing character then translate the last open_character to
#a closing character and compare them to make sure characters are closed in order
#the result of this comparison is applied to the valid variable
#valid &= e == translate_open_closed[#open_characters.pop] if e=~ /[\]\}\)]/
end
#return validity and make sure all open characters have been matched
#valid &= #open_characters.empty?
end
You could also do this with inject but it would be a bit less transparent.
I was given this as part of a simulated interview coding challenge. In my case, there was also a parens map passed in { "(" => ")", "[" => "]" }, meaning types of parentheses could vary.
def balanced_parens(string, parens_map)
# where we throw opening parens
opening_parens = []
i = 0
while i < string.length
# if current index is opening paren add to array
if parens_map.keys.include? string[i]
opening_parens << string[i]
# if current index is closing paren, remove last item from opening_array
elsif parens_map.values.include? string[i]
popped_paren = opening_parens.pop
# checking that closing parens at current index is a match for last open parens in opening_array
return false if string[i] != parens_map[popped_paren]
end
i += 1
end
# if opening_parens array is empty, all parens have been matched (&& value = true)
opening_parens.empty?
end
def valid_string?(exp)
return false if exp.size % 2 != 0
curly = "{}"
square = "[]"
parenthesis = "()"
emptystr = ""
loop do
old_exp = exp
exp = exp.sub(curly, emptystr)
break if exp == emptystr
exp = exp.sub(square, emptystr)
break if exp == emptystr
exp = exp.sub(parenthesis, emptystr)
break if exp == emptystr || exp == old_exp
end
exp == emptystr
end
I'm trying to create a method that reorders all the vowels in a string as efficiently as possible. For example:
"you are incredible." returns "yae ere incridoblu."
This is what I've come up with:
def vowel_orderer(string)
vowels = ["a","e","i","o","u"]
ordered_vowels = string.scan(/[aeiou]/).sort
ordered_string = []
i = 0
j = 0
while i < string.length
if vowels.include?(string[i])
ordered_string << ordered_vowels[j]
j += 1
else
ordered_string << string[i] unless vowels.include?(string[i])
end
i += 1
end
puts ordered_string.join
end
I feel like there should be a shorter way to accomplish this, using something like gsub?
string = "you are incredible."
ordered_vowels = string.scan(/[aeiou]/).sort
string.gsub(/[aeiou]/){ordered_vowels.shift} # => "yae ere incridoblu."
def vowel_orderer_gsub(string)
ordered_vowels = string.scan(/[aeiou]/).sort { |x,y| y <=> x }
puts string.gsub(/[aeiou]/) { |match| ordered_vowels.pop }
end
1.9.3p448 :128 > vowel_orderer( "you are incredible." )
# yae ere incridoblu.
# => nil
1.9.3p448 :129 > vowel_orderer_gsub( "you are incredible." )
# yae ere incridoblu.