Error comparing strings? - ruby

When I run the following code where str is of type string:
def self.split(str)
array = Array.new
for i in 0..str.length-1
if str[i].casecmp('a') == 0 || str[i].casecmp(':') == 0
...
end
end
array
end
if __FILE__ == $PROGRAM_NAME
count = 0;
File.readlines('C:/Users/JRQuick/Desktop/while1.test').each do |line|
line = line.strip()
count = count + 1
#CHECKS IF LINE IS COMMENT
if (line.include? '//').equal? false
#CHECKS IF EMPTY LINE
if (line.gsub(" ", "").length != 0)
array = split(line)
print array
end
end
end
end
end
This error appears:
`block in split': undefined method `[]' for nil:NilClass (NoMethodError)
The error us thrown on the 4th line above, which is the if statement found in split().
I have tried equals() and matches() for string comparison but they throw the same error. I have also tried eql?() which throws no error but does not correctly compare string, always returns false, even when should return true.
Along with the above I have made sure str[i] is never nil, and even tried str[i].to_s

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

How can I modify a Symbol in Ruby without a RuntimeError: Can't modify frozen Symbol?

I am attempting to change the color of the chess piece if the position matches given criteria however keep getting the following error message:
Position#move_str
Failure/Error: it {expect(Position[P: [e2, e3], p:[d3, d4]].move_str(e2,d3)).to eq "ed3"}
RuntimeError:
can't modify frozen Symbol
# ./chess.rb:24:in `color'
# ./chess.rb:122:in `block in move_str'
# ./chess.rb:122:in `select!'
# ./chess.rb:122:in `move_str'
# ./chess_spec.rb:75:in `block (3 levels) in <top (required)>'
I am calling the code from a separate file (which has linked properly as previous tests with other sections are working). It is running through the following code snippets
chess_spec.rb File:
75. it {expect(Position[P: e2, p:d3].move_str(e2,d3)).to eq "ed"}
76. it {expect(Position[P: [e2, e3], p:[d3, d4]].move_str(e2,d3)).to eq "ed3"}
chess.rb File color
21. class Symbol
22. def color
23. return #color unless #color.nil?
24. #color = :a < self ? :black : :white
25.
26. end
chess.rb File move_str
113. def move_str(from, to)
114. piece = board[from]
115. piece_str = piece.pawn? ? "" : piece
116. list = find(piece, to)
117. is_capture = board[to] || piece.pawn? && to == ep
118. if piece.pawn? && is_capture then
119.
120. possible_pawn_pos = [*0..7].select{|row|board[from%10+(row+2)*10] == piece}
121. possible_pawn_pos.select! { |row| target = board[to%10 + (row+2+white(-1, 1))*10]; target && target.color != piece.color }
122. if possible_pawn_pos == 1 then"#{from.to_sq[0]}#{to.to_sq[0]}"
123. else
124. "#{from.to_sq[0]}#{to.to_sq}"
125. end
126. else
127. if list.size == 1 then
128. "#{piece_str}#{to.to_sq}"
129. elsif list.select { |idx| idx%10 == from%10}.size == 1
130. "#{piece_str}#{from.to_sq[0]}#{to.to_sq}"
131. elsif list.select { |idx| idx/10 == from/10}.size == 1
132. "#{piece_str}#{from.to_sq[1]}#{to.to_sq}"
133. else
134. "#{piece_str}#{from.to_sq}#{to.to_sq}"
135. end
136. end
137. end
chess.rb File white
109. def white(w,b,t=turn)
110. t == :white ? w : b
111. end
I know the error is coming from line 122 as stated in the error message, and believe it is coming from the (row+2+white(-1, 1))*10] section, although not really that sure as am new to Ruby. As it is a Symbol I know you can not dup it at all.
How then would I be able to change the Symbols color?
Thanks for any help in advance and sorry if I have made any mistakes in posting this as I am new to both Ruby and stack overflow.
In Ruby instances of Symbol are intended for use as constants or immutable value. For this reason symbols are always frozen.
:a.frozen? #=> true
The Object#freeze documentation says the following about frozen objects:
freeze → obj
Prevents further modifications to obj. A RuntimeError will be raised if modification is attempted. There is no way to unfreeze a frozen object. See also Object#frozen?.
This method returns self.
a = [ "a", "b", "c" ]
a.freeze
a << "z"
produces:
prog.rb:3:in `<<': can't modify frozen Array (FrozenError)
from prog.rb:3
Objects of the following classes are always frozen: Integer, Float, Symbol.
This means that the error will be raised on the following line:
class Symbol
def color
return #color unless #color.nil?
#color = :a < self ? :black : :white
# ^ Instance variable can only be read, writing to it
# counts as modification, thus raising an exception.
end
end

NoMethodError: undefined method `+' for nil:NilClass

