Author Topic: Ruby Junctions  (Read 5411 times)

0 Members and 1 Guest are viewing this topic.

Ashbad

  • Guest
Ruby Junctions
« 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:


Code: [Select]
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:

Code: [Select]
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:

Code: [Select]
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.

Ashbad

  • Guest
Re: Ruby Junctions
« Reply #1 on: July 06, 2011, 08:06:09 pm »
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:

Code: [Select]
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

Ashbad

  • Guest
Re: Ruby Junctions
« Reply #2 on: July 15, 2011, 10:34:38 am »
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.
« Last Edit: July 15, 2011, 10:38:33 am by Ashbad »

Ashbad

  • Guest
Re: Ruby Junctions
« Reply #3 on: July 15, 2011, 09:40:02 pm »
Chicken nuggets == good

just seeing if anyone is awake, I have no replies and I feel sad :-/
« Last Edit: July 15, 2011, 09:40:24 pm by Ashbad »

Offline jnesselr

  • King Graphmastur
  • LV11 Super Veteran (Next: 3000)
  • ***********
  • Posts: 2270
  • Rating: +81/-20
  • TAO == epic
    • View Profile
Re: Ruby Junctions
« Reply #4 on: July 16, 2011, 11:43:57 am »
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.

Ashbad

  • Guest
Re: Ruby Junctions
« Reply #5 on: July 23, 2011, 03:28:26 pm »
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?
« Last Edit: July 23, 2011, 10:17:02 pm by Ashbad »

Offline ztrumpet

  • The Rarely Active One
  • CoT Emeritus
  • LV13 Extreme Addict (Next: 9001)
  • *
  • Posts: 5712
  • Rating: +364/-4
  • If you see this, send me a PM. Just for fun.
    • View Profile
Re: Ruby Junctions
« Reply #6 on: July 23, 2011, 09:56:13 pm »
So could you do Junctions in TI Basic by comparing two lists?  Or did I not understand the concept?

Ashbad

  • Guest
Re: Ruby Junctions
« Reply #7 on: July 23, 2011, 10:15:57 pm »
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.

Offline ztrumpet

  • The Rarely Active One
  • CoT Emeritus
  • LV13 Extreme Addict (Next: 9001)
  • *
  • Posts: 5712
  • Rating: +364/-4
  • If you see this, send me a PM. Just for fun.
    • View Profile
Re: Ruby Junctions
« Reply #8 on: July 23, 2011, 10:25:09 pm »
How is the value calculated then?  I guess I don't understand Junctions. :-\

Ashbad

  • Guest
Re: Ruby Junctions
« Reply #9 on: July 23, 2011, 10:28:03 pm »
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.

Offline ztrumpet

  • The Rarely Active One
  • CoT Emeritus
  • LV13 Extreme Addict (Next: 9001)
  • *
  • Posts: 5712
  • Rating: +364/-4
  • If you see this, send me a PM. Just for fun.
    • View Profile
Re: Ruby Junctions
« Reply #10 on: July 23, 2011, 10:50:25 pm »
Ah, okay.
That's pretty cool. :)

Offline Munchor

  • LV13 Extreme Addict (Next: 9001)
  • *************
  • Posts: 6199
  • Rating: +295/-121
  • Code Recycler
    • View Profile
Re: Ruby Junctions
« Reply #11 on: July 24, 2011, 05:35:06 am »
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 :)

Ashbad

  • Guest
Re: Ruby Junctions
« Reply #12 on: July 24, 2011, 09:08:02 am »
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

Offline Munchor

  • LV13 Extreme Addict (Next: 9001)
  • *************
  • Posts: 6199
  • Rating: +295/-121
  • Code Recycler
    • View Profile
Re: Ruby Junctions
« Reply #13 on: July 24, 2011, 10:27:24 am »
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.

Ashbad

  • Guest
Re: Ruby Junctions
« Reply #14 on: July 24, 2011, 10:40:10 am »
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 ;)