levenshtein_distance in ruby - ruby

I wrote levenshtein_distance as below to calculate the distance between two strings:
def min3(a, b, c)
if a < b && a < c then
a
elsif b < c then
b
else
c
end
end
def levenshtein_distance(s, t)
m = s.length
n = t.length
return m if n.zero?
return n if m.zero?
d = (0..m+1).to_a
x = nil
s.each_char.each_with_index do |ch1, i|
e = i + 1
t.each_char.each_with_index do |ch2, j|
cost = ch1 == ch2 ? 0 : 1
x = min3(d[j + 1] + 1, e + 1, d[j] + cost)
d[j] = e
e = x
end
d[m] = x
end
x
end
When the two strings are different, it gives an error message:
NoMethodError - undefined method `+' for nil:NilClass
The error detects line:
x = min3(d[j + 1] + 1, e + 1, d[j] + cost)
I thought this was due to index surpassing the defined d's limit. But enlarging the length of d doesn't solve this problem.
Is there something I missed in implementing the algorithm?
this is the case that I tested on irb
irb(main):052:0> levenshtein_distance("a", "abc")
NoMethodError: undefined method `+' for nil:NilClass
from (irb):24:in `block (2 levels) in levenshtein_distance'
from (irb):22:in `each_char'
from (irb):22:in `each_with_index'
from (irb):22:in `block in levenshtein_distance'
from (irb):20:in `each_char'
from (irb):20:in `each_with_index'
from (irb):20:in `levenshtein_distance'
from (irb):52
from /usr/bin/irb:12:in `<main>'

I've rewritten the algorithm in accordance to Wikipedia:
def ld(s, t)
v0 = (0..t.length).to_a
v1 = []
#p v0
s.chars.each_with_index do |s_ch, i|
v1[0] = i + 1
t.chars.each_with_index do |t_ch, j|
cost = s_ch == t_ch ? 0 : 1
v1[j + 1] = [v1[j] + 1, v0[j + 1] + 1, v0[j] + cost].min
end
v0 = v1.dup
#p v1
end
v0[t.length]
end
It seem to work. Also you can as well uncomment p v1 and p v0 to see how the vectors change on each iteration.

Related

undefined method `<' for nil:NilClass error but no nil exists?

Was wondering why I get the error: "undefined method `<' for nil:NilClass" when compiling. After looking for reasons why, I found that you cannot use [] on an object with nil as a value. This makes sense, but I don't see why my array would contain nil in it. What am I missing?
def binary_search(n, arr)
middle = arr.length #pick middle value
i = 0
j = arr.length - 1
while i <= j
if arr[middle] == n
return true
elsif arr[middle] < n
i = middle + 1
middle = (i + j) / 2
else
j = middle - 1
middle = (i + j) / 2
end
end
false
end
nums = [76,32,50,90,10,8,15,49]
nums.sort
puts nums.inspect
binary_search(50, nums)
Let's look at a simplified subset of the code:
arr = [76,32,50,90,10,8,15,49]
middle = arr.length # 8
arr[middle] < 50 # NoMethodError
The length is 8.
arr[8] is nil, because there is no item at index 8. Remember that Ruby indexes begin with 0.
nil < 50 is a NoMethodError

Binary Search stuck in infinite loop

I am trying to test a binary search algorithm. The logic seems right, however, I'm running into some issues.
There are two different methods, case and if. If one method fails, the other is right.
1.
arr = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]
def search(arr, value)
if arr.nil?
return "value not in array"
end
l = 0
u = (arr.length - 1)
t = value
if l > u
puts "array is wrong!"
return
end
while l < u
m = (l + u) / 2
case m
when m < t
l = (m + 1)
when m == t
puts "hello world"
when m > t
u = (m - 1)
end
end
end
search(arr, 5)
2.
def search(arr, value)
if arr.nil?
return "value not in array"
end
l = 0
u = (arr.length - 1)
t = value
if l > u
puts "array is wrong!"
return
end
while l < u
m = (l + u) / 2
if m < t
l = (m + 1)
elsif m == t
puts "hello world"
break
elsif m > t
u = (m - 1)
end
end
end
I thought my code would work, but something is causing my while loop to go on infinitely. What am I doing to cause an infinite loop?

