Forum: Ruby on Rails evaluating expressions left to right

Posted by John Merlino (johnmerlino)
on 2012-12-22 17:24
(Received via mailing list)
class Proc
def apply(enum)
    enum.map &self
  end
  alias | apply

  def reduce(enum)
    enum.inject &self
  end
  alias <= reduce

def compose(f)
    if self.respond_to?(:arity) && self.arity == 1
      lambda {|*args| self[f[*args]] }
    else
      lambda {|*args| self[*f[*args]] }
    end
  end
  alias * compose

end

sum = lambda {|x,y| x+y }        # A function to add two numbers
mean = (sum<=a)/a.size           # Or sum.reduce(a) or a.inject(&sum)
deviation = lambda {|x| x-mean } # Function to compute difference from
mean
square = lambda {|x| x*x }       # Function to square a number
standardDeviation = Math.sqrt((sum<=square*deviation|a)/(a.size-1))

Ok so now I have another example from this book which does not contain
parentheses around deviation|a. So then why is this part:

(sum<=square*deviation|a)

not evaluated from left to right.

Apparently, it evaluates this part:

deviation|a

first

and this part second:

square*deviation|a

and then finally:

sum<=

So it looks like it is evaluating from right to left. Even though it
should be evaluating from left to right...
Posted by 7stud -- (7stud)
on 2012-12-23 09:14
...
Posted by Jordon Bedwell (Guest)
on 2012-12-23 09:49
(Received via mailing list)
meth3 -> (meth2) -> (meth1)

The logic is that meth3 has to return so that meth2 can accept,
process and return so that meth1 can accept, process and return.
Don't read nested methods like you read a book, with nested methods
the last to be nested is the first to be executed.
Posted by Colin Law (Guest)
on 2012-12-23 12:00
(Received via mailing list)
If you think about it there is only one possible answer to that
question.  How could it evaluate meth2(meth3) without evaluating meth3
first, in order to pass the result to meth2?  Similarly how could it
call meth1 before it evaluated the parameter to pass to it?

Colin
Posted by John Merlino (johnmerlino)
on 2012-12-26 02:14
(Received via mailing list)
ok, it didn't look like nested methods. But I made to believe that
this:

sum<=square*deviation|a

is exactly the same as this:

sum<=(square*(deviation|(a)))

So if this is true, then still a question remains.

Here's the original context again:


module Functional

  def compose(f)
    if self.respond_to?(:arity) && self.arity == 1
      lambda { |*args| self[f[*args]] }
    else
      lambda {|*args| self[*f[*args]] }
    end
  end
  alias * compose


  def apply(enum)
    enum.map &self
  end
  alias | apply

  def reduce(enum)
    enum.inject &self
  end
  alias <= reduce

end

class Proc
  include Functional
end

#client code
a = [1,2,3]
sum = lambda { |x,y| x+y }
mean = (sum<=a)/a.size
deviation = lambda { |x| x-mean }
square = lambda { |x| x*x }
standardDeviation = Math.sqrt((sum<=square*deviation|a)/(a.size-1))

On the last line, this executes first:

deviation|a

this returns a new array of how far each of elements are from the
mean.

Then this array gets passed to * which is invoked on square (a lambda
object):

square*returned_array

That calls compose where f parameter is the returned array from above.
So then this line is returned by compose since the array object doesnt
respond to arity:

lambda {|*args| self[*f[*args]] }

So it appears the return value of compose is the lambda object. That
presents a problem because <= expects an enum argument.

sum<=this_should_be_an_enum
Posted by Matt Jones (Guest)
on 2012-12-27 00:49
(Received via mailing list)
On Tuesday, 25 December 2012 20:13:16 UTC-5, John Merlino wrote:
> So if this is true, then still a question remains.
>

 That's not how it parses, thanks to operator precedence - the same 
reason
that 2+5*10+3 parses as 2.+((5.*(10)).+(3)) and not 2.+(5.*(10.+(3))).

 You can use a tool like Ripper
(http://www.rubyinside.com/using-ripper-to-see-how-...)
to see exactly how something is being parsed. Trying your expression 
yields:

[:program,
 [[:binary,
   [:vcall, [:@ident, "sum", [1, 0]]],
   :<=,
   [:binary,
    [:binary,
     [:vcall, [:@ident, "square", [1, 5]]],
     :*,
     [:vcall, [:@ident, "deviation", [1, 12]]]],
    :|,
    [:vcall, [:@ident, "a", [1, 22]]]]]]]

Or, distilled back to a fully-parenthized code version:

sum <= ((square*deviation) | a)

With the method calls written out explicitly:

sum.<=((square.*(deviation)).|(a))

Essentially, this creates a function that calculates the squared 
deviation
from the mean (square*deviation), applies it to the list a, and then 
sums
the resulting values.

This sort of confusion is why most people recommend avoiding operator
overloading in most cases - there are a bunch of precedence rules built
into the language, and you're essentially stuck with them.

--Matt Jones
Posted by John Merlino (johnmerlino)
on 2012-12-28 03:04
(Received via mailing list)
That was my hunch. Thanks for clarifying.
Please log in before posting. Registration is free and takes only a minute.
Existing account (Switch to SSL-encrypted connection)
NEW: Do you have a Google/GoogleMail or Yahoo account? No registration required!
Log in with Google account | Log in with Yahoo account
No account? Register here.