|
Hi,
I'm working through Programming in Scala and when I came to the Rational number example, I found myself wondering about the "private val g" defined to hold the result of the greatest common divisor calculation used to reduce the arguments to the normal form. This value is used twice, so is computed once and stored in a private val so both the numerator and the denominator can be divided by it. It is never needed after the reduced numerator and denominator are computed: -==--==--==--==--==--==--==--==--==--==--==--==--==--==--==--==- /* * Copyright (C) 2007-2008 Artima, Inc. All rights reserved. */ class Rational(n: Int, d: Int) { require(d != 0) private val g = gcd(n.abs, d.abs) val numer = n / g val denom = d / g // ... private def gcd(a: Int, b: Int): Int = if (b == 0) a else gcd(b, a % b) } -==--==--==--==--==--==--==--==--==--==--==--==--==--==--==--==- When I compile this and dump it with javap, I find that the private val g is part of the class: -==--==--==--==--==--==--==--==--==--==--==--==--==--==--==--==- % javap -private !-2:$ javap -private Rational Compiled from "Rational.scala" public class Rational extends java.lang.Object implements scala.ScalaObject{ private final int denom; private final int numer; private final int g; ... private int g(); ... } -==--==--==--==--==--==--==--==--==--==--==--==--==--==--==--==- Now if Rational is going to be used in a way that produces large populations, this wasted field is undesirable. So the question is: How does one avoid it? I puzzled over this for quite a while and couldn't figure out how, but I'm a Scala neophyte, at best, so this failure may just reflect the insufficiency of my knowledge. What's the answer? Is the only way out calling gcd(...) twice? Randall Schulz |
|
Try:
val (numer, denom) = { val g = gcd(n.abs, d.abs) (n / g, d / g) } --j On Sun, Mar 15, 2009 at 10:23 AM, Randall R Schulz <[hidden email]> wrote: Hi, |
|
Neverming, it seems that doesn't actually help.
Stick with factories and assert gcd == 1. --j On Sun, Mar 15, 2009 at 11:38 AM, Jorge Ortiz <[hidden email]> wrote: Try: |
|
In reply to this post by Randall R Schulz-2
The following should work:
val (numer,denom) = { val g = gcd(n.abs, d.abs) (n/g,d/g) } I did not validate it though. Kind regards, Jan 2009/3/15 Randall R Schulz <[hidden email]> Hi, |
|
In reply to this post by Jorge Ortiz
You mean that there is no way to use local (temporary) vars in primary
constructors?? This sounds bad. O/H Jorge Ortiz έγραψε: > Neverming, it seems that doesn't actually help. > > Stick with factories and assert gcd == 1. > > --j > > On Sun, Mar 15, 2009 at 11:38 AM, Jorge Ortiz <[hidden email] > <mailto:[hidden email]>> wrote: > > Try: > > val (numer, denom) = { > > val g = gcd(n.abs, d.abs) > (n / g, d / g) > } > > --j > > > On Sun, Mar 15, 2009 at 10:23 AM, Randall R Schulz > <[hidden email] <mailto:[hidden email]>> wrote: > > Hi, > > I'm working through Programming in Scala and when I came to > the Rational > number example, I found myself wondering about the "private val g" > defined to hold the result of the greatest common divisor > calculation > used to reduce the arguments to the normal form. This value is > used > twice, so is computed once and stored in a private val so both the > numerator and the denominator can be divided by it. It is > never needed > after the reduced numerator and denominator are computed: > > -==--==--==--==--==--==--==--==--==--==--==--==--==--==--==--==- > /* > * Copyright (C) 2007-2008 Artima, Inc. All rights reserved. > */ > class Rational(n: Int, d: Int) { > > require(d != 0) > > private val g = gcd(n.abs, d.abs) > val numer = n / g > val denom = d / g > > // ... > > private def gcd(a: Int, b: Int): Int = > if (b == 0) a else gcd(b, a % b) > } > -==--==--==--==--==--==--==--==--==--==--==--==--==--==--==--==- > > > When I compile this and dump it with javap, I find that the > private val > g is part of the class: > > -==--==--==--==--==--==--==--==--==--==--==--==--==--==--==--==- > % javap -private !-2:$ > javap -private Rational > Compiled from "Rational.scala" > public class Rational extends java.lang.Object implements > scala.ScalaObject{ > private final int denom; > private final int numer; > private final int g; > ... > private int g(); > ... > } > -==--==--==--==--==--==--==--==--==--==--==--==--==--==--==--==- > > > Now if Rational is going to be used in a way that produces large > populations, this wasted field is undesirable. So the question > is: How > does one avoid it? I puzzled over this for quite a while and > couldn't > figure out how, but I'm a Scala neophyte, at best, so this > failure may > just reflect the insufficiency of my knowledge. > > What's the answer? Is the only way out calling gcd(...) twice? > > > Randall Schulz > > > |
|
In reply to this post by Randall R Schulz-2
2009/3/15 Randall R Schulz <[hidden email]>:
> Now if Rational is going to be used in a way that produces large > populations, this wasted field is undesirable. So the question is: How > does one avoid it? I puzzled over this for quite a while and couldn't > figure out how, but I'm a Scala neophyte, at best, so this failure may > just reflect the insufficiency of my knowledge. > > What's the answer? Is the only way out calling gcd(...) twice? private[this] val stuff = ... private variables can in theory escape the scope of the current object as they're accessible to other instances of the same class. Therefore they get made fields (in principle you should be able to tell if this is neccessary and the compiler should be able to avoid it. But this doesn't happen). private[this] ones are guaranteed to never escape so will be made fields only if it's needed. |
|
I tried that. Reality does not agree with you:
$ cat Rational.scala class Rational(n: Int, d: Int) { require(d != 0) private[this] val g = gcd(n.abs, d.abs) val numer = n / g val denom = d / g private def gcd(a: Int, b: Int): Int = if (b == 0) a else gcd(b, a % b) } $ scalac Rational.scala $ javap -private Rational Compiled from "Rational.scala" public class Rational extends java.lang.Object implements scala.ScalaObject{ private final int denom; private final int numer; private final int g; public Rational(int, int); private int gcd(int, int); public int denom(); public int numer(); public int $tag() throws java.rmi.RemoteException; } --j On Sun, Mar 15, 2009 at 12:03 PM, David MacIver <[hidden email]> wrote: 2009/3/15 Randall R Schulz <[hidden email]>: |
|
In reply to this post by Jan Lohre
On Sunday March 15 2009, Jan Lohre wrote:
> The following should work: > > val (numer,denom) = { > val g = gcd(n.abs, d.abs) > (n/g,d/g) > } > > I did not validate it though. > > Kind regards, > Jan Keeping in mind that the point of the exercise is to minimize unnecessary storage within the representation, this is not an improvement, since now you're dragging a Tuple around! To wit: -==--==--==--==--==--==--==--==--==--==--==--==--==--==--==--==- class Rational(n: Int, d: Int) { require(d != 0) val (numerator, denomator) = { val g = gcd(n.abs, d.abs) (n/g,d/g) } private def gcd(a: Int, b: Int): Int = if (b == 0) a else gcd(b, a % b) } -==--==--==--==--==--==--==--==--==--==--==--==--==--==--==--==- % javap -private Rational Compiled from "Rational.scala" public class Rational extends java.lang.Object implements scala.ScalaObject{ private final int denomator; private final int numerator; private final scala.Tuple2 x$1; public Rational(int, int); private int gcd(int, int); public int denomator(); public int numerator(); public int $tag() throws java.rmi.RemoteException; } Definite step backwards! Randall Schulz |
|
In reply to this post by David MacIver
On Sunday March 15 2009, David MacIver wrote:
> 2009/3/15 Randall R Schulz <[hidden email]>: > > ... > > > > What's the answer? Is the only way out calling gcd(...) twice? > > private[this] val stuff = ... > > private variables can in theory escape the scope of the current > object as they're accessible to other instances of the same class. > Therefore they get made fields (in principle you should be able to > tell if this is neccessary and the compiler should be able to avoid > it. But this doesn't happen). private[this] ones are guaranteed to > never escape so will be made fields only if it's needed. That doesn't change the generated code, other than in the absence of the private method for accessing the val g: -==--==--==--==--==--==--==--==--==--==--==--==--==--==--==--==- class Rational(n: Int, d: Int) { require(d != 0) private[this] val g = gcd(n.abs, d.abs) val numer = n / g val denom = d / g private def gcd(a: Int, b: Int): Int = if (b == 0) a else gcd(b, a % b) } -==--==--==--==--==--==--==--==--==--==--==--==--==--==--==--==- -==--==--==--==--==--==--==--==--==--==--==--==--==--==--==--==- % javap -private Rational Compiled from "Rational.scala" public class Rational extends java.lang.Object implements scala.ScalaObject{ private final int denom; private final int numer; private final int g; public Rational(int, int); private int gcd(int, int); public int denom(); public int numer(); public int $tag() throws java.rmi.RemoteException; } -==--==--==--==--==--==--==--==--==--==--==--==--==--==--==--==- Randall Schulz |
|
In reply to this post by Jorge Ortiz
On Sunday March 15 2009, Jorge Ortiz wrote:
> Nevermind, it seems that doesn't actually help. > > Stick with factories and assert gcd == 1. > > --j Just to clarify, the idea is to do all the computation requiring intermediate values in a factory and simply require that the numerator and denominator are in reduced form in the primary constructor? Of course, in this case, that would imply two calls to gcd(...), which would leave you in the same situation as simply calling gcd twice in the primary constructor (in this particular example). Naturally that's an incidental matter that would manifest itself differently in different classes, but any counterpart of gcd that was computationally very expensive would still pose a problem, if it had to be verified in the primary constructor. Can primary constuctors be made private? Alternately could auxiliary constructors work for this, or do vals defined within auxiliary constructors also contribute state to the resulting class? Randall Schulz |
|
In reply to this post by Jorge Ortiz
On Sun, 2009-03-15 at 11:42 -0700, Jorge Ortiz wrote:
> Neverming, it seems that doesn't actually help. For the ones who haven't checked, the reason why it doesn't help is that the tuple returned from the block is stored as a synthetic field. It does help in cases where one needs to use some locals in the process of computing a single result. e.g.: val numer = { val g = gcd(n.abs, d.abs) n / g } In this case, g is not stored as a field. Ismael > Stick with factories and assert gcd == 1. > > --j > > On Sun, Mar 15, 2009 at 11:38 AM, Jorge Ortiz <[hidden email]> > wrote: > Try: > > val (numer, denom) = { > > val g = gcd(n.abs, d.abs) > > (n / g, d / g) > } > > --j |
|
On Sunday March 15 2009, Ismael Juma wrote:
> On Sun, 2009-03-15 at 11:42 -0700, Jorge Ortiz wrote: > > Neverming, it seems that doesn't actually help. > > For the ones who haven't checked, the reason why it doesn't help is > that the tuple returned from the block is stored as a synthetic > field. It does help in cases where one needs to use some locals in > the process of computing a single result. e.g.: > > val numer = { > val g = gcd(n.abs, d.abs) > n / g > } > > In this case, g is not stored as a field. True, but you're back to calling gcd twice, once to compute the reduced numerator and once for the denominator. > Ismael Randall Schulz |
|
In reply to this post by Randall R Schulz-2
Randall,
I'd do something like: package foo class Rational private[foo](val numer: Int, val denom: Int) { require(denom != 0) } object Rational { def apply(n: Int, d: Int): Rational = { val g = gcd(n.abs, d.abs) new Rational(n / g, d / g) } def gcd(a: Int, b: Int): Int = if (b == 0) a else gcd(b, a % b) } --j On Sun, Mar 15, 2009 at 12:15 PM, Randall R Schulz <[hidden email]> wrote: > > ... |
|
In reply to this post by Jorge Ortiz
2009/3/15 Jorge Ortiz <[hidden email]>:
> I tried that. Reality does not agree with you: Weird. Reality sure used to agree with me on that one. That probably constitutes a bug. |
|
In reply to this post by Randall R Schulz-2
Maybe another private function could be used to compute the numerator and denominator, or maybe even a nested function. I didn't try it though.
However, it does seem that you should be able to get what you want without having to call another function. Maybe you could use a nested block, but then I think you may need to make numer and denom vars rather than vals (so they can be declared before the block and changed in the block). --Russ |
|
In reply to this post by Randall R Schulz-2
There should be an optimization that turns private[this] variables
that are only used for class initialization into constructor-local variables. It would be relatively easy to do that, I think. But it has not yet been done. Cheers -- Martin On Sun, Mar 15, 2009 at 8:15 PM, Randall R Schulz <[hidden email]> wrote: > On Sunday March 15 2009, David MacIver wrote: >> 2009/3/15 Randall R Schulz <[hidden email]>: >> > ... >> > >> > What's the answer? Is the only way out calling gcd(...) twice? >> >> private[this] val stuff = ... >> >> private variables can in theory escape the scope of the current >> object as they're accessible to other instances of the same class. >> Therefore they get made fields (in principle you should be able to >> tell if this is neccessary and the compiler should be able to avoid >> it. But this doesn't happen). private[this] ones are guaranteed to >> never escape so will be made fields only if it's needed. > > > That doesn't change the generated code, other than in the absence of the > private method for accessing the val g: > > -==--==--==--==--==--==--==--==--==--==--==--==--==--==--==--==- > class Rational(n: Int, d: Int) { > > require(d != 0) > > private[this] val g = gcd(n.abs, d.abs) > val numer = n / g > val denom = d / g > > private def gcd(a: Int, b: Int): Int = > if (b == 0) a else gcd(b, a % b) > } > -==--==--==--==--==--==--==--==--==--==--==--==--==--==--==--==- > > > -==--==--==--==--==--==--==--==--==--==--==--==--==--==--==--==- > % javap -private Rational > Compiled from "Rational.scala" > public class Rational extends java.lang.Object implements > scala.ScalaObject{ > private final int denom; > private final int numer; > private final int g; > public Rational(int, int); > private int gcd(int, int); > public int denom(); > public int numer(); > public int $tag() throws java.rmi.RemoteException; > } > -==--==--==--==--==--==--==--==--==--==--==--==--==--==--==--==- > > > Randall Schulz > |
|
In reply to this post by Randall R Schulz-2
On Sun, Mar 15, 2009 at 12:13:01PM -0700, Randall R Schulz wrote:
> Keeping in mind that the point of the exercise is to minimize > unnecessary storage within the representation No problem. class Rational(n: Int, d: Int) { require(d != 0) val numerator = n/ gcd(n.abs, d.abs) val denominator = numerator * d / n private def gcd(a: Int, b: Int): Int = if (b == 0) a else gcd(b, a % b) } (Is there a top prize for missing the point?) Until now I hadn't realized the multiple assignment approach drags in a permanent tuple. I would agree this seems like a real problem, but as already pointed out factory methods are the way for now. -- Paul Phillips | Simplicity and elegance are unpopular because Stickler | they require hard work and discipline to achieve Empiricist | and education to be appreciated. i'll ship a pulp | -- Dijkstra |
|
On Sun, 2009-03-15 at 12:36 -0700, Paul Phillips wrote:
> (Is there a top prize for missing the point?) Until now I hadn't > realized the multiple assignment approach drags in a permanent tuple. I > would agree this seems like a real problem, but as already pointed out > factory methods are the way for now. Since everyone seemed to be surprised by this in the #scala IRC channel and there doesn't seem to be a good reason for it, shall a bug be filed? Ismael |
|
In reply to this post by Martin Odersky
2009/3/15 martin odersky <[hidden email]>:
> There should be an optimization that turns private[this] variables > that are only used for class initialization into constructor-local > variables. It would be relatively easy to do that, I think. But it has > not yet been done. I was really sure that I'd seen such behaviour in the past, but I can't seem to find any evidence of it. It's very strange. |
|
In reply to this post by Randall R Schulz-2
On Sunday March 15 2009, Randall R Schulz wrote:
> Hi, > > I'm working through Programming in Scala and when I came to the > Rational number example, I found myself wondering about the "private > val g" defined to hold the result of the greatest common divisor > calculation used to reduce the arguments to the normal form. ... > > ... I find that the private val g is part of the class ... > > What's the answer? Is the only way out calling gcd(...) twice? Thanks for the analysis and ideas, folks. I learned some new stuff and I think I know how to handle this general issue now. Randall Schulz |
| Powered by Nabble | Edit this page |