'undefined method `>' for nil:NilClass in ruby

This is my code for converting a user entered string into a Caesar cipher.
puts "text?"
text = gets.chomp
puts "key?"
key = gets.chomp.to_i
plainTex = Array.new
ciphTex = Array.new
j = 0
text.each_byte do |i|
plainTex[j] = i
j += 1
end
j = 0
plainTex.each_entry do |i|
if ( i == 32 )
ciphTex[j] = plainTex[j]
j += 1
end
if( plainTex[j] > 64) and (plainTex[j] < 91 )
if( (plainTex[j] + key) > 91)
ciphTex[j] = (plainTex[j] + key ) - 90
j += 1
else
ciphTex[j] = plainTex[j] + key
j += 1
end
end
if( plainTex[j] > 94) and (plainTex[j] < 123)
if( (plainTex [j] + key) > 122)
ciphTex [j] = (plainTex[j] + key) - 122
j += 1
else
ciphTex[j] = plainTex[j] + key
j += 1
end
end
end
ciphTex.each_entry do |i|
puts i
end
now, I'm getting error :
undefined method `>' for nil:NilClass (NoMethodError)
Search on the web led me to conclude that plainTex might be nil,as the error message is saying( which should not be the case as plainTex is fed data beforehand).
On a side note : the program runs fine when the input string is all lowercase and has no space. I don't know why.
So, what am I doing wrong?
Problem Explained
When you access array with index that is out of bounds you'll get no error, but result nil.
irb --simple-prompt
>> a = [1]
=> [1]
>> a[0]
=> 1
>> a[1]
=> nil
>> a[200]
=> nil
Debugging your code
A little bit of puts statements reveal the issue:
text.each_byte do |i|
plainTex[j] = i
j += 1
end
j = 0
puts "Length: #{plainTex.length}"
plainTex.each_entry do |i|
if ( i == 32 )
ciphTex[j] = plainTex[j]
j += 1
end
puts "j: #{j}"
This is the output
text?
Testing Test
key?
32
Length: 12
j: 0
j: 2
j: 3
j: 4
j: 5
j: 6
j: 7
j: 8
j: 10
j: 11
j: 12
test.rb:30:in `block in <main>': undefined method `>' for nil:NilClass (NoMethodError)
from test.rb:23:in `each'
from test.rb:23:in `each_entry'
from test.rb:23:in `<main>
Last index of array in this case was 11 and you accessed index 12.
You got result nil then you tried to call method > on nil.
Professional debugging
You can install gem pry-byebug and use short aliases s, n for step into, next etc.
https://github.com/deivid-rodriguez/pry-byebug
Bug Found
Using pry-byebug it's immediatelly clear what's the bug, even though I don't really understand your code because those numbers are meaningless to me.
For first letter it increments j in second condition if( plainTex[j] > 64) and (plainTex[j] < 91 ). Then with incremented j it moves to if( plainTex[j] > 94) and (plainTex[j] < 123) where it increments it again.
You should use elsif for all of these or next
Code Cleanup
Ruby convention is to use snake case to name your variable and I'd change this piece of code:
if( plainTex[j] > 64) and (plainTex[j] < 91 )
if( (plainTex[j] + key) > 91)
ciphTex[j] = (plainTex[j] + key ) - 90
j += 1
else
ciphTex[j] = plainTex[j] + key
j += 1
end
end
to this:
if plain_text[j].between?(65, 90)
ciph_text[j] = plain_text[j] + key
ciph_text[j] -= 90 if ciph_text[j] > 91
j += 1
end
It seems that you are repeating j += 1 in every condition, so why not remove it outside if/else blocks and put it as last line before the loop end?
Also I am a human and I don't want to remember that "A" is 65 and "Z" is 90. Why not say so in the code?
if plain_text[j].between?("A".ord, "Z".ord)
I have never messed with ASCII in ruby, but I am sure you could further improve this, possibly do entire cypher in one simple loop with 10 lines of code or less.
Ruby is really good at processing arrays, so use that. I'd do something like this:
plain_text = text.codepoints
cyph_text = plain_text.map do |code|
if code == ' '.ord
' '.ord
elsif
# return cypher code
end
end
Check out how map function works.

very simple ruby programing, getting error and don't understand it

I'm asked to write the ruby program that generate the output based the given command,
The full description
I'm really new in ruby (maybe few hours that I have started ruby)
I'm getting this error, please check my code for other possible errors:
Thank you.
n `block in each2': undefined method `[]' for #<MyVector:0x00000002c4ad90 #array=[2, 3, 4]> (NoMethodError)
What I have done so far:
# MyVector Class
class MyVector
def initialize (a)
if !(a.instance_of? Array)
raise "ARGUMENT OF INITIALIZER MUST BE AN ARRAY"
else
#array = a
end
end
def array
#array
end
def to_s
#array.to_s
end
def length
#array.length
end
def each2(a)
raise Error, "INTEGER IS NOT LIKE VECTOR" if a.kind_of?(Integer)
Vector.Raise Error if length != a.length
return to_enum(:each2, a) unless block_given?
length.times do |i|
yield #array[i], a[i]
end
self
end
def * (a)
Vector.Raise Error if length != a.length
p = 0
each2(a) {|a1, a2|p += a1 * a2}
p
end
end
# MyMatrix Class
class MyMatrix
def initialize a
#array=Array.new(a.length)
i=0
while(i<a.length)
#array[i]=MyVector.new(a[i])
end
end
def to_s
#array.to_s
end
def transpose
size=vectors[0].length
arr= Array.new(size)
i=0
while i<size
a=Array.new(vector.length)
j=0
while j<a.length
a[j]=vectors[j].arr[i]
j+=1
end
arr[i]=a
i+=1
end
arr[i]=a
i+=1
end
def *m
if !(m instance_of? MyMatrix)
raise Error
a=Array.new(#array.length)
i=0
while (i<#array.length)
a[i]=#array[i]*m
i=i+1
end
end
end
end
Input:
Test code
v = MyVector.new([1,2,3])
puts "v = " + v.to_s
v1 = MyVector.new([2,3,4])
puts "v1 = " + v1.to_s
puts "v * v1 = " + (v * v1).to_s
m = MyMatrix.new([[1,2], [1, 2], [1, 2]])
puts "m = " + m.to_s + "\n"
puts "v * m = " + (v * m).to_s
m1 = MyMatrix.new([[1, 2, 3], [2, 3, 4]])
puts "m1 = " + m1.to_s + "\n"
puts "m * m1 = " + (m * m1).to_s
puts "m1 * m = " + (m1 * m).to_s
Desired Output:
v = 1 2 3
v1 = 2 3 4
v * v1 = 20
m =
1 2
1 2
1 2
v * m = 6 12
m1 =
1 2 3
2 3 4
m * m1 =
5 8 11
5 8 11
5 8 11
m1 * m =
6 12
9 18
length.times do |i|
yield #array[i], a[i]
end
In the above block, a is an instance of MyVector. You need to define the [] operator on it, probably something like:
def [](i)
#array[i]
end

ArgumentError in #new, Subclassing Enumerator

I'm subclassing Enumerator like this:
class CuadraticPrimeGenerator < Enumerator
def initialize(a=1,b=49)
super do |y|
x = 1
loop do
y << x**2 + a*x + b
x += 1
end
end
end
end
However...
> CuadraticPrimeGenerator.new(1,49)
0027.rb:41:in `initialize': 49 is not a symbol (TypeError)
from 0027.rb:41:in `initialize'
from 0027.rb:48:in `new'
from 0027.rb:48:in `<main>'
Thoughts?
What about:
class CuadraticPrimeGenerator < Enumerator
def initialize(a=1,b=49)
super() do |y|
count, x = 0, 1
loop { y.yield(x**2 + a*x + b) }
end
end
end

Resources