I tried googling and looking through stackoverflow and still couldnt figure out why does it not work. Essentially, this line of code reads in a file.txt and loop through the line of instructions on the text file
# Load instruction in an array
File.open('file.txt').each do |line|
line_num += 1
array.to_a.push line.split(" ")
end
# Loop through the array of Instructions
array.each do |line|
instruction = line[0]
value = line[1].to_i
This is the error that I got
NoMethodError: undefined method `+' for nil:NilClass
method block in <main> in VirtualMemory.rb at line 3
method each in VirtualMemory.rb at line 2
method <main> in VirtualMemory.rb at line 2
line_num += 1
is equivalent to
line_num = line_num + 1
Since line_num is not defined, it is nil -- the fix would be to initialize it as 0 before opening the file, or you could do something like:
array = File.open('file.txt').each.map{ |line| line.split(" ")}
You have to initialize your variables.
line_num += 1
Unless line_num is previously defined this will fail, it's basically the same thing as line_num = nil + 1
This will also fail if you haven't previously defined array, and if you had, then there'd be no point calling to_a.
array.to_a.push line.split(" ")

An integer I pass as an argument becomes a method

I am trying to write a function. Here is the code.
def get_sum(a,b)
if a == b do
return a
end
else
total = 0
for num in a...b
total += num
end
return total
end
end
I get this error:
undefined method `b' for main:Object (NoMethodError)
Can anyone tell me why I get this?
No do for ifs
if a == b
return a
end
Note that you can do the same thing so
def get_sum(a, b)
return a if a == b
(a...b).inject(:+)
end
It is because you have a block do ... end after b. Neither the keyword if nor the syntax sugar form ... == ... accepts a block. The only possibility left to interpret your code syntactically is to interpret b as a method that takes this do ... end block, and that is how it is parsed.

binary alphabet sort ruby

i tried writing my own alphabet search for Chris Pine tutorial chapter 7, and i waned to implement a binary method. there is no validity for string input, so i don't know what will happen with integers mixed with strings, but the idea was to do it for a list of strings only.
#get list of strings
puts "type words to make a list. type 'exit' to leave program."
x = ()
list = []
while x.to_s.upcase != 'EXIT'
x = gets.chomp
list.push(x)
end
list.pop
#binary method
nano = list.length
half= list.each_slice(nano/2).to_a
left = half[0]
right = half[1]
nanol=left.length
nanor=right.length
#initialize results array
A = []
for i in 0..nano-1
smallest_left = left.min
smallest_right = right.min
#no difference with this commented out or not
#if nanol==0
# A<<smallest_right
#end
#if nanor==0
# A<<smallest_left
#end
#error message points to the line below (rb:44)
if smallest_left<smallest_right
A << smallest_left
print A
left.pop[i]
elsif smallest_left>smallest_right
A << smallest_right
print A
right.pop[i]
else
print A
end
end
for input = ['z','b','r','a'] i can see the list being sorted in the error:
["a"]["a", "b"]["a", "b", "r"] rb:44:in `<': comparison of String with nil failed (ArgumentError)
please help me see my error :) Thanks in advance!
The exception is occurring because you are trying to compare nil. You get a different exception when nil is on the left.
'1' < nil
#=> scratch.rb:1:in `<': comparison of String with nil failed (ArgumentError)
nil > '1'
scratch.rb:1:in `<main>': undefined method `>' for nil:NilClass (NoMethodError)
Your code gets into this situation when the left or right array is empty (ie all of its elements have been added to A already). Presumably, this is why you had originally added the if-statements for nanol == 0 and nanor == 0 (ie to handle when one of the arrays is empty).
Your if-statements have a couple of issues:
You do need the nanol == 0 and nanor == 0 statements
The three if-statements are always run, even though only one would apply in an iteration
nanol and nanor are never re-calculated (ie they will never get to zero)
When the left and right values are equal, you don't actually add anything to the A array
The inside of your iteration should be:
smallest_left = left.min
smallest_right = right.min
nanol=left.length
nanor=right.length
if nanol == 0 #Handles left no longer having values
A << right.delete_at(right.index(smallest_right) || right.length)
elsif nanor == 0 #Handles right no longer having values
A << left.delete_at(left.index(smallest_left) || left.length)
elsif smallest_left < smallest_right
A << left.delete_at(left.index(smallest_left) || left.length)
elsif smallest_left > smallest_right
A << right.delete_at(right.index(smallest_right) || right.length)
else #They are equal so take one
A << left.delete_at(left.index(smallest_left) || left.length)
end
You will still have an issue (no error, but unexpected results) when your list has an odd number of elements. But hopefully that answers your question.

Resources