setting local variables in a binding

Discussion in 'Ruby' started by Martin DeMello, Oct 6, 2010.

  1. Why does this not work?

    ruby-1.9.2-p0 > b = binding
    => #<Binding:0x9adc81c>
    ruby-1.9.2-p0 > eval("x = 5", b)
    => 5
    ruby-1.9.2-p0 > x
    NameError: undefined local variable or method `x' for main:Object
    from (irb):6
    from /home/martin/.rvm/rubies/ruby-1.9.2-p0/bin/irb:17:in `<main>'
    ruby-1.9.2-p0 > eval("x")
    NameError: undefined local variable or method `x' for main:Object
    from (irb):7:in `eval'
    from (irb):7:in `eval'
    from (irb):7
    from /home/martin/.rvm/rubies/ruby-1.9.2-p0/bin/irb:17:in `<main>'
    ruby-1.9.2-p0 > eval("x", b)
    => 5

    martin
     
    Martin DeMello, Oct 6, 2010
    #1
    1. Advertising

  2. Martin DeMello wrote:
    > Why does this not work?
    >
    > ruby-1.9.2-p0 > b = binding
    > => #<Binding:0x9adc81c>
    > ruby-1.9.2-p0 > eval("x = 5", b)
    > => 5
    > ruby-1.9.2-p0 > x
    > NameError: undefined local variable or method `x' for main:Object
    > from (irb):6
    > from /home/martin/.rvm/rubies/ruby-1.9.2-p0/bin/irb:17:in `<main>'


    irb(main):001:0> b = binding
    => #<Binding:0x7f9079ec7460>
    irb(main):002:0> eval("x = 5", b)
    => 5
    irb(main):003:0> x
    => 5
    irb(main):004:0> RUBY_DESCRIPTION
    => "ruby 1.8.7 (2010-01-10 patchlevel 249) [x86_64-linux]"

    It might be an irb-ism in 1.9. Have you tried it in a standalone .rb
    program?
    --
    Posted via http://www.ruby-forum.com/.
     
    Brian Candler, Oct 6, 2010
    #2
    1. Advertising

  3. On Wed, Oct 6, 2010 at 6:13 PM, Brian Candler <> wrote:
    >
    > It might be an irb-ism in 1.9. Have you tried it in a standalone .rb
    > program?


    Yes, didn't work there either. Didn't think to try 1.8 - wonder why
    the behaviour changed.

    martin
     
    Martin DeMello, Oct 6, 2010
    #3
  4. Martin DeMello

    John Sikora Guest

    Martin DeMello wrote:
    > Why does this not work?
    >
    > ruby-1.9.2-p0 > b = binding
    > => #<Binding:0x9adc81c>
    > ruby-1.9.2-p0 > eval("x = 5", b)
    > => 5
    > ruby-1.9.2-p0 > x
    > NameError: undefined local variable or method `x' for main:Object
    > from (irb):6
    > from /home/martin/.rvm/rubies/ruby-1.9.2-p0/bin/irb:17:in `<main>'
    > ruby-1.9.2-p0 > eval("x")
    > NameError: undefined local variable or method `x' for main:Object
    > from (irb):7:in `eval'
    > from (irb):7:in `eval'
    > from (irb):7
    > from /home/martin/.rvm/rubies/ruby-1.9.2-p0/bin/irb:17:in `<main>'
    > ruby-1.9.2-p0 > eval("x", b)
    > => 5
    >
    > martin


    Hmmm, it seems to work when x is already defined (in 1.9.2):

    irb(main):001:0> x=4
    => 4
    irb(main):002:0> b=binding
    => #<Binding:0xfa2ab0>
    irb(main):003:0> eval("x=5", b)
    => 5
    irb(main):004:0> x
    => 5
    irb(main):005:0>

    If I do not set x to a value beforehand, I get the same error. Do not
    know why though.

    js
    --
    Posted via http://www.ruby-forum.com/.
     
    John Sikora, Oct 6, 2010
    #4
  5. On Thu, Oct 7, 2010 at 12:38 AM, John Sikora <> wrote:
    > Hmmm, it seems to work when x is already defined (in 1.9.2):
    >
    > irb(main):001:0> x=4
    > => 4
    > irb(main):002:0> b=binding
    > => #<Binding:0xfa2ab0>
    > irb(main):003:0> eval("x=5", b)
    > => 5
    > irb(main):004:0> x
    > => 5
    > irb(main):005:0>
    >
    > If I do not set x to a value beforehand, I get the same error. Do not
    > know why though.


    Oddly enough, if x is defined beforehand you don't even need the binding:

    ruby-1.9.2-p0 > x = 5
    => 5
    ruby-1.9.2-p0 > eval "x = 42"
    => 42
    ruby-1.9.2-p0 > x
    => 42

    martin
     
    Martin DeMello, Oct 6, 2010
    #5
  6. Martin DeMello

    Tony Arcieri Guest

    [Note: parts of this message were removed to make it a legal post.]

    Note that "binding" is the default argument of eval if you don't specify
    one, so capturing binding as a variable and passing it explicitly is
    redundant.

    On Wed, Oct 6, 2010 at 1:53 PM, Martin DeMello <>wrote:

    > On Thu, Oct 7, 2010 at 12:38 AM, John Sikora <>
    > wrote:
    > > Hmmm, it seems to work when x is already defined (in 1.9.2):
    > >
    > > irb(main):001:0> x=4
    > > => 4
    > > irb(main):002:0> b=binding
    > > => #<Binding:0xfa2ab0>
    > > irb(main):003:0> eval("x=5", b)
    > > => 5
    > > irb(main):004:0> x
    > > => 5
    > > irb(main):005:0>
    > >
    > > If I do not set x to a value beforehand, I get the same error. Do not
    > > know why though.

    >
    > Oddly enough, if x is defined beforehand you don't even need the binding:
    >
    > ruby-1.9.2-p0 > x = 5
    > => 5
    > ruby-1.9.2-p0 > eval "x = 42"
    > => 42
    > ruby-1.9.2-p0 > x
    > => 42
    >
    > martin
    >
    >



    --
    Tony Arcieri
    Medioh! A Kudelski Brand
     
    Tony Arcieri, Oct 6, 2010
    #6
  7. Martin DeMello

    John Sikora Guest

    Tony Arcieri wrote:
    > Note that "binding" is the default argument of eval if you don't specify
    > one, so capturing binding as a variable and passing it explicitly is
    > redundant.


    You are correct but the problem 'persists'. The following irb session in
    1.9.2 shows the same thing without the binding:

    irb(main):001:0> eval("x=5")
    => 5
    irb(main):002:0> x
    NameError: undefined local variable or method `x' for main:Object
    from (irb):2
    from C:/Ruby192/bin/irb:12:in `<main>'
    irb(main):003:0> x=4
    => 4
    irb(main):004:0> eval("x=5")
    => 5
    irb(main):005:0> x
    => 5
    irb(main):006:0>


    In order for this to work, the x needs to be @x per the following irb
    session (again 1.9.2):

    irb(main):001:0> eval("@x=5")
    => 5
    irb(main):002:0> @x
    => 5
    irb(main):003:0>

    The behavior seen in the first irb session was the same in 1.8.6, 1.9.1,
    and 1.9.2 when run standalone. However, it works in irb for 1.8.6 (x w/o
    the @).

    I have sometimes seen 'local' or 'unassociated' instance variables (not
    sure if my terminology is correct) in example programs and I have
    wondered why they are used. I guess this is one reason although most
    people don't like to use eval.

    I use eval in personal scripts since it is so convenient. Had to go back
    and look at some code to remind myself how I got around the behavior
    seen in the first irb session. I don't remember if I read about using
    the local instance variable or just played around with it until I got it
    to work.

    js
    --
    Posted via http://www.ruby-forum.com/.
     
    John Sikora, Oct 7, 2010
    #7
  8. On Thu, Oct 7, 2010 at 4:19 AM, John Sikora <> wrote:
    > Tony Arcieri wrote:
    >> Note that "binding" is the default argument of eval if you don't specify
    >> one, so capturing binding as a variable and passing it explicitly is
    >> redundant.

    >
    > You are correct but the problem 'persists'. The following irb session in
    > 1.9.2 shows the same thing without the binding:
    >
    > irb(main):001:0> eval("x=3D5")
    > =3D> 5
    > irb(main):002:0> x
    > NameError: undefined local variable or method `x' for main:Object
    > =A0 =A0 =A0 =A0from (irb):2
    > =A0 =A0 =A0 =A0from C:/Ruby192/bin/irb:12:in `<main>'
    > irb(main):003:0> x=3D4
    > =3D> 4
    > irb(main):004:0> eval("x=3D5")
    > =3D> 5
    > irb(main):005:0> x
    > =3D> 5
    > irb(main):006:0>


    The reason is that Ruby looks at the source code to find out which are
    local variables. This is basically the same as doing

    13:00:57 ~$ ruby19 -e 'def f;puts "x=3D10";p x end;f'
    x=3D10
    -e:1:in `f': undefined local variable or method `x' for main:Object (NameEr=
    ror)
    from -e:1:in `<main>'
    13:00:59 ~$ ruby19 -e 'def f;eval "x=3D10";p x end;f'
    -e:1:in `f': undefined local variable or method `x' for main:Object (NameEr=
    ror)
    from -e:1:in `<main>'
    13:01:02 ~$

    In other words: the interpreter does not know that there is an
    assignment to x in the eval code so x is undefined.

    > In order for this to work, the x needs to be @x per the following irb
    > session (again 1.9.2):
    >
    > irb(main):001:0> eval("@x=3D5")
    > =3D> 5
    > irb(main):002:0> @x
    > =3D> 5
    > irb(main):003:0>


    That's an instance variable which is a completely different beast.

    > The behavior seen in the first irb session was the same in 1.8.6, 1.9.1,
    > and 1.9.2 when run standalone. However, it works in irb for 1.8.6 (x w/o
    > the @).


    irb has some specialities with handling local variables which is the
    reason it behaves differently. The reason is that it will not parse
    the code in one go because it only ever sees a chunk (every time you
    hit enter). If the interpreter's regular behavior would be retained
    you could not use local variables in irb which would be inconvenient.

    > I have sometimes seen 'local' or 'unassociated' instance variables (not
    > sure if my terminology is correct)


    Not sure what you actually mean by that.

    > in example programs and I have
    > wondered why they are used. I guess this is one reason although most
    > people don't like to use eval.


    The reasons against eval are more in the area of safety and
    performance. If you know which variable you want to assign you can
    use it directly. If you do not know you typically use a Hash.

    > I use eval in personal scripts since it is so convenient. Had to go back
    > and look at some code to remind myself how I got around the behavior
    > seen in the first irb session. I don't remember if I read about using
    > the local instance variable or just played around with it until I got it
    > to work.


    What do you use eval for? In cases where the set of variables is not
    fixed at coding time chances are that you usually want a Hash. Using
    eval should be really be rare and for specific cases but not an
    everyday habit IMHO.

    Kind regards

    robert

    --=20
    remember.guy do |as, often| as.you_can - without end
    http://blog.rubybestpractices.com/
     
    Robert Klemme, Oct 7, 2010
    #8
  9. Martin DeMello

    John Sikora Guest

    Robert Klemme wrote:
    > On Thu, Oct 7, 2010 at 4:19 AM, John Sikora <>
    > wrote:
    >> I have sometimes seen 'local' or 'unassociated' instance variables (not
    >> sure if my terminology is correct)

    >
    > Not sure what you actually mean by that.


    I mean that I have seen examples where people use, say '@var', outside
    of any class, module, or method; in the 'main body' of the program. I
    would think that they would just use 'var' instead. I do not see why it
    would be advantageous to use '@var'. Is there a reason to do this?

    >> I use eval in personal scripts since it is so convenient. Had to go back
    >> and look at some code to remind myself how I got around the behavior
    >> seen in the first irb session. I don't remember if I read about using
    >> the local instance variable or just played around with it until I got it
    >> to work.

    >
    > What do you use eval for? In cases where the set of variables is not
    > fixed at coding time chances are that you usually want a Hash. Using
    > eval should be really be rare and for specific cases but not an
    > everyday habit IMHO.
    >


    I mainly use 'eval' to evaluate syntactic sugar expressions for
    enumerable methods such as 'sort', 'find_all', 'all?', etc. I have
    created a Module with enumerable classes (they have an 'each' method)
    and I have added syntactic sugar to some of the methods. So I can be
    lazy (and quick) and write things like:

    Slot.enum.find_all(‘:slot_num >= 8’, ‘:slot_num <= 22’, “:rate ==
    ‘ufec’”).all?(‘:errors == 0’)

    As you can probably guess, anything with a ':' before it is an attribute
    that gets evaluated. Then the expression gets evaluated. I know I could
    use ‘send’ for the first evaluation, but sometimes I chain together
    attributes of attributes, use variables in the expressions, etc., and it
    gets more complicated. 'eval' is my easy way out (I’m just a hardware
    engineer trying to make my life easier). In case anyone is wondering,
    the parameters given in 'find_all' have the option of an ‘and’ or ‘or’
    function. Default is ‘and’ as in the example.

    Speed is generally not an issue and I am somewhat aware of the dangers
    of using 'eval'. I try to isolate the 'eval' expressions by limiting the
    user (me, mostly) to commands that have pre-programmmed options. A
    malicious user could probably find a way around it though since I am not
    too savvy in this area.

    I am always looking to improve the code and if anyone can suggest ways
    of eliminating the 'eval' statements while keeping the same
    functionality, I will certainly listen. I am also trying to read up on
    the latest dynamic techniques that rely less upon 'eval'.

    js
    --
    Posted via http://www.ruby-forum.com/.
     
    John Sikora, Oct 8, 2010
    #9
  10. On Fri, Oct 8, 2010 at 6:06 AM, John Sikora <> wrote:
    > Robert Klemme wrote:
    >> On Thu, Oct 7, 2010 at 4:19 AM, John Sikora <>
    >> wrote:
    >>> I have sometimes seen 'local' or 'unassociated' instance variables (not
    >>> sure if my terminology is correct)

    >>
    >> Not sure what you actually mean by that.

    >
    > I mean that I have seen examples where people use, say '@var', outside
    > of any class, module, or method; in the 'main body' of the program. I
    > would think that they would just use 'var' instead. I do not see why it
    > would be advantageous to use '@var'. Is there a reason to do this?
    >
    >>> I use eval in personal scripts since it is so convenient. Had to go bac=

    k
    >>> and look at some code to remind myself how I got around the behavior
    >>> seen in the first irb session. I don't remember if I read about using
    >>> the local instance variable or just played around with it until I got i=

    t
    >>> to work.

    >>
    >> What do you use eval for? =A0In cases where the set of variables is not
    >> fixed at coding time chances are that you usually want a Hash. =A0Using
    >> eval should be really be rare and for specific cases but not an
    >> everyday habit IMHO.

    >
    > I mainly use 'eval' to evaluate syntactic sugar expressions for
    > enumerable methods such as 'sort', 'find_all', 'all?', etc. I have
    > created a Module with enumerable classes (they have an 'each' method)


    So you have something like

    class Foo
    def self.each
    yield "whatever"
    self
    end
    end

    ? Did you also do

    class Foo
    def self.each
    yield "whatever"
    end

    extend Enumerable
    end

    ? That would give you all methods like #find_all etc. for free.

    > and I have added syntactic sugar to some of the methods. So I can be
    > lazy (and quick) and write things like:
    >
    > Slot.enum.find_all(=91:slot_num >=3D 8=92, =91:slot_num <=3D 22=92, =93:r=

    ate =3D=3D
    > =91ufec=92=94).all?(=91:errors =3D=3D 0=92)


    Is this equivalent to

    enum.find_all {|s| (8..22) =3D=3D=3D s.slot_num && s.rate =3D=3D 'ufec'}.al=
    l?
    {|s| e.errors =3D=3D 0}

    ? If yes, I do not really see the advantage of your approach. It's
    slower needs a similar amount of typing and will detect errors later
    (at execution time vs. at parse time).

    > As you can probably guess, anything with a ':' before it is an attribute
    > that gets evaluated. Then the expression gets evaluated. I know I could
    > use =91send=92 for the first evaluation, but sometimes I chain together
    > attributes of attributes, use variables in the expressions, etc., and it
    > gets more complicated. 'eval' is my easy way out (I=92m just a hardware
    > engineer trying to make my life easier). In case anyone is wondering,
    > the parameters given in 'find_all' have the option of an =91and=92 or =91=

    or=92
    > function. Default is =91and=92 as in the example.


    If my assumption above is correct I do not understand where the "easy
    way out" is.

    > Speed is generally not an issue and I am somewhat aware of the dangers
    > of using 'eval'. I try to isolate the 'eval' expressions by limiting the
    > user (me, mostly) to commands that have pre-programmmed options. A
    > malicious user could probably find a way around it though since I am not
    > too savvy in this area.
    >
    > I am always looking to improve the code and if anyone can suggest ways
    > of eliminating the 'eval' statements while keeping the same
    > functionality, I will certainly listen. I am also trying to read up on
    > the latest dynamic techniques that rely less upon 'eval'.


    (see above)

    Kind regards

    robert

    --=20
    remember.guy do |as, often| as.you_can - without end
    http://blog.rubybestpractices.com/
     
    Robert Klemme, Oct 8, 2010
    #10
  11. On 09.10.2010 08:25, John Sikora wrote:

    > Robert Klemme wrote:
    >> On Fri, Oct 8, 2010 at 6:06 AM, John Sikora<>
    >> wrote:
    >>
    >> So you have something like
    >>
    >> class Foo
    >> def self.each
    >> yield "whatever"
    >> self
    >> end
    >> end
    >>
    >> ? Did you also do
    >>
    >> class Foo
    >> def self.each
    >> yield "whatever"
    >> end
    >>
    >> extend Enumerable
    >> end
    >>
    >> ? That would give you all methods like #find_all etc. for free.

    >
    > Robert,
    > Note that I have kept the code and comments to a minimum here as I did
    > not want the post to be even longer. I know that you are quite
    > knowledgeable and that you should have little trouble following what I
    > am doing - although you might not necessarily understand why I chose to
    > do it this way:


    Fluent speaking of Ruby makes it somewhat easier but even for me the
    length of the post needs some digesting. So I won't come up with a full
    coverage of everything you wrote for now. I hope my remarks are useful
    nevertheless.

    > class Base
    > @class_all_enum_objects = Array.new
    >
    > class<< self
    > attr_accessor :class_all_enum_objects
    > end
    >
    > def initialize
    > self.class.class_all_enum_objects<< self
    > end
    >
    > def self.enum
    > EnumeratorEOC.new(self, :each)
    > end
    >
    > def self.each(&code_block)
    > self.class_all_enum_objects.each do |object|
    > yield object
    > end
    > end
    > end
    >
    > module EnumeratorModifierEOC


    This should rather be a EnumerableModifierEOC IMHO since #find is
    defined in Enumerable and this module contains all the enumerating
    methods which are based on #each (e.g. find_all, inject, map).

    > def find_all(*condition_strings,&code_block)
    > if block_given?
    > find_array = super
    > else
    > find_array = # process condition strings (syntactic sugar) here.
    > end
    > ArrayEOC.new(find_array)
    > end
    >
    > # code for other methods such as sort, min, max, all?, etc. here.
    > end
    >
    > class EnumeratorEOC< Enumerator # avoids modifying Enumerator.
    > include EnumeratorModifierEOC
    >
    > def initilize(object, method_sym)
    > super
    > end
    > end
    >
    > class ArrayEOC< Array # avoids modifying Array.
    > include EnumeratorModifierEOC
    >
    > def initialize(array = [])
    > super(array)
    > end
    > end


    Although no additional methods are shown (these are presented below if I
    am not mistaken) the purpose of EnumeratorEOC and ArrayEOC is mainly to
    augment standard types with additional functionality it seems. Correct?

    > To suit my purposes: 1) I want the class itself to keep track of all of
    > the class instances, as shown above, 2) if an attribute has more than
    > one value or is more complex than an Array, I prefer to define a class
    > for it (inherited from Base) and assign attributes, 3) I want syntactic
    > sugar for enumerables, 4) especially for sort, including default sort
    > parameters (min and max get defaults for free as a result of the
    > necessary<=> definition). 5) I want to be able to define new functions
    > and to be able to use syntactic sugar with them. See below. 6) I also
    > keep track of descendants of classes although this is only partly shown
    > below. There is a little more on this at the end if anyone is still
    > reading.
    >
    >>
    >>> and I have added syntactic sugar to some of the methods. So I can be
    >>> lazy (and quick) and write things like:
    >>>
    >>> Slot.enum.find_all(�:slot_num>= 8�, �:slot_num<= 22�, �:rate ==
    >>> �ufec��).all?(�:errors == 0�)

    >>
    >> Is this equivalent to
    >>
    >> enum.find_all {|s| (8..22) === s.slot_num&& s.rate == 'ufec'}.all?
    >> {|s| e.errors == 0}

    >
    > Yes, you are correct (e.errors is a typo - should be s.errors), they are
    > equivalent.


    Sorry, that was a typo. Thanks for catching that.

    >> ? If yes, I do not really see the advantage of your approach. It's
    >> slower needs a similar amount of typing and will detect errors later
    >> (at execution time vs. at parse time).
    >>>

    >> If my assumption above is correct I do not understand where the "easy
    >> way out" is.
    >>

    > I think 'sort' is the main reason for my 'easy way out'. Plus the use of
    > additional functions. To illustrate, I have included in the code below
    > one such function, 'sum_total_eoc' which is just 'inject' set up for
    > addition, but with the ability to use syntactic sugar (of course).


    In the code above I do not see any usage of #sort so I don't really see
    the easy way out in the example above. Even if you would need the
    String argument eval solution for sorting it would not be necessary for
    the code above which uses #find_all and #all?.

    > You may or may not agree with my 'easy way out', but here goes:
    >
    > First, a short explanation: I have attribute and expression comparables
    > for each class. The comparables are compared in the order that they are
    > given in attr_accessor. There is also a class method called
    > set_comparables_order that allows the user to re-arrange the order if
    > desired (it also allows expressions, not just attributes). Child classes
    > inherit the comparables and their order from the parent class. Any new
    > attributes defined in attr_accessor in the child class are added to the
    > comparables up front.


    A problem of your design is that you change the classes idea of default
    ordering. What I mean is this: you need to modify a class to achieve a
    particular ordering. Now the standard behavior of sorting has changed
    and you can never tell what ordering you will get by only looking at a
    particular piece of code which only contains the call to #sort.

    IMHO it would be better to not allow this but instead create a mechanism
    which makes the ordering explicit. Here is one approach

    module EnumeratorModifierEOC # or Enumerable
    def sort_by_fields(*fields)
    sort_by {|x| fields.map {|f| x.send(f)}}
    end
    end

    This might not be the most efficient solution because of the repeated
    invocation of #map. Alternatively you can easily stick with #sort_by by
    doing

    enum.sort_by {|x| [x.field_1, x.field_2]}

    which is pretty short, readable and efficient.

    You could improve this by creating something like a Sorter:

    def Sorter(*fields)
    eval "lambda {|enum| enum.sort_by{|x| [#{fields.map {|f|
    "x.#{f}"}.join(', ')}]}}"
    end

    MAIN = Sorter:)foo, :bar)
    ...
    sorted = MAIN[enum]

    Now you actually have your eval but you use it to compile code which you
    can then use more often efficiently. :)

    You can even make this more modular by removing the sorting and
    compiling only the field extraction:

    def FieldExtractor(*field)
    eval "lambda {|x| [#{fields.map {|f| "x.#{f}"}.join(', ')}]}"
    end

    MAIN = FieldExtractor:)foo, :bar)
    ...
    sorted = enum.sort_by &MAIN

    > OK, so what can I do with this? The following are some simplified
    > examples:
    >
    > class Slot< EnumerableObjectClass::Base
    > attr_accessor :slot_num, :rate, :error_info #<--Array of ErrorInfo
    > objects.
    >
    > def initialize(slot_num, rate, error_info)
    > super()
    > @slot_num, @rate, @error_info = slot_num, rate, error_info
    > end
    > end
    >
    > class ErrorInfo< EnumerableObjectClass::Base
    > attr_accessor :time_stamp, :num_errors
    >
    > def initialize(time_stamp, num_errors)
    > super()
    > @time_stamp, @num_errors = time_stamp, num_errors
    > end
    > end
    >
    > Slot.new(3, 'efec', [ErrorInfo.new('10-07-10', 500),
    > ErrorInfo.new('10-08-10', 1000)])
    > Slot.new(1, 'ufec', [ErrorInfo.new('10-04-10', 1000),
    > ErrorInfo.new('10-05-10', 2000)])
    > Slot.new(8, 'ufec', [ErrorInfo.new('10-07-10', 1500),
    > ErrorInfo.new('10-08-10', 1200)])
    > Slot.new(4, 'efec', [ErrorInfo.new('10-07-10', 3500)])
    > Slot.enum{}.find(':slot_num == 4').error_info<<
    > ErrorInfo.new('10-08-10', 2500)
    >
    > -------------
    > # note - most of the examples below do not use variables. However, this
    > is not a limitation and a couple of examples are given that use
    > variables.
    >
    > # default sort is sorted by :slot_num, etc. - order of attr_accessor
    > p Slot.enum.sort.collect:)slot_num)
    > # => [[1], [3], [4], [8]]
    >
    > # sort by total errors.
    > p
    > Slot.enum.sort(':error_info.sum_total_eoc:)num_errors)').collect:)rate,
    > ':error_info.sum_total_eoc:)num_errors)')
    > # => [["efec", 1500], ["ufec", 2700], ["ufec", 3000], ["efec", 6000]]
    >
    > # sort by rate, total errors.
    > p Slot.enum.sort:)rate,
    > ':error_info.sum_total_eoc:)num_errors)').collect:)rate,
    > ':error_info.sum_total_eoc:)num_errors)')
    > # => [["efec", 1500], ["efec", 6000], ["ufec", 2700], ["ufec", 3000]]
    >
    > # sort by rate, total errors, with total errors sorted high to low by
    > ending with '___' triple underscore.
    > p Slot.enum.sort:)rate,
    > ':error_info.sum_total_eoc:)num_errors)___').collect:)rate,
    > ':error_info.sum_total_eoc:)num_errors)', :slot_num)
    > # => [["efec", 6000, 4], ["efec", 1500, 3], ["ufec", 3000, 1], ["ufec",
    > 2700,8]]
    >
    > I think that the above would be hard to do in a one-liner w/o syntactic
    > sugar, so this is my 'easy way out' (although I would not be surprised
    > if you came up with one Robert). I think this is pretty easy and it
    > saves me time.


    The tricky thing I did not consider yet is that you have nested access
    to fields and not just one level. In my world this would look like

    p Slot.enum.sort_by {|x| [x.rate,
    x.error_info.sum_total_eoc:)num_errors)___]}.
    map {|x| [x.:rate, x.error_info.sum_total_eoc:)num_errors), x.slot_num]}

    That's certainly not nice to squeeze on one line but your solution does
    not look that much shorter.

    > # total errors of all slots.
    > p Slot.enum.sum_total_eoc(':error_info.sum_total_eoc:)num_errors)')
    > # => 13200
    >
    > # slots that have>= 3000 total errors.
    > p Slot.enum.find_all(':error_info.sum_total_eoc:)num_errors)>=
    > 3000').collect:)slot_num)
    > # => [[1], [4]]
    >
    > # use a variable for sort parameter.
    > var = ':slot_num'
    > p Slot.enum.sort("#{var}").collect("#{var}")
    > # => [[1], [3], [4], [8]]
    >
    > # use a variable w/o interpolation - must pass a binding with enum{}
    > (which is where this topic started by the way). It is carried along as
    > an attribute to ArrayEOC and/or EnumeratorEOC, similar to coparables.
    > var = ':slot_num'
    > p Slot.enum{}.sort(var).collect(var)
    > # => [[1], [3], [4], [8]]
    >
    > # slots that have>= 2500 errors in a single time period.
    > p Slot.enum.find_all(":error_info.any?:)num_errors>=
    > 2500)").collect:)slot_num)
    > # => Exceptions galore! my simple 'parser' breaks down. will have touse
    > the 'old fashioned way' of a code block. maybe I will try to get this to
    > work in the future.
    >
    > In case anyone is wondering, if data is input as an Array, it comes out
    > as an ArrayEOC the first time it is read. In the simplified code above,
    > I show a call to super in attr_accessor, but really for the reader part,
    > I add a line that converts an Array to an ArrayEOC. I do this so I can
    > use the ArrayEOC methods as mofified since I want to keep Array's
    > methods unmodified.
    >
    > Since I already use self.inherited, I am able to keep track of
    > descendants (a recursive jaunt through the child classes using inject to
    > build an array), so there is enum_only{} which enumerates only the given
    > class, and enum{} which enumerates the given class and all descendants.
    > I do this because I have a number of units of similar type which follow
    > the classic inheritance OO model (even though it is apparent from posts
    > that this has fallen out of favor). Oh well, it seems that I am behind
    > the times on a lot of things.
    >
    > Oh, and since Ruby is dynamic, I had to modify remove_method and
    > undefine_method to remove any comparables, if applicable (there is a
    > difference between the two however). And if there are subsequent calls
    > to attr_accessor, I have to add those methods to the class comparables
    > in the class and any descendant classes. I allow the user to reset the
    > comparables to those given in the class and its ancestors. I also tried
    > to make the code reflective, by making the comparables, child_classes,
    > descndants, etc. availble.


    Your need to redefine remove_method etc. is fallout of your design
    decision to change the default ordering. As I said, I believe there are
    better and more efficient designs.

    > Writing this code has certainly been fun and
    > educational!


    That's good!

    There are still some things that I didn't yet wrap my head around: why
    do you want to make classes keep track of all their instances? This
    essentially makes classes global variables - but less obvious. During
    the course of a program usually you create instances and let forget them
    again. But in your situation you will keep all instances around and
    there is no clear demarcation. If you want automated tracking of
    instances there are other options, e.g.

    class InstanceTracker
    def initialize
    @all = Hash.new {|h,k| h[k] = []}
    end

    def new(cl,*args, &b)
    @all[cl] << cl.new(*args, &b)
    end

    def clear
    @all.clear
    self
    end

    def clear_class(cl)
    @all.delete(cl)
    self
    end

    include Enumerable

    def each(&b)
    if b
    @all.each {|k,v| v.each(&b)}
    self
    else
    Enumerator.new(self, :each)
    end
    end

    def each_class(cl, &b)
    if b
    @all[cl].each(&b)
    self
    else
    Enumerator.new(self, :each_class, cl)
    end
    end

    def each_sub_classes(cl, &b)
    if b
    @all.each {|k,v| v.each(&b) if k <= cl}
    self
    else
    Enumerator.new(self, :each_sub_classes, cl)
    end
    end
    end

    it = InstanceTracker.new

    it.new(Foo, 1, 2)
    it.each_class(Foo).find_all {|f| f.size > 0}

    etc.

    Kind regards

    robert

    --
    remember.guy do |as, often| as.you_can - without end
    http://blog.rubybestpractices.com/
     
    Robert Klemme, Oct 10, 2010
    #11
  12. Martin DeMello

    John Sikora Guest

    Robert Klemme wrote in post #947187:
    > Fluent speaking of Ruby makes it somewhat easier but even for me the
    > length of the post needs some digesting. So I won't come up with a full
    > coverage of everything you wrote for now. I hope my remarks are useful
    > nevertheless.


    Your remarks are always useful, and I appreciate them.

    >> module EnumeratorModifierEOC

    >
    > This should rather be a EnumerableModifierEOC IMHO since #find is
    > defined in Enumerable and this module contains all the enumerating
    > methods which are based on #each (e.g. find_all, inject, map).
    >


    True, especially since I also include the module in a subclass of Array,
    not just a subclass of Enumerator.

    > Although no additional methods are shown (these are presented below if I
    > am not mistaken) the purpose of EnumeratorEOC and ArrayEOC is mainly to
    > augment standard types with additional functionality it seems. Correct?


    Correct. All of the modified methods such as sort, find_all, collect,
    all?, etc. are in EnumeratorModifierEOC although I presented only one or
    two methods. I originally had modified Array and Enumerator and aliased
    the methods for normal code block use (non syntactic sugar), but after
    some correspondence in this forum decided that it was not a good idea to
    do that. So now I modify the methods in a subclass and call super to
    pass on the code block if syntactic sugar (passed parameter string(s) or
    symbol(s)) is not used.

    > A problem of your design is that you change the classes idea of default
    > ordering. What I mean is this: you need to modify a class to achieve a
    > particular ordering. Now the standard behavior of sorting has changed...


    I do not need to modify a class to achieve a particular ordering, I need
    to define a class. The default ordering is set during the class
    definition with attr_accessor and set_comparables_order. However, if I
    do modify the class (with the same two methods), the default ordering
    will change.

    Or did you mean that a subclass can / will have a different default sort
    order than it's superclass? This can certainly be the case and is
    actually encouraged to add flexibility. Are these element of a bad
    design? If so, why? (You can be brief, hopefully I will understand.)

    > ...and you can never tell what ordering you will get by only looking at a
    > particular piece of code which only contains the call to #sort.


    True, you would have to look at the attr_accessor and
    set_comparables_order methods elsewhere in the code (possibly multiple
    places). If these have been modified dynamically, the
    ClassName#comparables method can be used to return the default
    comparables.

    > enum.sort_by {|x| [x.field_1, x.field_2]}
    >
    > which is pretty short, readable and efficient.


    When I was looking over the Enumerable methods that I wanted to modify,
    I skipped sort_by for some reason. You are right, it is pretty short and
    readable. May need to take another look at it.

    > Your need to redefine remove_method etc. is fallout of your design
    > decision to change the default ordering. As I said, I believe there are
    > better and more efficient designs.


    True. I said this to make the point that I have thought of things that I
    need to do to try to keep things from breaking since Ruby is dynamic. I
    think you are saying that by coding this way, I am making it tough on
    myself since Ruby is dynamic. Hmmm, need to think about this, because I
    know that there will be cases out there that I do not think to cover. I
    guess this is a way to tell a good design from a bad one.

    > There are still some things that I didn't yet wrap my head around: why
    > do you want to make classes keep track of all their instances?


    When I was learing Ruby, I came across ObjectSpace.each_object and
    thought that since Ruby makes this method available, why not use it
    instead of setting up my own containers? So early versions of the code
    used ObjectSpace. Then I discovered self.inherited and class instance
    variables. I decided to use self.inherited to pass along the values of
    certain class instance variables that I want inherited (and slightly
    modified for that subclass). Since keeping track of child classes was
    fairly easy with self.inherited and I could also use it to initialize
    @class_all_enum_objects, I dropped the use of ObjectSpace. It seems like
    ObjectSpace would be less efficient too.

    > This essentially makes classes global variables - but less obvious.


    Never thought of that. See my comments below on my lack of having to
    interface with other users' code.

    > During the course of a program usually you create instances and let forget them
    > again. But in your situation you will keep all instances around and
    > there is no clear demarcation.


    I do provide a delete method that will delete an EOC object from the
    @class_all_enum_objects class instance variable. I also even provide a
    recursive delete object method which deletes the EOC object and any EOC
    object that it finds as an attribute (for example, a Slot object can
    have an attribute :xp which is an Xp object (transponder, in case you
    are wondering).

    >If you want automated tracking of
    > instances there are other options, e.g.
    >
    > class InstanceTracker
    > ... lines of code
    > > end

    >
    > it = InstanceTracker.new
    >
    > it.new(Foo, 1, 2)
    > it.each_class(Foo).find_all {|f| f.size > 0}
    >


    I see, but why have a seperate class for this? Aren't you doing the same
    thing?

    I think that the reason that my code is the way it is, is partially due
    to the fact that I am writing my code in isolation; I do
    not have to interface to other code to perform a broader function. In
    fact, I have no experience writing code with any kind of interface
    (explains a lot, huh?). Well, I guess I do use gems, so I am not in
    total isolation, and at least I have given it some thought since I
    turned away from mofidying Array and Enumerator directly.

    js

    --
    Posted via http://www.ruby-forum.com/.
     
    John Sikora, Oct 13, 2010
    #12
  13. Martin DeMello

    John Sikora Guest

    By the way, I've been busy and not checking the forums too much lately.
    However, I just noticed the concurrent thread of:

    sort_by: multiple fields with reverse sort.

    http://www.ruby-forum.com/topic/224672#new

    In my world I would do:

    class B < EnumerableObjectClass::Base
    attr_accessor :field_1, :field_0, :field_2 # sets default sort order.

    def initialize(field_0, field_1, field_2)
    super()
    @field_0, @field_1, @field_2 = field_0, field_1, field_2
    end
    end

    B.new("radio", 30, 5)
    B.new("radio", 20, 5)
    B.new("archie", 20, 5)
    B.new("newton", 10, 3)

    # default sort - :field_1, field_0, field_2 order, normal direction.
    p B.enum{}.sort.collect:)field_0, :field_1, :field_2)
    # => [["newton", 10, 3], ["archie", 20, 5], ["radio", 20, 5], ["radio",
    30, 5]]

    # sort by field_1 reverse, field_0 normal (same as the OP 3rd sort_by in
    that thread).
    p B.enum{}.sort(':field_1___', :field_0).collect:)field_0, :field_1,
    :field_2)
    # => [["radio", 30, 5], ["archie", 20, 5], ["radio", 20, 5], ["newton",
    10, 3]]

    # sort by field_0 reverse, field_2 normal, field_1 reverse.
    p B.enum{}.sort(':field_0___', :field_2,
    ':field_1___').collect:)field_0, :field_1, :field_2)
    # => [["radio", 30, 5], ["radio", 20, 5], ["newton", 10, 3], ["archie",
    20, 5]]

    Easy! Note the triple underscore suffix denotes reverse order sorting
    for that parameter. But this code uses the evil eval.

    js

    --
    Posted via http://www.ruby-forum.com/.
     
    John Sikora, Oct 14, 2010
    #13
  14. In case you get two copies of this: First send attempt was returned by
    a spamassassin crash...

    On Wed, Oct 13, 2010 at 9:23 PM, John Sikora <> wrote:
    > Robert Klemme wrote in post #947187:
    >> Fluent speaking of Ruby makes it somewhat easier but even for me the
    >> length of the post needs some digesting. =A0So I won't come up with a fu=

    ll
    >> coverage of everything you wrote for now. =A0I hope my remarks are usefu=

    l
    >> nevertheless.

    >
    > Your remarks are always useful, and I appreciate them.


    Thanks for the feedback!

    >> A problem of your design is that you change the classes idea of default
    >> ordering. =A0What I mean is this: you need to modify a class to achieve =

    a
    >> particular ordering. =A0Now the standard behavior of sorting has changed=

    ...
    >
    > I do not need to modify a class to achieve a particular ordering, I need
    > to define a class. The default ordering is set during the class
    > definition with attr_accessor and set_comparables_order. However, if I
    > do modify the class (with the same two methods), the default ordering
    > will change.


    That's what I mean. =A0Maybe renaming "set_comparables" to something
    else is enough (e.g. "attr_sort_order" which IMHO makes it look more
    like a keyword like "private"). =A0The name "set_comparables" seems to
    suggest that the order can be changed at will. =A0People might be
    inclined to do that multiple times in a program and since at any point
    in time there is only one such order per class this also poses issues
    for multithreaded programs (assuming different ordering would be
    employed by different threads). =A0An alternative would be to allow to
    call set_comparables at most once per class.

    > Or did you mean that a subclass can / will have a different default sort
    > order than it's superclass? This can certainly be the case and is
    > actually encouraged to add flexibility. Are these element of a bad
    > design? If so, why? (You can be brief, hopefully I will understand.)


    No, the issue I am seeing has nothing to do with inheritance. =A0See above.

    >> ...and you can never tell what ordering you will get by only looking at =

    a
    >> particular piece of code which only contains the call to #sort.

    >
    > True, you would have to look at the attr_accessor and
    > set_comparables_order methods elsewhere in the code (possibly multiple
    > places). If these have been modified dynamically, the
    > ClassName#comparables method can be used to return the default
    > comparables.


    My point is that sort order is something that belongs to a particular
    ordering, i.e. the place in code that uses sorting. =A0If you make this
    a property of the class which is allowed to change you open your
    application for all sorts of nasty effects caused by the fact that
    different pieces of code (not necessarily in separate threads) use
    that "global variable" in different ways.

    >> Your need to redefine remove_method etc. is fallout of your design
    >> decision to change the default ordering. =A0As I said, I believe there a=

    re
    >> better and more efficient designs.

    >
    > True. I said this to make the point that I have thought of things that I
    > need to do to try to keep things from breaking since Ruby is dynamic. I
    > think you are saying that by coding this way, I am making it tough on
    > myself since Ruby is dynamic. Hmmm, need to think about this, because I
    > know that there will be cases out there that I do not think to cover. I
    > guess this is a way to tell a good design from a bad one.


    :)

    >> There are still some things that I didn't yet wrap my head around: why
    >> do you want to make classes keep track of all their instances?

    >
    > When I was learing Ruby, I came across ObjectSpace.each_object and
    > thought that since Ruby makes this method available, why not use it
    > instead of setting up my own containers? So early versions of the code
    > used ObjectSpace. Then I discovered self.inherited and class instance
    > variables. =A0I decided to use self.inherited to pass along the values of
    > certain class instance variables that I want inherited (and slightly
    > modified for that subclass). Since keeping track of child classes was
    > fairly easy with self.inherited and I could also use it to initialize
    > @class_all_enum_objects, I dropped the use of ObjectSpace. It seems like
    > ObjectSpace would be less efficient too.
    >
    >> This essentially makes classes global variables - but less obvious.

    >
    > Never thought of that. See my comments below on my lack of having to
    > interface with other users' code.


    I am not sure whether this is only related to interfacing with foreign
    code. =A0Basically my main theme is modularity. =A0By tying tracking
    functionality into the classes your design is a tad less modular. =A0One
    consequence of this is that since each class is a singleton you can
    only ever track all instances of a class in one place. =A0Assuming your
    application grows and you need this tracking in different places but
    independently you are screwed. =A0If you separate the tracking as I have
    tried to demonstrate it's as easy as creating another InstanceTracker.

    >>If you want automated tracking of
    >> instances there are other options, e.g.
    >>
    >> class InstanceTracker
    >> =A0 ... lines of code
    >> > end

    >>
    >> it =3D InstanceTracker.new
    >>
    >> it.new(Foo, 1, 2)
    >> it.each_class(Foo).find_all {|f| f.size > 0}
    >>

    >
    > I see, but why have a seperate class for this? Aren't you doing the same
    > thing?


    From a functionality point of view, yes. =A0But I choose to distribute
    the functionality in a different (more modular) way across language
    artifacts (classes and methods). =A0Separating concerns is an important
    task of a software engineer: all the time when coding we decide where
    we place functionality. =A0For small scripts it's OK to lump everything
    together. =A0If applications grow you often have to go through a painful
    refactoring process to untangle different aspects. =A0If you start out
    modular you _may_ have increased effort initially but it pays off mid
    to long term. =A0Also, it _can_ help to make code more readable.

    > I think that the reason that my code is the way it is, is partially due
    > to the fact that I am writing my code in isolation; I do
    > not have to interface to other code to perform a broader function. In
    > fact, I have no experience writing code with any kind of interface
    > (explains a lot, huh?). Well, I guess I do use gems, so I am not in
    > total isolation, and at least I have given it some thought since I
    > turned away from mofidying Array and Enumerator directly.


    Well, that's perfectly OK. =A0Software is soft and so we change it over
    time. =A0Also, we as humans learn while we go along. =A0My coding
    certainly has changed over the years. =A0That's only natural. =A0And
    discussing things like these helps in thinking differently about code.
    =A0It's a creative process that increases knowledge on all sides.

    Kind regards

    robert


    --=20
    remember.guy do |as, often| as.you_can - without end
    http://blog.rubybestpractices.com/
     
    Robert Klemme, Oct 14, 2010
    #14
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Matthias Kaeppler
    Replies:
    3
    Views:
    299
    Jeff Flinn
    Dec 1, 2005
  2. Sullivan WxPyQtKinter
    Replies:
    10
    Views:
    670
    Antoon Pardon
    Nov 8, 2007
  3. Chris Withers

    setting variables in the local namespace

    Chris Withers, Oct 13, 2009, in forum: Python
    Replies:
    8
    Views:
    326
    Carl Banks
    Oct 13, 2009
  4. ara howard

    setting local variables via eval

    ara howard, Mar 11, 2008, in forum: Ruby
    Replies:
    15
    Views:
    321
    Robert Dober
    Mar 12, 2008
  5. Brian Candler
    Replies:
    2
    Views:
    787
    Brian Candler
    Oct 14, 2010
Loading...

Share This Page