Omnimaga
General Discussion => Technology and Development => Computer Programming => Topic started by: Ashbad on July 06, 2011, 11:48:31 am
-
Yesterday I decided I needed a break from calculator programming for a day or two, so I decided to have another practice with Ruby. Somehow, looking up a substitute for Bignums in Ruby brought me to the Perl 6 specifications over time. When I was glancing over the specs, I noticed that it introduced the module Quantum::Superposition as an official part of the language. This module defined the use of Junctions in Perl. I didn't know what a junction was before it, but once I learned it seemed like a decently cool idea. Therefore, I decided to write my own module for Junctions in Ruby written the Ruby Way.
For those who haven't heard of them: Junctions are data types that are treated like a single value but in reality occupy multiple values, much like in quantum theory certain molecules can occupy superpositions -- they can occupy different spatial areas concurrently. Junctions are to be treated as single values that are determined based on their rules (based on ANY and ALL declarations) but can be expanded out to evaluate contents when wanted. When a Junction is going by an exclusive-all ruleset, all values have to be true when evaluated in an expression to give true; when going on an exclusive-any ruleset, if any of the values give true, then it gives a true value. They can also be mixed so that to obtain a true at least a certain number of values must come out as true.
Anyways, here's the class I wrote for Ruby Junctions. No RDoc yet cause I'm lazy with commenting, but it kinda speaks for itself. I cheated a bit and basically held the overall value in an array and made specific evaluation methods, but it works perfectly so its hard to complain:
class Junction
attr_accessor :value, :coelements
attr_reader :type
ANY = "|";ALL = "&"
def initialize(num_of_values,typeof,array=nil)
if block_given?
index = 0;@value = Array.new(num_of_values){|value|
yield value, index;index+=1}
elsif array;@value = array
else;@coelements = (@value = Array.new(num_of_values)).size.to_i
end
@coelements = @value.size.to_i
@type = (typeof=="any" or typeof=="all")?
((typeof=="any")?ANY: ALL):
((typeof==ANY)?ANY: ALL) #always defaults to ALL if not valid
end
def forall
if block_given?
index = 0;while index < coelements
yield @value.at(index), index;index+=1
end
else;raise "No block given to \'forall\' method"
end
end
def ==(value)
if @type==ANY
return @value.include?(value)
elsif @type==ALL
return (@value==value)?true:false
else;raise "Unknown Junction Typing error in \'==\' method"
end
end
def !=(value)
if @type==ANY
return !(@value.include?(value))
elsif @type==ALL
return (@value==value)?false:true
else;raise "Unknown Junction Typing error in \'==\' method"
end
end
def <(value)
return @value.max < value
end
def >(value)
return @value.min > value
end
def <=(value)
return @value.max <= value
end
def >=(value)
return @value.min >= value
end
def ===(value)
return (@value.include?(value) and @value.at(@value.index(value))===value)
end
def to_a#override
return @value.to_a
end
def to_s#override
return @value.to_s.gsub(", ",((@type==ANY)?ANY: ALL))
end
def to_i
@value.each {|i|(i = (i.to_i)) rescue i = 0}.to_a;return self
end
def to_f
@value.each {|i|(i = (i.to_f)) rescue i = 0.0}.to_a;return self
end
def <<(value)
@value.push(value);return self
end
def push_to_last(value)
@value.push(value);return self
end
def pop_last!
return @value.pop
end
def pop_last_array!
@value.pop;return self
end
def type_change(new_type=nil)
if new_type;@type = new_type
else;@type = ((@type==ANY)?ALL: ANY)
end
end
def elements?
@coelements.to_i
end
def clean!
@value.uniq!;return self
end
def pretty_puts #not needed, but cool for easy reading :3
self.forall {|i,j|puts "#{(j+1).to_s}" <<
format_number_th(j) << " possible value is #{i.to_s}" }
end
private#helpers
def format_number_th(j)#helper for pretty_puts
if [10,11,12].include?(j%100);return "th"
else;return (["st","nd","rd","th"].at((j%10>=4)?3:j%10)).to_s
end
end
end
And with the test code:
superpos = Junction.new 5,Junction::ANY,[1,2,3,4,5]
superpos.pretty_puts
puts superpos.to_s
puts superpos.to_i.to_s
puts superpos.to_a.to_s
puts (superpos << 6).to_s
puts superpos.pop_last_array!.to_s
puts superpos == 3 #all
superpos.type_change
puts superpos == 3 #any
puts superpos.to_s
superpos.forall{|value,coelement_i|
puts "\\o/ hai mr.#{value} from #{coelement_i} I liek chocolat mikl"
}
I get:
1st value is 1
2nd value is 2
3rd value is 3
4th value is 4
5th value is 5
[1|2|3|4|5]
[1|2|3|4|5]
[1, 2, 3, 4, 5]
[1|2|3|4|5|6]
[1|2|3|4|5]
false
true
[1&2&3&4&5]
\o/ hai mr.1 from 0 I liek chocolat mikl
\o/ hai mr.2 from 1 I liek chocolat mikl
\o/ hai mr.3 from 2 I liek chocolat mikl
\o/ hai mr.4 from 3 I liek chocolat mikl
\o/ hai mr.5 from 4 I liek chocolat mikl
So I can confirm that instances of Junction are treated like they're supposed to according to the specifications for ALL and ANY specifications. I'm thinking of adding a SOM sub-type where you can specify non-completeness of ANY and ALL mixes, such as [1&2&3|4|5] which isn't exactly how the Perl one goes, but this isn't Perl I'm doing this in, is it? Anyways, while at the moment it's not quite as useful as the Perl version (it's mostly only useful for standard-type instances and doesn't fare well with supplying a form of generic like it can be used in Perl), it at least gave my a decent programming practice, and might shorten some of my code that makes use of the class by using junctions instead of Arrays with constant element checking.
Anyways, for other people who use Ruby, I'd like to hear any ideas you have for me concerning additions and fixes :) right now I don't have that many standard methods for Junctions yet, but any that anyone would like to provide would be appreciated.
-
Well, I looked over the documentation for the one on RubyForge (not the source mind you I don't know how Apache SVN works well enough to even get the code from the repo x.x yeah I suck) and realized:
- it's lame and only supports inlining instances with any(), all(), and none(), no support for full instances with easy evaluation (though when it comes to instantiation syntax it gives you a syntax sugar rush)
- it hasn't been updated since '08 and is in early alpha form
- it doesn't have that great of documentation
- it has only 9 code views..
And so I decided that I wanted to be more serious about my implementation and maybe get further than them. With that, I now have quintupled the size of the source (AKA pastebinning so Kerm doesn't maul me yay for omnimaga :)) and added these features:
- added ONE, NON, and NIL rulesets (nil means that no ruleset was given and therefore doesn't return an evaluation with an applied rule)
- inlining support with any(),all(),one(), and non(), which like the init method support nil loading, array loading and/or block value evaluation
- fixed comparison operators concerning Junction Instances (most of them besides == an != only evaluated with the ANY ruleset)
- fixed to_s, to_i, and to_f
- optimized (haha yes Ruby is slow but there are tricks to faster evaluation and execution of code) using crazy use of embedded and chained ternary operators
.. and a few more things I forgot about. Anyways, source:
class Junction
attr_accessor :value, :coelements
attr_reader :type
ANY = "|";ALL = "&";ONE = "@";NON = "~";NIL = "?"
def initialize(num_of_values,typeof,array=nil)
if block_given?
index = 0;@value = Array.new(num_of_values){|value|
yield value, index;index+=1}
elsif array;@value = array
else;@value = Array.new(num_of_values)
end
@coelements = @value.size.to_i
@type = ((typeof==ANY or typeof==ALL)?((typeof==ANY)?ANY: ALL):(NIL))
@type = ((typeof==ONE or typeof==NON)?((typeof==ONE)?ONE: NON):(@type))
return self
end
def any(num_of_values,array=nil)
a = Junction.new(num_of_values,Junction::ANY,array)
if block_given?
index = 0;a.value = Array.new(num_of_values){|value|
yield value, index;index+=1}
elsif array;a.value = array
else;a.value = Array.new(num_of_values)
end
a.coelements = a.value.size.to_i
return a
end
def all(num_of_values,array=nil)
a = Junction.new(num_of_values,Junction::ALL,array)
if block_given?
index = 0;a.value = Array.new(num_of_values){|value|
yield value, index;index+=1}
elsif array;a.value = array
else;a.value = Array.new(num_of_values)
end
a.coelements = a.value.size.to_i
return a
end
def one(num_of_values,array=nil)
a = Junction.new(num_of_values,Junction::ONE,array)
if block_given?
index = 0;a.value = Array.new(num_of_values){|value|
yield value, index;index+=1}
elsif array;a.value = array
else;a.value = Array.new(num_of_values)
end
a.coelements = a.value.size.to_i
return a
end
def non(num_of_values,array=nil)
a = Junction.new(num_of_values,Junction::NON,array)
if block_given?
index = 0;a.value = Array.new(num_of_values){|value|
yield value, index;index+=1}
elsif array;a.value = array
else;a.value = Array.new(num_of_values)
end
a.coelements = a.value.size.to_i
return a
end
def forall
if block_given?
index = 0;while index < coelements
yield @value.at(index), index;index+=1
end
else;raise "No block given to \'forall\' method"
end
end
def ==(value)
case @type
when ANY
return @value.include?(value)
when ALL
occurances=1;@value.each {|index|
occurances+=((index==value)?1:0)
}
return occurances==@coelements
when ONE
occurances=0;@value.each {|index|
occurances+=((index==value)?1:0)
}
return occurances==1
when NON
return (@value.include?(value))?false:true
when NIL;return nil
end
end
def !=(value)
case @type
when ANY
return @value.include?(value)?false:true
when ALL
occurances=1;@value.each {|index|
occurances+=((index!=value)?1:0)
}
return occurances==@coelements
when ONE
occurances=0;@value.each {|index|
occurances+=((index!=value)?1:0)
}
return occurances==1
when NON
return (@value.include?(value))
when NIL;return nil
end
end
def <(value)
case @type
when ANY
return (@value.include?(value))?(@value.max < value):(nil)
when ALL
occurances=1;@value.each {|index|
occurances+=((index < value)?1:0)
}
return occurances==@coelements
when ONE
occurances=0;@value.each {|index|
occurances+=((index < value)?1:0)
}
return occurances==1
when NON
return (@value.include?(value))?((@value.max < value)?false:true):(nil)
when NIL;return nil
end
end
def >(value)
case @type
when ANY
return (@value.include?(value))?(@value.max > value):(nil)
when ALL
occurances=1;@value.each {|index|
occurances+=((index > value)?1:0)
}
return occurances==@coelements
when ONE
occurances=0;@value.each {|index|
occurances+=((index > value)?1:0)
}
return occurances==1
when NON
return (@value.include?(value))?((@value.max > value)?false:true):(nil)
when NIL;return nil
end
end
def <=(value)
case @type
when ANY
return (@value.include?(value))?(@value.max <= value):(nil)
when ALL
occurances=1;@value.each {|index|
occurances+=((index <= value)?1:0)
}
return occurances==@coelements
when ONE
occurances=0;@value.each {|index|
occurances+=((index <= value)?1:0)
}
return occurances==1
when NON
return (@value.include?(value))?((@value.max <= value)?false:true):(nil)
when NIL;return nil
end
end
def >=(value)
case @type
when ANY
return (@value.include?(value))?(@value.max >= value):(nil)
when ALL
occurances=1;@value.each {|index|
occurances+=((index >= value)?1:0)
}
return occurances==@coelements
when ONE
occurances=0;@value.each {|index|
occurances+=((index >= value)?1:0)
return occurances==1
}
when NON
return (@value.include?(value))?((@value.max >= value)?false:true):(nil)
when NIL;return nil
end
end
def ===(value)
case @type
when ANY
return (@value.include?(value) and @value.at(@value.index(value))===value)
when ALL
occurances=1;@value.each {|index|
occurances+=((index===value)?1:0)
}
return occurances==@coelements
when ONE
occurances=0;@value.each {|index|
occurances+=((index===value)?1:0)
}
return occurances==1
when NON
return (@value.include?(value) and
@value.at(@value.index(value))===value)?false:true
when NIL;return nil
end
end
def to_a#override
return @value.to_a
end
def to_s#override
character=NIL;case @type
when ANY
character=ANY
when ALL
character=ALL
when ONE
character=ONE
when NON
character=NON
end
return @value.to_s.gsub(", ",character)
end
def to_i
@value.each {|i|(i = (i.to_i)) rescue i = 0}.to_a;return self
end
def to_f
@value.each {|i|(i = (i.to_f)) rescue i = 0.0}.to_a;return self
end
def <<(value)
@value.push(value);return self
end
def push_to_last!(value)
@value.push(value);return self
end
def pop_last!
return @value.pop
end
def pop_last_array!
@value.pop;return self
end
def type_change(new_type)
case new_type
when ANY
@type=ANY
when ALL
@type=ALL
when ONE
@type=ONE
when NON
@type=NON
else
@type=NIL
end
end
def elements?
@coelements.to_i
end
def clean!
@value.uniq!;return self
end
def pretty_puts #not needed, but cool for easy reading :3
self.forall {|i,j|puts "#{(j+1).to_s}" <<
format_number_th(j) << " possible value is #{i.to_s}" }
end
private#helpers
def format_number_th(j)#helper for pretty_puts
if [10,11,12].include?(j%100);return "th"
else;return (["st","nd","rd","th"].at((j%10>=4)?3:j%10)).to_s
end
end
end
-
I was considering using some simple metaprogramming to shorten the source for the class, it would probably half the size. However, slight beginning runtime pause of around .2 seconds for the addition of arbitrary methods to the class added directly to the temporary source file.
Besides considering that, I've made some more mixed progress with this lately concerning implementing all methods from String, Fixnum, Float, Array, Enumerable, and other classes. I'm at about 100 methods at the moment, I'm working on adding a few hundred more.
Edit: and made inline junction creation methods _much_ more friendly with beginning arguments.
-
Chicken nuggets == good
just seeing if anyone is awake, I have no replies and I feel sad :-/
-
Chicken nuggets == good
just seeing if anyone is awake, I have no replies and I feel sad :-/
I'm awake. I'm sorry you feel sad. But I don't program Ruby, so I can't help you.
-
Chicken nuggets == good
just seeing if anyone is awake, I have no replies and I feel sad :-/
I'm awake. I'm sorry you feel sad. But I don't program Ruby, so I can't help you.
No offense, but then what was the point of that post?
-
So could you do Junctions in TI Basic by comparing two lists? Or did I not understand the concept?
-
So could you do Junctions in TI Basic by comparing two lists? Or did I not understand the concept?
No, with junctions you would treat the entire list as one single value and compare that value against the other compared value.
-
How is the value calculated then? I guess I don't understand Junctions. :-\
-
How is the value calculated then? I guess I don't understand Junctions. :-\
Based on the ruleset. The value is compared against how many expressions between the value and the elements in the junction return true, so it basically compares it against all of them. If any of them make the expression true, and the type of the junction is ANY, it returns true. If one of them makes the thing true, and the type is ONE, it returns true, etc.
-
Ah, okay.
That's pretty cool. :)
-
Woah nice Ashbad! This reminds me when Tari (or was it Tanner? I don't really remember) made a Switch class for Python on Cemetech :)
-
Woah nice Ashbad! This reminds me when Tari (or was it Tanner? I don't really remember) made a Switch class for Python on Cemetech :)
Glad you like it :) yeah, I think tanner did the first one, and Kllrnohj came back the next day with an even more kick-ass one. Fortunately Ruby already has built-in capabilities for switches :P
-
Woah nice Ashbad! This reminds me when Tari (or was it Tanner? I don't really remember) made a Switch class for Python on Cemetech :)
Glad you like it :) yeah, I think tanner did the first one, and Kllrnohj came back the next day with an even more kick-ass one. Fortunately Ruby already has built-in capabilities for switches :P
I don't see the need for switches, but yeah it'd be good to have them default.
-
Woah nice Ashbad! This reminds me when Tari (or was it Tanner? I don't really remember) made a Switch class for Python on Cemetech :)
Glad you like it :) yeah, I think tanner did the first one, and Kllrnohj came back the next day with an even more kick-ass one. Fortunately Ruby already has built-in capabilities for switches :P
I don't see the need for switches, but yeah it'd be good to have them default.
Oh, switches are quite useful. I used them multiple times when writing the current version of this library ;)
-
Woah nice Ashbad! This reminds me when Tari (or was it Tanner? I don't really remember) made a Switch class for Python on Cemetech :)
Glad you like it :) yeah, I think tanner did the first one, and Kllrnohj came back the next day with an even more kick-ass one. Fortunately Ruby already has built-in capabilities for switches :P
I don't see the need for switches, but yeah it'd be good to have them default.
Oh, switches are quite useful. I used them multiple times when writing the current version of this library ;)
They are useful, but they are easily replaced by if's. Give me an example when I can't use an if, and switch is only option?
Switches are good for code organization and easier writing though :)
-
Yeah, switches are totally replaceable by if statements, and they are more limited since they must evaluate an expression first and the different branches are determined by the outcome of the result expression. However, in cases where you're doing things like "x==1", "elsif x==2", etc. Switches are slightly more optimized and easier to read.
-
Yeah, switches are totally replaceable by if statements, and they are more limited since they must evaluate an expression first and the different branches are determined by the outcome of the result expression. However, in cases where you're doing things like "x==1", "elsif x==2", etc. Switches are slightly more optimized and easier to read.
As an example:
Check if a var equals 1, 2 or 3 and if none print "Error".
if var==1
print "It's 1"
else-if var==2
print "It's 2"
else-if var==3
print "It's 3"
else:
print "Error"
switch var;
case 1:
print "It's 1"
case 2:
print "It's 2"
case 3:
print "It's 3"
default:
print "Error"
It's +- the same thing.
-
I like switches better, though. I was able to use them in C++ and found them to be really easy to use. :)