I'm trying to add a method to the Range class. The goal is to check if an integer is is included in the range and to include it if it isn't. It would be useful to store in a Range some minimum and maximum values.
So I was thinking of the following:
class Range
def include!(n)
if n < self.begin
self = n..self.end
elsif n > self.end
self = self.begin..n
end
end
end
r = Range.new(500, 500)
100.times do
r.include!(rand(1000))
end
But I get a Can't change the value of self error.
Would this be the only solution:
class Range
def include(n)
if n < self.begin
n..self.end
elsif n > self.end
self.begin..n
else
self
end
end
end
r = Range.new(500, 500)
100.times do
r = r.include(rand(1000))
end
Ranges themselves are immutable, you can't change their properties after they are created. However, it's still possible to achieve what you want by creating new ranges instead of modifying existing ones, as you discovered. I'd probably do it like this:
module RangeAdder
def +(value)
return self if value.nil?
return self + value.min + value.max if value.is_a? Range
return value.reduce(self, :+) if value.is_a? Enumerable
new_begin = [self.begin, value].min
new_end = [self.end, value].max
new_exclude_end = value > self.end ? false : self.exclude_end?
Range.new(new_begin, new_end, new_exclude_end)
end
end
class Range
include RangeAdder
end
Then:
r = 500..500
r += Array.new(100) { rand(1000) }
#=> 15..982
Or if all you really want to do is get the minimum and maximum values from an array, you can do that with:
a = Array.new(100) { rand(1000) }
r = Range.new(*a.minmax)
#=> 11..996
Related
I'm working on a TOP's project where i should do a function that return the shortest path between two vertices of a graph, but i get an error.
What can i do to solve this?
Nothing yet because i am a newbie.
the Graph class
class Vertex
attr_accessor :value, :neighbors, :prev, :dist
def initialize(value)
#value = value
#neighbors = []
#prev = nil
#dist = Float::INFINITY
end
def add_edge(adjacent_vertex)
#neighbors << adjacent_vertex
end
end
class Graph
attr_reader :vertices
def initialize
#vertices = {}
#dijkstra_source = nil
end
def add_vertex(vertex)
#vertices[vertex.value] = vertex
end
def add_edge(vertex1, vertex2)
#vertices[vertex1.value].add_edge(#vertices[vertex2])
#vertices[vertex2.value].add_edge(#vertices[vertex1])
end
def dijkstra(source)
return if #dijkstra_source == source
unvisited = #vertices.values
unvisited.each do |vertex|
vertex.dist = Float::INFINITY
vertex.prev = nil
end
#vertices[source].dist = 0
until unvisited.empty?
current_node = unvisited.min_by(&:dist)
break if current_node.dist == Float::INFINITY
unvisited.delete(current_node)
current_node.neighbors.each do |v|
neighbor = #vertices[v]
if unvisited.include?(neighbor)
alt = current_node.dist + 1
if alt < neighbor.dist
neighbor.dist = alt
neighbor.prev = current_node
end
end
end
end
#dijkstra_source = source
end
def find_shortest_path(source, target)
dijkstra(source)
path = []
u = target
while u
path.unshift(u)
u = #vertices[u].prev
end
return path
end
end
Where i instatiane the vertices
require_relative 'graph'
class Knight
attr_reader :character, :graph
def initialize
#character = "♞"
#possible_moves = nil
#positions = Array.new(8) { Array.new(8, " ") }
#move_set = [
[-1,-2],
[-2,-1],
[-2,+1],
[-1,+2],
[+1,+2],
[+2,+1],
[+2,-1],
[+1,-2]
]
#graph = generate_graph
generate_edges
end
def generate_graph
graph = Graph.new
#positions.each_index do |x|
#positions[x].each_index do |y|
vertex = Vertex.new([x,y])
graph.add_vertex(vertex)
end
end
graph
end
def generate_edges
#graph.vertices.each do |key, value|
#move_set.each do |move|
x = key[0] + move[0]
y = key[1] + move[1]
if (0..7).include?(x) && (0..7).include?(y)
vertex = [x,y]
value.add_edge(vertex) unless value.neighbors.include?(vertex)
end
end
end
end
end
knight = Knight.new
knight.generate_edges
p knight.graph.find_shortest_path([1,2],[2,3])
I expect an array which stores inside the shortest path from a source vertex([1,2]) to a target vertex(2,3).
But instead of that what i get is a NilClass error
Traceback (most recent call last):
1: from knight.rb:50:in `<main>'
/home/brito/the_odin_project/knight_moves/graph.rb:63:in `find_shortest_path': undefined method `prev' for nil:NilClass (NoMethodError)
The issue is with something being not initialized (nil) while an attempt to call prev method on it, as might be seen from the error message.
The nearest suspicious call is in Graph#find_shortest_path. If you’d print the #vertices and u from there, you’ll see that on the second iteration u becomes the Vertex instance, while #vertices has raw values as keys. The fix would be to change u = #vertices[u].prev to u = #vertices[u].prev.value to keep u being the raw value (note the check for the #vertices[u].prev in the first place.
The summing up:
def find_shortest_path(source, target)
dijkstra(source)
path = []
u = target
while u
path.unshift(u)
u = (#vertices[u].prev.value if #vertices[u].prev)
end
return path
end
Also the code above would fix the issue, the code is not ruby idiomatic; FWIW, I’d post the idiomatic version.
I understand, it’s too much of uncommon patterns for you yet, but just in case you’d be interested to dig into:
def find_shortest_path(source, target)
dijkstra(source)
loop.reduce([[], target]) do |(path, u), _|
break path unless u
[
path << u,
(#vertices[u].prev.value if #vertices[u].prev)
]
end
end
I've been working on this for a few days, at least. Testing seems to show the correct value is being returned. My problem is being able to grab the best_move value and have it print out. I set up the suggested_move method and try to use return suggested_move(best_move) but it triggers the method for every level back up the tree. Also it returns the wrong value, which I'm guessing is because it's stopping before depth is back to 0.
In my minimax I had a similar setup the difference being the depth was incremented (not decremented) on successive calls. Point being I was able to say if depth == 0 p best_move. So I'm scratching my head because using that same conditional I get a nil class error in this code.
#board = ["X","O","O","X","X","","","O",""]
def available_spaces
#board.map.with_index {|a, i| a == "" ? i+1 : nil}.compact
end
def suggested_move(move)
p "Suggested move: #{move}"
end
def full?
#board.all?{|token| token == "X" || token == "O"}
end
def suggested_move(move)
p "Move: #{move}"
end
def bestmove(board, depth=0, best_score = {})
return 0 if full?
return 1 if won?
best = -Float::INFINITY
available_spaces.each do |space|
#board[space-1] = current_player
best_score[space] = -bestmove(#board, depth-1, {})
#board[space-1] = ""
end
p best_score
if best_score.max_by {|key,value| value }[1] > best
best = best_score.max_by {|key,value| value }[1]
best_move = best_score.max_by {|key,value| value }[0]
end
return best_move
end
bestmove(#board)
I have a number of ranges that I want merge together if they overlap. The way I’m currently doing this is by using Sets.
This is working. However, when I attempt the same code with a larger ranges as follows, I get a `stack level too deep (SystemStackError).
require 'set'
ranges = [Range.new(73, 856), Range.new(82, 1145), Range.new(116, 2914), Range.new(3203, 3241)]
set = Set.new
ranges.each { |r| set << r.to_set }
set.flatten!
sets_subsets = set.divide { |i, j| (i - j).abs == 1 } # this line causes the error
puts sets_subsets
The line that is failing is taken directly from the Ruby Set Documentation.
I would appreciate it if anyone could suggest a fix or an alternative that works for the above example
EDIT
I have put the full code I’m using here:
Basically it is used to add html tags to an amino acid sequence according to some features.
require 'set'
def calculate_formatting_classes(hsps, signalp)
merged_hsps = merge_ranges(hsps)
sp = format_signalp(merged_hsps, signalp)
hsp_class = (merged_hsps - sp[1]) - sp[0]
rank_format_positions(sp, hsp_class)
end
def merge_ranges(ranges)
set = Set.new
ranges.each { |r| set << r.to_set }
set.flatten
end
def format_signalp(merged_hsps, sp)
sp_class = sp - merged_hsps
sp_hsp_class = sp & merged_hsps # overlap regions between sp & merged_hsp
[sp_class, sp_hsp_class]
end
def rank_format_positions(sp, hsp_class)
results = []
results += sets_to_hash(sp[0], 'sp')
results += sets_to_hash(sp[1], 'sphsp')
results += sets_to_hash(hsp_class, 'hsp')
results.sort_by { |s| s[:pos] }
end
def sets_to_hash(set = nil, cl)
return nil if set.nil?
hashes = []
merged_set = set.divide { |i, j| (i - j).abs == 1 }
merged_set.each do |s|
hashes << { pos: s.min.to_i - 1, insert: "<span class=#{cl}>" }
hashes << { pos: s.max.to_i - 0.1, insert: '</span>' } # for ordering
end
hashes
end
working_hsp = [Range.new(7, 136), Range.new(143, 178)]
not_working_hsp = [Range.new(73, 856), Range.new(82, 1145),
Range.new(116, 2914), Range.new(3203, 3241)]
sp = Range.new(1, 20).to_set
# working
results = calculate_formatting_classes(working_hsp, sp)
# Not Working
# results = calculate_formatting_classes(not_working_hsp, sp)
puts results
Here is one way to do this:
ranges = [Range.new(73, 856), Range.new(82, 1145),
Range.new(116, 2914), Range.new(3203, 3241)]
ranges.size.times do
ranges = ranges.sort_by(&:begin)
t = ranges.each_cons(2).to_a
t.each do |r1, r2|
if (r2.cover? r1.begin) || (r2.cover? r1.end) ||
(r1.cover? r2.begin) || (r1.cover? r2.end)
ranges << Range.new([r1.begin, r2.begin].min, [r1.end, r2.end].max)
ranges.delete(r1)
ranges.delete(r2)
t.delete [r1,r2]
end
end
end
p ranges
#=> [73..2914, 3203..3241]
The other answers aren't bad, but I prefer a simple recursive approach:
def merge_ranges(*ranges)
range, *rest = ranges
return if range.nil?
# Find the index of the first range in `rest` that overlaps this one
other_idx = rest.find_index do |other|
range.cover?(other.begin) || other.cover?(range.begin)
end
if other_idx
# An overlapping range was found; remove it from `rest` and merge
# it with this one
other = rest.slice!(other_idx)
merged = ([range.begin, other.begin].min)..([range.end, other.end].max)
# Try again with the merged range and the remaining `rest`
merge_ranges(merged, *rest)
else
# No overlapping range was found; move on
[ range, *merge_ranges(*rest) ]
end
end
Note: This code assumes each range is ascending (e.g. 10..5 will break it).
Usage:
ranges = [ 73..856, 82..1145, 116..2914, 3203..3241 ]
p merge_ranges(*ranges)
# => [73..2914, 3203..3241]
ranges = [ 0..10, 5..20, 30..50, 45..80, 50..90, 100..101, 101..200 ]
p merge_ranges(*ranges)
# => [0..20, 30..90, 100..200]
I believe your resulting set has too many items (2881) to be used with divide, which if I understood correctly, would require 2881^2881 iterations, which is such a big number (8,7927981983090337174360463368808e+9966) that running it would take nearly forever even if you didn't get stack level too deep error.
Without using sets, you can use this code to merge the ranges:
module RangeMerger
def merge(range_b)
if cover?(range_b.first) && cover?(range_b.last)
self
elsif cover?(range_b.first)
self.class.new(first, range_b.last)
elsif cover?(range_b.last)
self.class.new(range_b.first, last)
else
nil # Unmergable
end
end
end
module ArrayRangePusher
def <<(item)
if item.kind_of?(Range)
item.extend RangeMerger
each_with_index do |own_item, idx|
own_item.extend RangeMerger
if new_range = own_item.merge(item)
self[idx] = new_range
return self
end
end
end
super
end
end
ranges = [Range.new(73, 856), Range.new(82, 1145), Range.new(116, 2914), Range.new(3203, 3241)]
new_ranges = Array.new
new_ranges.extend ArrayRangePusher
ranges.each do |range|
new_ranges << range
end
puts ranges.inspect
puts new_ranges.inspect
This will output:
[73..856, 82..1145, 116..2914, 3203..3241]
[73..2914, 3203..3241]
which I believe is the intended output for your original problem. It's a bit ugly, but I'm a bit rusty at the moment.
Edit: I don't think this has anything to do with your original problem before the edits which was about merging ranges.
I have the following implementation of a linked list in Ruby:
class Node
attr_accessor :data, :next
def initialize(data = nil)
#data = data
#next = nil
end
end
class LinkedList
def initialize(items)
#head = Node.new(items.shift)
items.inject(#head) { |last, data| #tail = last.next = Node.new(data) }
end
def iterate
return nil if #head.nil?
entry = #head
until entry.nil?
yield entry
entry = entry.next
end
end
def equal?(other_list)
#How do I check if all the data for all the elements in one list are the same in the other one?
end
end
I have tried using the .iterate like this:
def equals?(other_list)
other_list.iterate do |ol|
self.iterate do |sl|
if ol.data != sl.data
return false
end
end
end
return true
end
But this is doing a nested approach. I fail to see how to do it.
You can't do it easily with the methods you have defined currently, as there is no way to access a single next element. Also, it would be extremely useful if you implemented each instead of iterate, which then gives you the whole power of the Enumerable mixin.
class LinkedList
include Enumerable # THIS allows you to use `zip` :)
class Node # THIS because you didn't give us your Node
attr_accessor :next, :value
def initialize(value)
#value = value
#next = nil
end
end
def initialize(items)
#head = Node.new(items.shift)
items.inject(#head) { |last, data| #tail = last.next = Node.new(data) }
end
def each
return enum_for(__method__) unless block_given? # THIS allows block or blockless calls
return if #head.nil?
entry = #head
until entry.nil?
yield entry.value # THIS yields node values instead of nodes
entry = entry.next
end
end
def ==(other_list)
# and finally THIS - get pairs from self and other, and make sure all are equal
zip(other_list).all? { |a, b| a == b }
end
end
a = LinkedList.new([1, 2, 3])
b = LinkedList.new([1, 2, 3])
c = LinkedList.new([1, 2])
puts a == b # => true
puts a == c # => false
EDIT: I missed this on the first run through: equal? is supposed to be referential identity, i.e. two variables are equal? if they contain the reference to the same object. You should not redefine that method, even though it is possible. Rather, == is the general common-language meaning of "equal" as in "having the same value", so I changed it to that.
I think there is something wrong with your initialize method in LinkedList, regardless could this be what you need
...
def equal?(other_list)
other_index = 0
cur_index = 0
hash = Hash.new
other_list.iterate do |ol|
hash[ol.data.data] = other_index
other_index += 1
end
self.iterate do |node|
return false if hash[node.data.data] != cur_index
return false if !hash.has_key?(node.data.data)
cur_index += 1
end
return true
end
...
Assuming this is how you use your code
a = Node.new(1)
b = Node.new(2)
c = Node.new(3)
listA = [a,b,c]
aa = Node.new(1)
bb = Node.new(2)
cc = Node.new(3)
listB = [aa,bb,cc]
linkA = LinkedList.new(listA)
linkB = LinkedList.new(listB)
puts linkA.equal?(linkB)
I have the following code for an assignment. After much debugging I found what was happening
class Integer
def initialize()
#ans = ""
end
def ans
#ans = ""
end
def ans=(value)
#ans = value
end
def to_base(base)
# convert given number into the base
# figure out how to make it the most efficient
num = self
r = 0
loop do
r = num % base # modulus
#ans = r.to_s + #ans.to_s # add to answer
num /= base # division
break unless num != 0
end
english = #ans # return value
end
def to_oct
self.to_base(8)
end
end
puts 8.to_oct
puts 8.to_base(2)
Output:
10
100010
The output for the binary version should be 1000 not 100010
What it did was append the first instance of the class 8.to_oct onto the second call 8.to_base(2)
Is there a way to have this cleared as I want to use the same number (8) in this example and convert it to various base numbers. what am I doing wrong in my class?
Thanks!